/* * Copyright (C) 2001 by Javier Achirica * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* Standard headers */ #include #include #include #include #include #include #include #include #include #include #include #ifndef __FreeBSD__ #include /* For ARPHRD_ETHER */ #include /* For AF_INET & struct sockaddr */ #include #define WRQ_DATA(x) (x.u.data.pointer) #else #include #include #include #include #define WRQ_DATA(x) (x.ifr_data) #endif typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; #ifndef __FreeBSD__ #define AIROIOCTL SIOCIWFIRSTPRIV #define AIROIDIFC AIROIOCTL + 1 #else #define AIROIOCTL SIOCGPRIVATE_0 #define AIROIDIFC SIOCGPRIVATE_1 #endif #define AIROMAGIC 0xa55a /* Flash codes */ #define AIROGCAP 1 #define AIROGCFG 1 #define AIROGSLIST 2 #define AIROGVLIST 3 #define AIROPVLIST 51 #define AIROPSLIST 52 #define AIROPCFG 53 #define AIROPMACON 56 #define AIROPMACOFF 57 #define AIROFLSHRST 100 #define AIROFLSHGCHR 101 #define AIROFLSHSTFL 102 #define AIROFLSHPCHR 103 #define AIROFLPUTBUF 104 #define AIRORESTART 105 #define HEADERSIZE 256 #define FLASHSIZE 32768 #define FLASHBLOCKS 4 #define RIDSIZE 2048 typedef struct aironet_ioctl { unsigned short command; // What to do unsigned short len; // Len of data unsigned short ridnum; // ridnum unsigned char *data; // d-data } aironet_ioctl; unsigned char id[] = "Aironet Wireless Communications, Inc. Firmware Image File - PC4500"; unsigned char id2[] = "Aironet Wireless Communications, Inc. Firmware Image File - PC5000"; unsigned char header[HEADERSIZE]; unsigned char flash[FLASHBLOCKS][FLASHSIZE]; unsigned char rid_cap[RIDSIZE], rid_config[RIDSIZE], rid_ssid[RIDSIZE], rid_aplist[RIDSIZE]; unsigned char fv, hv; int recover; int socket_open(void) { int sock; if ((sock=socket(AF_INET, SOCK_DGRAM, 0)) != -1) return sock; #ifndef __FreeBSD__ if ((sock=socket(AF_IPX, SOCK_DGRAM, 0)) != -1) return sock; if ((sock=socket(AF_AX25, SOCK_DGRAM, 0)) != -1) return sock; #endif return socket(AF_APPLETALK, SOCK_DGRAM, 0); } int main(int argc, char **argv) { int i, j, skfd; /* generic raw socket desc. */ FILE *fp; #ifndef __FreeBSD__ struct iwreq wrq; #else struct ifreq wrq; #endif aironet_ioctl ai; /* Create a channel to the NET kernel. */ if((skfd = socket_open()) < 0) { perror("socket"); exit(-1); } if(argc == 4 && argv[1][0] == '-' && argv[1][1] == 'r' && argv[1][2] == 0) recover = 1; /* Special case for help... */ if(argc != 3 && (argc != 4 || !recover)) { close(skfd); fprintf(stderr, "Usage: airoflash [-r] interface firmware\n"); exit(0); } /* Open firmware file */ if ((fp = fopen (argv[argc-1], "r")) == NULL) { perror("firmware open"); return(-1); } /* Read file header */ if (fread (header, HEADERSIZE, 1, fp) != 1) { fprintf(stderr, "cannot read firmware file\n"); fclose(fp); return(-1); } /* Read firmware */ if (fread (flash, FLASHSIZE, FLASHBLOCKS, fp) != FLASHBLOCKS) { fprintf(stderr, "cannot read firmware file\n"); fclose(fp); return(-1); } fclose(fp); /* Is this really a header? What should I do with it? */ if (memcmp (header, id, sizeof(id) - 1) ||) { (memcmp (header, id2, sizeof(id) - 1)) { fprintf(stderr, "Invalid firmware file\n"); return(-1); } /* Check for Cisco extensions */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.ridnum = 0; ai.len = sizeof (i); ai.data = (unsigned char *)&i; if((ioctl(skfd, AIROIDIFC, &wrq) < 0)) { perror("Aironet extensions not found"); close(skfd); return(-1); } if (i != AIROMAGIC) { fprintf(stderr, "Aironet extensions not found\n"); close(skfd); return(-1); } if (!recover) { /* Read RID_CAPABILITIES */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROGCAP; ai.len = sizeof (rid_cap); ai.data = rid_cap; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("RID_CONFIG read"); close(skfd); return(-1); } fv = header[0x81]; hv = (rid_cap[7] << 8) | rid_cap[6]; if (!(((hv == 10) && ((fv == 0xb0) || (fv == 0xc0))) || /* 350 */ ((hv == 8) && ((fv == 0xb0) || (fv == 0xc0) || (fv == 0xd0))) || /* 350 also ??? */ ((hv == 7) && ((fv == 0xb0) || (fv == 0xc0))) || /* 340 */ ((hv == 5) && ((fv == 0xb0) || (fv == 0xb1))) || /* 4500 */ ((hv == 6) && ((fv == 0xb0) || (fv == 0xb1))) || /* 2500 */ ((hv == 4) && (fv == 0x90)) || /* 3500 */ ((hv == 9) && (fv == 0x91)))) { /* 3100 */ fprintf(stderr, "Incompatible firmware - hardware version (%d,%x)\n", hv, fv); close(skfd); return(-1); } /* Read RID_CONFIG */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROGCFG; ai.len = sizeof (rid_config); ai.data = rid_config; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("RID_CONFIG read"); close(skfd); return(-1); } /* Read RID_APLIST */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROGVLIST; ai.len = sizeof (rid_aplist); ai.data = rid_aplist; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("RID_APLIST read"); close(skfd); return(-1); } /* Read RID_SSID */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROGSLIST; ai.len = sizeof (rid_ssid); ai.data = rid_ssid; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("RID_SSID read"); close(skfd); return(-1); } /* Disable MAC */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROPMACOFF; ai.len = 0; ai.data = NULL; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("card reset"); close(skfd); return(-1); } /* Reset Aironet */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROFLSHRST; ai.len = 0; ai.data = NULL; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("card reset"); close(skfd); return(-1); } } /* Set flash mode */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROFLSHSTFL; ai.len = 0; ai.data = NULL; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("flash mode"); close(skfd); return(-1); } /* Wait for ack */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROFLSHGCHR; ai.len = sizeof (int); i = '*'; ai.data = (unsigned char *)&i; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("flash mode ack"); close(skfd); return(-1); } /* Send flash load command */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROFLSHPCHR; ai.len = sizeof (int); i = 'L'; ai.data = (unsigned char *)&i; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("program command"); close(skfd); return(-1); } strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROFLSHPCHR; ai.len = sizeof (int); i = '\r'; ai.data = (unsigned char *)&i; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("program command"); close(skfd); return(-1); } /* Download flash */ for (j = 0; j < FLASHBLOCKS; j++) { /* Wait for ack */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROFLSHGCHR; ai.len = sizeof (int); i = 0x1B; ai.data = (unsigned char *)&i; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("download ack"); close(skfd); return(-1); } strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROFLPUTBUF; ai.len = FLASHSIZE; ai.data = flash[j]; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("firmware download"); close(skfd); return(-1); } } /* Wait for ack */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROFLSHGCHR; ai.len = sizeof (int); i = 0x80; ai.data = (unsigned char *)&i; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("download complete ack"); close(skfd); return(-1); } /* Wait for ack */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROFLSHGCHR; ai.len = sizeof (int); i = '*'; ai.data = (unsigned char *)&i; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("flash ack"); close(skfd); return(-1); } /* Send flash command */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROFLSHPCHR; ai.len = sizeof (int); i = recover ? 'Q' : 'g'; ai.data = (unsigned char *)&i; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("flash command"); close(skfd); return(-1); } strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROFLSHPCHR; ai.len = sizeof (int); i = '\r'; ai.data = (unsigned char *)&i; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("flash command"); close(skfd); return(-1); } /* Wait for ack */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROFLSHGCHR; ai.len = sizeof (int); i = '*'; ai.data = (unsigned char *)&i; ioctl(skfd, AIROIOCTL, &wrq); /* Restart card */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIRORESTART; ai.len = 0; ai.data = NULL; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("restart"); close(skfd); return(-1); } if (!recover) { /* Write RID_SSID */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROGSLIST; ai.len = *(unsigned short *)rid_ssid; ai.data = rid_ssid; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("RID_SSID read"); close(skfd); return(-1); } /* Write RID_APLIST */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROGVLIST; ai.len = *(unsigned short *)rid_aplist; ai.data = rid_aplist; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("RID_APLIST read"); close(skfd); return(-1); } /* Write RID_CONFIG */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROPCFG; ai.len = *(unsigned short *)rid_config; ai.data = rid_config; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("RID_CONFIG read"); close(skfd); return(-1); } } /* Enable MAC */ strcpy (wrq.ifr_name, argv[argc-2]); WRQ_DATA(wrq) = (caddr_t)&ai; ai.command = AIROPMACON; ai.len = 0; ai.data = NULL; if(ioctl(skfd, AIROIOCTL, &wrq) < 0) { perror("card reset"); close(skfd); return(-1); } /* Close the socket. */ close(skfd); return(0); }