/* ------------------------------------------------------------------------- */
/* ap-tftp.c */
/* */
+/* Version: 1.1 */
+/* */
+/* Copyright (C) 2004-2005 Jan Rafaj <jr-aputils at cedric dot unob dot cz> */
+/* */
/* A simple tftp client for upgrading ATMEL AT76C510 WiSOC-based APs. */
/* Supports ATMEL+INTERSIL boards (1.4x.y firmware) and ATMEL+RFMD boards */
/* (0.x.y.z firmware). */
-/* Modelled around TELLUS (GEMTEK/ATMEL OEM) TFTP client functionality. */
+/* Modelled around TELLUS (GEMTEK/ATMEL OEM) and SMARTBRIDGES TFTP clients */
+/* functionality. */
+/* Tested with TELLUS, D-Link and SmartBridges hardware. */
/* This program is part of AP-UTILS project (http://ap-utils.polesye.net) */
/* */
-/* Copyright (C) 2004-2005 Jan Rafaj <jr-aputils at cedric dot unob dot cz> */
-/* */
/* Loosely based on a simple tftp client for busybox. */
/* Tries to follow RFC1350. */
/* Only "octet" mode and "put" method supported. */
/* */
-/* Version: 1.0 */
-/* */
/* Not implemented: */
/* - uploading of OEM (default) settings */
/* - uploading of PATCH code */
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
+#include "ap-utils.h"
#ifdef HAVE_GETTEXT
/* GNU gettext stuff*/
#define _(String) (String)
#endif
-#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
+#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, do not change */
#define TFTP_TIMEOUT 2 /* # seconds waiting for single response */
#define TFTP_RETRIES 5 /* # retries with TFTP_TIMEOUT */
#undef FEATURE_TFTP_DEBUG
#endif
#define ERR_READFW _("Error while read()ing firmware file")
+
+/* This is necessary becouse of linkage to libap.a */
+short ap_type = ATMEL410;
+char *community = NULL;
+struct in_addr ap_ip;
+
+int tftp(int ip, const char *remotefile, int localfd, const char *wopt,
+ int woptlen, int maxdlen)
+{
/* known errors server may respond with */
-static const char *tftp_error_msg[] = {
+char *tftp_error_msg[8] = {
_("Undefined error"),
_("File not found"),
_("Access violation"),
_("File already exists"),
_("No such user")
};
-
-int tftp(int ip, const char *remotefile, int localfd, const char *options)
-{
const int port = 69;
struct sockaddr_in to;
struct sockaddr_in from;
struct timeval tv;
- struct stat sb;
fd_set rfds;
unsigned short tmp;
int fromlen;
int finished = 0;
int socketfd;
int len;
- int maxlen;
- int optlen;
+ int bc = lseek(localfd, 0, SEEK_CUR);
char *nl = "";
char *cp;
char *buf = malloc(tftp_bufsize);
printf("Trying to upload firmware to the AP...\n");
- if (!strcmp(remotefile, "atbrfirm.bin")) {
- optlen = 8;
- maxlen = 94448;
- } else {
- /* remotefile = atsingle.bin */
- optlen = 234;
- maxlen = 0;
- }
-
- fstat(localfd, &sb);
-
if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket()");
return EXIT_FAILURE;
if (opcode == TFTP_WRQ) {
/* see if the filename fits into buf */
len = strlen(remotefile) + 1;
- if ((cp + len + 1 + 6 + optlen) >= &buf[tftp_bufsize - 1]) {
+ if ((cp + len + 1 + 6 + woptlen) >= &buf[tftp_bufsize - 1]) {
printf(_("Remote-filename too long.\n"));
break;
}
cp += 6;
// ATMEL tftp client specific ("upload auth. code")
- memcpy(cp, options, optlen);
- cp += optlen;
+ memcpy(cp, wopt, woptlen);
+ cp += woptlen;
}
if (opcode == TFTP_DATA) {
block_nr++;
/*
- * assure we wont upload more than maxlen bytes
- * even if total file length > maxlen
+ * assure we wont upload more than maxdlen bytes
+ * even if total file length > maxdlen
*/
- if (maxlen && (block_nr - 2) * tftp_bufsize + len > maxlen) {
- len = maxlen - (block_nr - 2) * tftp_bufsize;
+ if (bc + len > maxdlen) {
+ len = maxdlen - bc;
cp[len] = '\0';
}
+ bc += len;
if (len != tftp_bufsize)
finished++;
if (to.sin_port == from.sin_port) {
#ifndef FEATURE_TFTP_DEBUG
if (opcode == TFTP_DATA) {
- float f = (block_nr - 2) * tftp_bufsize + len;
-
- sprintf(buf2, _("\rProgress: uploaded %.0f %%."),
- f / sb.st_size * 100);
+ sprintf(buf2, _("\rProgress: uploaded %3i%%."),
+ bc * 100 / maxdlen);
write(fileno(stdout), buf2, strlen(buf2));
nl = "\n";
}
void usage(char **argv)
{
printf (_("PLEASE BE _ABSOLUTELY_ SURE TO READ MANPAGE PRIOR USE!!!\n"));
- printf (_("\nUsage: %s <-l firmware_file.rom> <IP>\n"), argv[0]);
+ printf (_(
+ "\nUsage: %s -i IP -f firmware_file.rom [-c community] [-h]\n"),
+ argv[0]);
}
int main(int argc, char **argv)
{
- struct in_addr in;
struct stat sb;
char *localfile = NULL;
char *remotefile = NULL;
int fd;
int result = 0;
int i;
- static char buf[256];
+ static char wopt[256];
+ int woptlen;
+ int maxdlen;
#ifdef HAVE_GETTEXT
/* locale support init */
printf (_("(C) 2004-2005 Jan Rafaj "
"<jr-aputils at cedric dot unob dot cz>\n"));
- while ((i = getopt(argc, argv, "l:h")) != -1) {
+ if (argc == 1) {
+ usage(argv);
+ return EXIT_SUCCESS;
+ }
+
+ ap_ip.s_addr = 0;
+
+ while ((i = getopt(argc, argv, "i:f:c:h")) != -1) {
switch (i) {
- case 'l':
+ case 'i':
+ /* host = gethostbyname(optarg); */
+ for (cp = optarg, i = 0; *cp && (cp = index(cp, '.')); cp++, i++);
+ if (i < 3 || !(inet_aton(optarg, &ap_ip))) {
+ printf(_("Error: invalid IP address format given.\n"));
+ result = EXIT_FAILURE;
+ goto quit;
+ }
+ break;
+ case 'f':
localfile = strdup(optarg);
break;
+ case 'c':
+ community = strdup(optarg);
+ break;
case 'h':
usage(argv);
- return EXIT_SUCCESS;
+ result = EXIT_SUCCESS;
+ goto quit;
default:
- return EXIT_FAILURE;
+ result = EXIT_FAILURE;
+ goto quit;
}
}
- if (argc == 1) {
- usage(argv);
- return EXIT_SUCCESS;
- }
-
/*
- * either no mandatory opts, or too few mandatory opts, or more than 1
- * non-opt arg. specified, or no non-option arg. specified
+ * either any of mandatory options (IP, firmware path) not specified,
+ * or some extra non-option arguments specified
*/
- if (optind == 1 || optind < 3 || argc > 4 || optind == argc) {
- printf(_("Error: invalid arguments given.\n"));
- return EXIT_FAILURE;
+ if (ap_ip.s_addr == 0 || !localfile || optind != argc) {
+ printf(_("Error: invalid argument combination.\n"));
+ result = EXIT_FAILURE;
+ goto quit;
}
- /* host = gethostbyname(argv[optind]); */
- for (cp = argv[optind], i = 0; *cp && (cp = index(cp, '.')); cp++, i++);
- if (i < 3 || !(inet_aton(argv[optind], &in))) {
- printf(_("Error: invalid IP address format given.\n"));
- return EXIT_FAILURE;
- }
- inet_aton(argv[optind], &in);
-
fd = open(localfile, O_RDONLY, 0644);
if (fd < 0) {
perror(_("Error while open()ing firmware file"));
- return EXIT_FAILURE;
+ result = EXIT_FAILURE;
+ goto quit;
}
fstat(fd, &sb);
if (sb.st_size < 94448 || sb.st_size > 524288) {
printf(_("Error: invalid firmware file given.\n"));
- return EXIT_FAILURE;
+ result = EXIT_FAILURE;
+ goto quit;
}
- while ((i = read(fd, buf, sizeof(buf))) != 0) {
+ while ((i = read(fd, wopt, sizeof(wopt))) != 0) {
if (i < 0) {
perror(ERR_READFW);
- return EXIT_FAILURE;
+ result = EXIT_FAILURE;
+ goto quit;
}
- if (i == sizeof(buf))
+ if (i == sizeof(wopt))
lseek(fd, -13, SEEK_CUR);
while (i--) {
- if (!(memcmp(&buf[i], "ATMEL", 5)))
+ if (!(memcmp(&wopt[i], "ATMEL", 5)))
result |= 1;
- if (!(memcmp(&buf[i], "802.11 AP", 9)))
+ if (!(memcmp(&wopt[i], "802.11 AP", 9)))
result |= 2;
- if (!(memcmp(&buf[i], "atbrfirm.bin", 12))) {
+ if (!(memcmp(&wopt[i], "atbrfirm.bin", 12))) {
result |= 16;
remotefile = "atbrfirm.bin";
}
- if (!(memcmp(&buf[i], "atsingle.bin", 12))) {
+ if (!(memcmp(&wopt[i], "atsingle.bin", 12))) {
result |= 32;
remotefile = "atsingle.bin";
}
if (result > 18)
break;
- memset(buf, 0, sizeof(buf));
+ memset(wopt, 0, sizeof(wopt));
}
#if FEATURE_TFTP_DEBUG
printf(_("Firmware file contains:\n"
(result & (16|32)) == 0)
{
printf(_("Error: invalid firmware file given.\n"));
- return EXIT_FAILURE;
+ result = EXIT_FAILURE;
+ goto quit;
}
lseek(fd, 0, SEEK_SET);
"- server: %s\n"
"- firmware file: \"%s\"\n"
"- name used for upload: \"%s\"\n"),
- inet_ntoa(in), cp, remotefile);
+ inet_ntoa(ap_ip), cp, remotefile);
if (result & 16) {
/* Firmware series 1.4x.y - atbrfirm.bin */
+ /*
+ * For most devices using 1.4x.y firmware:
+ * - at most 94448 bytes needs to be written (file content above
+ * this offset is simply cut off)
+ * - checksum is only performed on the uploaded (first 94448)
+ * bytes of the firmware file
+ * - auth. code has 8 bytes in length (see below for its structure)
+ * SmartBridges, however, differs in this:
+ * - the TFTP firmware upgrade utility first checks whether the OUI
+ * portion of its MAC address is 00301A (SMARTBRIDGES PTE. LTD.),
+ * followed by 09 (apparently AT76C510-based SmartBridges devices),
+ * and will continue only upon match. This is to prevent performing
+ * this SmartBridges-style upgrade to non-SmartBridges devices, that
+ * could otherwise end up with damaged flash content. Note that
+ * this is still somewhat clumsy, since there's a remote possibility
+ * that someone redefined his/her ATMEL AP MAC address with
+ * SmartBridges OUI and added 09, so this check would not prevent
+ * damage in this case (ouch!). There's however, a better check
+ * we could come up up with: lets borrow a part of the code from
+ * get_mib_details() to find out whether the target device appears
+ * to be running firmware with ATMEL410 SBRIDGES MIB, and proceed
+ * with 'SmartBridges' upgrade method only, if it does.
+ * - entire file is being written; there's no 'first 94448 bytes only'
+ * limitation
+ * - 8-byte auth. code (see below) is followed by:
+ * - (char): length of the following "password" string
+ * - (string): "password" string - the firmware will perform
+ * actual upgrade only if this "password" matches with
+ * any of the three communities defined in the AP
+ * (sysadmin's note: finally something reasonable)
+ */
+
unsigned short sum = 0;
unsigned short *wp;
- int maxlen = 94448;
- int bufs_read = 0;
+ int readen = 0;
+
+ if (community) {
+ /*
+ * OK, looks like we want to upgrade SmartBridges hardware => no
+ * firmware file cutoff, and lets check whether the target device
+ * appears to have SmartBridges firmware. If not, bail out.
+ */
+/*
+ char operEthernetAddress[12] = {
+ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A,
+ 0x01, 0x01, 0x02, 0x03, 0x00
+ };
+*/
+ char AuthRadiusIP[] = {
+ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A,
+ 0x01, 0x02, 0x06, 0x03, 0x00
+ };
+ varbind varbinds[1];
+
+ maxdlen = sb.st_size;
+
+/*
+ varbinds[0].oid = operEthernetAddress;
+ varbinds[0].len_oid = sizeof(operEthernetAddress);
+*/
+ varbinds[0].oid = AuthRadiusIP;
+ varbinds[0].len_oid = sizeof(AuthRadiusIP);
+ varbinds[0].value = NULL;
+ varbinds[0].len_val = 0;
+ varbinds[0].type = NULL_VALUE;
+ if (snmp(varbinds, 1, GET) > 0) {
+/*
+ i = varbinds[0].value[0] << 24 |
+ varbinds[0].value[1] << 16 |
+ varbinds[0].value[2] << 8 |
+ varbinds[0].value[3];
+ if (i != 0x00301A09) {
+*/
+ if (varbinds[0].len_val != 4) {
+ printf(_("Error: device with the given IP does not seem "
+ "to run SmartBridges firmware.\n"));
+ result = EXIT_FAILURE;
+ goto quit;
+ }
+ } else {
+ printf(_("Error: SNMP authorization error or device unavailable"
+ ".\n"));
+ result = EXIT_FAILURE;
+ goto quit;
+ }
+ } else {
+ /* non-SB hardware */
+ maxdlen = 94448;
+ }
/* compute checksum */
- while ((i = read(fd, buf, sizeof(buf))) != 0) {
+ while ((i = read(fd, wopt, sizeof(wopt))) != 0) {
if (i < 0) {
perror(ERR_READFW);
- return EXIT_FAILURE;
+ result = EXIT_FAILURE;
+ goto quit;
}
- /* assure we do not checksum more than maxlen bytes */
- if ((bufs_read + i) > maxlen)
- i = maxlen - bufs_read;
+ /* assure we do not checksum more than maxdlen bytes */
+ if ((readen + i) > maxdlen)
+ i = maxdlen - readen;
- bufs_read += i;
+ readen += i;
/* perform iterative checksumming */
i /= 2;
- wp = (unsigned short *)buf;
+ wp = (unsigned short *)wopt;
while (i--) {
if (sum + *wp > 0xFFFF)
sum++;
sum += *(wp++);
};
- /* prevent eventual fread() if file length > maxlen */
- if (bufs_read == maxlen)
+ /* prevent eventual fread() if file length > maxdlen */
+ if (readen == maxdlen)
break;
}
sum ^= 0xFFFF;
lseek (fd, 0, SEEK_SET);
- buf[0] = 0xf0;
- buf[1] = 0x70;
- buf[2] = sum;
- buf[3] = sum >> 8;
- buf[4] = 0xf0;
- buf[5] = 0x70;
- buf[6] = 0x01;
- buf[7] = 0x00;
+ /*
+ * wopt[0] - wopt[1] are fw length in unsigned short form
+ * wopt[2] - wopt[3] are fw checksum in unsigned short form
+ * wopt[4] - wopt[7] are fw length in unsigned int form
+ */
+ wopt[0] = maxdlen;
+ wopt[1] = maxdlen >> 8;
+ wopt[2] = sum;
+ wopt[3] = sum >> 8;
+ wopt[4] = maxdlen;
+ wopt[5] = maxdlen >> 8;
+ wopt[6] = maxdlen >> 16;
+ wopt[7] = maxdlen >> 24;
printf("- checksum: %x\n", ntohs(sum));
+
+ woptlen = 8;
+
+ if (community) {
+ wopt[8] = strlen(community);
+ memcpy((void *) &wopt[9], (void *)community, strlen(community));
+ woptlen += (1 + strlen(community));
+ }
} else { /* result & 32 */
/* Firmware series 0.x.y.z - atsingle.bin */
- i = read(fd, buf, 234);
+ /* For 0.x.y.z firmware:
+ * - first 234 bytes from the file are used as auth. code
+ * - the actual firmware is being uploaded since offset 256 in the file
+ */
+
+ i = read(fd, wopt, 234);
if (i < 234) {
printf(ERR_READFW);
- return EXIT_FAILURE;
+ result = EXIT_FAILURE;
+ goto quit;
}
if (lseek(fd, 256, SEEK_SET) == -1) {
perror(_("Error while lseek()ing in firmware file\n"));
- return EXIT_FAILURE;
+ result = EXIT_FAILURE;
+ goto quit;
}
+ woptlen = 234;
+ maxdlen = sb.st_size; /* no limit */
}
- result = tftp(in.s_addr, remotefile, fd, buf);
+ result = tftp(ap_ip.s_addr, remotefile, fd, wopt, woptlen, maxdlen);
+
+quit:
+ if (localfile)
+ free(localfile);
+ if (community)
+ free(community);
return(result);
}