1 /* ------------------------------------------------------------------------- */
6 /* Copyright (C) 2004-2005 Jan Rafaj <jr-aputils at cedric dot unob dot cz> */
8 /* A simple tftp client for upgrading ATMEL AT76C510 WiSOC-based APs. */
9 /* Supports ATMEL+INTERSIL boards (1.4x.y firmware) and ATMEL+RFMD boards */
10 /* (0.x.y.z firmware). */
11 /* Modelled around TELLUS (GEMTEK/ATMEL OEM) and SMARTBRIDGES TFTP clients */
13 /* Tested with TELLUS, D-Link and SmartBridges hardware. */
14 /* This program is part of AP-UTILS project (http://ap-utils.polesye.net) */
16 /* Loosely based on a simple tftp client for busybox. */
17 /* Tries to follow RFC1350. */
18 /* Only "octet" mode and "put" method supported. */
20 /* Not implemented: */
21 /* - uploading of OEM (default) settings */
22 /* - uploading of PATCH code */
26 /* bb tftp: Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
28 /* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> */
29 /* and Remi Lefebvre <remi@debian.org> */
31 /* utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> */
33 /* This program is free software; you can redistribute it and/or modify */
34 /* it under the terms of the GNU General Public License as published by */
35 /* the Free Software Foundation; either version 2 of the License, or */
36 /* (at your option) any later version. */
38 /* This program is distributed in the hope that it will be useful, */
39 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
40 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
41 /* General Public License for more details. */
43 /* You should have received a copy of the GNU General Public License */
44 /* along with this program; if not, write to the Free Software */
45 /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
47 /* ------------------------------------------------------------------------- */
52 #include <sys/types.h>
53 #include <sys/socket.h>
57 #include <netinet/in.h>
58 #include <arpa/inet.h>
64 /* GNU gettext stuff*/
66 #include <libgnuintl.h>
67 #define _(String) gettext (String)
69 #define _(String) (String)
72 #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, do not change */
73 #define TFTP_TIMEOUT 2 /* # seconds waiting for single response */
74 #define TFTP_RETRIES 5 /* # retries with TFTP_TIMEOUT */
75 #undef FEATURE_TFTP_DEBUG
77 /* opcodes we support */
83 #ifdef FEATURE_TFTP_DEBUG
87 #define ERR_READFW _("Error while read()ing firmware file")
90 /* This is necessary becouse of linkage to libap.a */
91 short ap_type = ATMEL410;
92 char *community = NULL;
95 int tftp(int ip, const char *remotefile, int localfd, const char *wopt,
96 int woptlen, int maxdlen)
98 /* known errors server may respond with */
99 char *tftp_error_msg[8] = {
100 _("Undefined error"),
102 _("Access violation"),
103 _("Disk full or allocation error"),
104 _("Illegal TFTP operation"),
105 _("Unknown transfer ID"),
106 _("File already exists"),
110 struct sockaddr_in to;
111 struct sockaddr_in from;
116 int opcode = TFTP_WRQ;
117 int tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT;
123 int bc = lseek(localfd, 0, SEEK_CUR);
126 char *buf = malloc(tftp_bufsize);
129 printf("Trying to upload firmware to the AP...\n");
131 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
138 bind(socketfd, (struct sockaddr *)&to, len);
140 to.sin_family = AF_INET;
141 to.sin_port = htons(port);
142 to.sin_addr.s_addr = ip;
145 timeout = TFTP_RETRIES;
148 /* first create the opcode part */
150 *((unsigned short *) cp) = htons(opcode);
153 if (opcode == TFTP_WRQ) {
154 /* see if the filename fits into buf */
155 len = strlen(remotefile) + 1;
156 if ((cp + len + 1 + 6 + woptlen) >= &buf[tftp_bufsize - 1]) {
157 printf(_("Remote-filename too long.\n"));
161 strncpy(cp, remotefile, len);
164 // ATMEL tftp client specific
168 memcpy(cp, "octet", 6);
171 // ATMEL tftp client specific ("upload auth. code")
172 memcpy(cp, wopt, woptlen);
176 if (opcode == TFTP_DATA) {
177 *((unsigned short *) cp) = htons(block_nr);
180 len = read(localfd, cp, tftp_bufsize);
183 sprintf(buf2, _("%sError in read()"), nl);
191 * assure we wont upload more than maxdlen bytes
192 * even if total file length > maxdlen
194 if (bc + len > maxdlen) {
200 if (len != tftp_bufsize)
211 #ifdef FEATURE_TFTP_DEBUG
212 printf(_("sending %u bytes\n"), len);
213 for (cp = buf; cp < &buf[len]; cp++)
214 printf("%02x ", *cp & 0xFF);
218 if (sendto(socketfd, buf, len, 0,
219 (struct sockaddr *) &to, sizeof(to)) < 0)
221 sprintf(buf2, _("%sError in sendto()"), nl);
229 memset(&from, 0, sizeof(from));
230 fromlen = sizeof(from);
232 tv.tv_sec = TFTP_TIMEOUT;
236 FD_SET(socketfd, &rfds);
238 switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
240 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
241 (struct sockaddr *) &from, &fromlen);
244 sprintf(buf2, _("%sError in recvfrom()"), nl);
250 if (to.sin_port == htons(port))
251 to.sin_port = from.sin_port;
253 if (to.sin_port == from.sin_port) {
254 #ifndef FEATURE_TFTP_DEBUG
255 if (opcode == TFTP_DATA) {
256 sprintf(buf2, _("\rProgress: uploaded %3i%%."),
258 write(fileno(stdout), buf2, strlen(buf2));
266 /* assume invalid packet */
267 printf(_("%sMalformed packet received. Aborting.\n"), nl);
276 printf(_("%sTimed out waiting for response from server "
277 "(%i/%i).\n"),nl, TFTP_RETRIES - timeout, TFTP_RETRIES);
283 sprintf(buf2, _("%sError in select()"), nl);
286 } while (timeout && (len >= 0));
291 /* process received packet */
293 opcode = ntohs(*((unsigned short *) buf));
294 tmp = ntohs(*((unsigned short *) &buf[2]));
296 #ifdef FEATURE_TFTP_DEBUG
297 printf(_("Received %d bytes: %04x %04x\n"), len, opcode, tmp);
300 if (opcode == TFTP_ERROR) {
301 sprintf(buf, "code %i", tmp);
302 if (tmp < (sizeof(tftp_error_msg) / sizeof(char *))) {
304 strcat(buf, tftp_error_msg[tmp]);
308 printf(_("%sError: server responded with %s. Aborting.\n"),nl,buf);
312 if (opcode == TFTP_ACK) {
313 if (tmp == (block_nr - 1)) {
315 printf(_("%sFlash programming in progress...\n"), nl);
317 printf(_("Finished successfully.\n"));
330 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
333 void usage(char **argv)
335 printf (_("PLEASE BE _ABSOLUTELY_ SURE TO READ MANPAGE PRIOR USE!!!\n"));
337 "\nUsage: %s -i IP -f firmware_file.rom [-c community] [-h]\n"),
341 int main(int argc, char **argv)
344 char *localfile = NULL;
345 char *remotefile = NULL;
350 static char wopt[256];
355 /* locale support init */
356 setlocale(LC_ALL, "");
357 bindtextdomain("ap-utils", LOCALEDIR);
358 textdomain("ap-utils");
361 printf (_("TFTP client for upgrading firmware in ATMEL AT76C510 "
362 "WiSOC-based APs.\n"));
363 printf (_("(C) 2004-2005 Jan Rafaj "
364 "<jr-aputils at cedric dot unob dot cz>\n"));
373 while ((i = getopt(argc, argv, "i:f:c:h")) != -1) {
376 /* host = gethostbyname(optarg); */
377 for (cp = optarg, i = 0; *cp && (cp = index(cp, '.')); cp++, i++);
378 if (i < 3 || !(inet_aton(optarg, &ap_ip))) {
379 printf(_("Error: invalid IP address format given.\n"));
380 result = EXIT_FAILURE;
385 localfile = strdup(optarg);
388 community = strdup(optarg);
392 result = EXIT_SUCCESS;
395 result = EXIT_FAILURE;
401 * either any of mandatory options (IP, firmware path) not specified,
402 * or some extra non-option arguments specified
404 if (ap_ip.s_addr == 0 || !localfile || optind != argc) {
405 printf(_("Error: invalid argument combination.\n"));
406 result = EXIT_FAILURE;
410 fd = open(localfile, O_RDONLY, 0644);
412 perror(_("Error while open()ing firmware file"));
413 result = EXIT_FAILURE;
418 if (sb.st_size < 94448 || sb.st_size > 524288) {
419 printf(_("Error: invalid firmware file given.\n"));
420 result = EXIT_FAILURE;
424 while ((i = read(fd, wopt, sizeof(wopt))) != 0) {
427 result = EXIT_FAILURE;
431 if (i == sizeof(wopt))
432 lseek(fd, -13, SEEK_CUR);
435 if (!(memcmp(&wopt[i], "ATMEL", 5)))
438 if (!(memcmp(&wopt[i], "802.11 AP", 9)))
441 if (!(memcmp(&wopt[i], "atbrfirm.bin", 12))) {
443 remotefile = "atbrfirm.bin";
446 if (!(memcmp(&wopt[i], "atsingle.bin", 12))) {
448 remotefile = "atsingle.bin";
458 memset(wopt, 0, sizeof(wopt));
460 #if FEATURE_TFTP_DEBUG
461 printf(_("Firmware file contains:\n"
462 "- string \"ATMEL\": %s\n"
463 "- string \"802.11 AP\": %s\n"
464 "- string \"atbrfirm.bin\": %s\n"
465 "- string \"atsingle.bin\": %s\n"),
466 result & 1 ? YES : NO, result & 2 ? YES : NO,
467 result & 16 ? YES : NO, result & 32 ? YES : NO);
469 if ((result & (1|2)) != (1|2) || (result & (16|32)) == (16|32) ||
470 (result & (16|32)) == 0)
472 printf(_("Error: invalid firmware file given.\n"));
473 result = EXIT_FAILURE;
477 lseek(fd, 0, SEEK_SET);
479 cp = strrchr(localfile, '/');
487 "- firmware file: \"%s\"\n"
488 "- name used for upload: \"%s\"\n"),
489 inet_ntoa(ap_ip), cp, remotefile);
492 /* Firmware series 1.4x.y - atbrfirm.bin */
495 * For most devices using 1.4x.y firmware:
496 * - at most 94448 bytes needs to be written (file content above
497 * this offset is simply cut off)
498 * - checksum is only performed on the uploaded (first 94448)
499 * bytes of the firmware file
500 * - auth. code has 8 bytes in length (see below for its structure)
501 * SmartBridges, however, differs in this:
502 * - the TFTP firmware upgrade utility first checks whether the OUI
503 * portion of its MAC address is 00301A (SMARTBRIDGES PTE. LTD.),
504 * followed by 09 (apparently AT76C510-based SmartBridges devices),
505 * and will continue only upon match. This is to prevent performing
506 * this SmartBridges-style upgrade to non-SmartBridges devices, that
507 * could otherwise end up with damaged flash content. Note that
508 * this is still somewhat clumsy, since there's a remote possibility
509 * that someone redefined his/her ATMEL AP MAC address with
510 * SmartBridges OUI and added 09, so this check would not prevent
511 * damage in this case (ouch!). There's however, a better check
512 * we could come up up with: lets borrow a part of the code from
513 * get_mib_details() to find out whether the target device appears
514 * to be running firmware with ATMEL410 SBRIDGES MIB, and proceed
515 * with 'SmartBridges' upgrade method only, if it does.
516 * - entire file is being written; there's no 'first 94448 bytes only'
518 * - 8-byte auth. code (see below) is followed by:
519 * - (char): length of the following "password" string
520 * - (string): "password" string - the firmware will perform
521 * actual upgrade only if this "password" matches with
522 * any of the three communities defined in the AP
523 * (sysadmin's note: finally something reasonable)
526 unsigned short sum = 0;
532 * OK, looks like we want to upgrade SmartBridges hardware => no
533 * firmware file cutoff, and lets check whether the target device
534 * appears to have SmartBridges firmware. If not, bail out.
537 char operEthernetAddress[12] = {
538 0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A,
539 0x01, 0x01, 0x02, 0x03, 0x00
542 char AuthRadiusIP[] = {
543 0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A,
544 0x01, 0x02, 0x06, 0x03, 0x00
548 maxdlen = sb.st_size;
551 varbinds[0].oid = operEthernetAddress;
552 varbinds[0].len_oid = sizeof(operEthernetAddress);
554 varbinds[0].oid = AuthRadiusIP;
555 varbinds[0].len_oid = sizeof(AuthRadiusIP);
556 varbinds[0].value = NULL;
557 varbinds[0].len_val = 0;
558 varbinds[0].type = NULL_VALUE;
559 if (snmp(varbinds, 1, GET) > 0) {
561 i = varbinds[0].value[0] << 24 |
562 varbinds[0].value[1] << 16 |
563 varbinds[0].value[2] << 8 |
564 varbinds[0].value[3];
565 if (i != 0x00301A09) {
567 if (varbinds[0].len_val != 4) {
568 printf(_("Error: device with the given IP does not seem "
569 "to run SmartBridges firmware.\n"));
570 result = EXIT_FAILURE;
574 printf(_("Error: SNMP authorization error or device unavailable"
576 result = EXIT_FAILURE;
580 /* non-SB hardware */
584 /* compute checksum */
585 while ((i = read(fd, wopt, sizeof(wopt))) != 0) {
588 result = EXIT_FAILURE;
592 /* assure we do not checksum more than maxdlen bytes */
593 if ((readen + i) > maxdlen)
594 i = maxdlen - readen;
598 /* perform iterative checksumming */
600 wp = (unsigned short *)wopt;
602 if (sum + *wp > 0xFFFF)
608 /* prevent eventual fread() if file length > maxdlen */
609 if (readen == maxdlen)
613 lseek (fd, 0, SEEK_SET);
616 * wopt[0] - wopt[1] are fw length in unsigned short form
617 * wopt[2] - wopt[3] are fw checksum in unsigned short form
618 * wopt[4] - wopt[7] are fw length in unsigned int form
621 wopt[1] = maxdlen >> 8;
625 wopt[5] = maxdlen >> 8;
626 wopt[6] = maxdlen >> 16;
627 wopt[7] = maxdlen >> 24;
629 printf("- checksum: %x\n", ntohs(sum));
634 wopt[8] = strlen(community);
635 memcpy((void *) &wopt[9], (void *)community, strlen(community));
636 woptlen += (1 + strlen(community));
638 } else { /* result & 32 */
639 /* Firmware series 0.x.y.z - atsingle.bin */
641 /* For 0.x.y.z firmware:
642 * - first 234 bytes from the file are used as auth. code
643 * - the actual firmware is being uploaded since offset 256 in the file
646 i = read(fd, wopt, 234);
649 result = EXIT_FAILURE;
652 if (lseek(fd, 256, SEEK_SET) == -1) {
653 perror(_("Error while lseek()ing in firmware file\n"));
654 result = EXIT_FAILURE;
658 maxdlen = sb.st_size; /* no limit */
661 result = tftp(ap_ip.s_addr, remotefile, fd, wopt, woptlen, maxdlen);