X-Git-Url: http://git.osdn.net/view?p=android-x86%2Fexternal-wireless-tools.git;a=blobdiff_plain;f=wireless_tools%2Fifrename.c;h=9c6c8067763384b1c512d5b79594f864744f6b00;hp=7d4ab5bfc5c3618465cb4a899362039f75b2ddce;hb=41b8e9b1157054ddcb90e68f1b12707811c90361;hpb=9d82610feb36dcd56d621e7831cbf5eec3f51104 diff --git a/wireless_tools/ifrename.c b/wireless_tools/ifrename.c index 7d4ab5b..9c6c806 100644 --- a/wireless_tools/ifrename.c +++ b/wireless_tools/ifrename.c @@ -1,14 +1,14 @@ /* * Wireless Tools * - * Jean II - HPL 04 + * Jean II - HPL 04 -> 07 * * Main code for "ifrename". This is tool allows to rename network * interfaces based on various criteria (not only wireless). * You need to link this code against "iwlib.c" and "-lm". * * This file is released under the GPL license. - * Copyright (c) 2004 Jean Tourrilhes + * Copyright (c) 2007 Jean Tourrilhes */ /* @@ -22,21 +22,20 @@ * Subject to the Gnu Public License, version 2. * TODO: make it support token ring etc. * $Id: nameif.c,v 1.3 2003/03/06 23:26:52 ecki Exp $ - * Add hotplug compatibility : ifname -i eth0. Jean II - 03.12.03 - * Add MAC address wildcard : 01:23:45:*. Jean II - 03.12.03 - * Add interface name wildcard : wlan*. Jean II - 03.12.03 - * Add interface name probing for modular systems. Jean II - 18.02.03 * ------------------------------------------------------- * - * The last 4 patches never made it into the regular version of - * 'nameif', and had some 'issues', which is the reason of this rewrite. + * It started with a series of patches to nameif which never made + * into the regular version, and had some architecural 'issues' with + * those patches, which is the reason of this rewrite. * Difference with standard 'nameif' : * o 'nameif' has only a single selector, the interface MAC address. * o Modular selector architecture, easily add new selectors. + * o Wide range of selector, including sysfs and sysfs symlinks... * o hotplug invocation support. * o module loading support. * o MAC address wildcard. * o Interface name wildcard ('eth*' or 'wlan*'). + * o Non-Ethernet MAC addresses (any size, not just 48 bits) */ /***************************** INCLUDES *****************************/ @@ -81,11 +80,14 @@ const int SELECT_BASEADDR = 7; /* Select by HW Base Address */ const int SELECT_IRQ = 8; /* Select by HW Irq line */ const int SELECT_INTERRUPT = 9; /* Select by HW Irq line */ const int SELECT_IWPROTO = 10; /* Select by Wireless Protocol */ -const int SELECT_PCMCIASLOT = 11; /* Select by Wireless Protocol */ -#define SELECT_NUM 12 +const int SELECT_PCMCIASLOT = 11; /* Select by Pcmcia Slot */ +const int SELECT_SYSFS = 12; /* Select by sysfs file */ +const int SELECT_PREVNAME = 13; /* Select by previous interface name */ +#define SELECT_NUM 14 #define HAS_MAC_EXACT 1 #define HAS_MAC_FILTER 2 +#define MAX_MAC_LEN 16 /* Maximum lenght of MAC address */ const struct ether_addr zero_mac = {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; @@ -98,6 +100,7 @@ const struct option long_opt[] = {"interface", 1, NULL, 'i' }, {"newname", 1, NULL, 'n' }, {"takeover", 0, NULL, 't' }, + {"udev", 0, NULL, 'u' }, {"version", 0, NULL, 'v' }, {"verbose", 0, NULL, 'V' }, {NULL, 0, NULL, '\0' }, @@ -107,6 +110,29 @@ const struct option long_opt[] = #define PCMCIA_STAB1 "/var/lib/pcmcia/stab" #define PCMCIA_STAB2 "/var/run/stab" +/* Max number of sysfs file types we support */ +#define SYSFS_MAX_FILE 8 + +/* Userspace headers lag, fix that... */ +#ifndef ARPHRD_IEEE1394 +#define ARPHRD_IEEE1394 24 +#endif +#ifndef ARPHRD_EUI64 +#define ARPHRD_EUI64 27 +#endif +#ifndef ARPHRD_IRDA +#define ARPHRD_IRDA 783 +#endif + +/* Length of various non-standard MAC addresses */ +const int weird_mac_len[][2] = +{ + { ARPHRD_IEEE1394, 8 }, + { ARPHRD_EUI64, 8 }, + { ARPHRD_IRDA, 4 }, +}; +const int weird_mac_len_num = sizeof(weird_mac_len) / sizeof(weird_mac_len[0]); + /****************************** TYPES ******************************/ /* Cut'n'paste from ethtool.h */ @@ -136,13 +162,16 @@ typedef struct if_mapping /* Name of this interface */ char ifname[IFNAMSIZ+1]; + char * sysfs_devpath; + int sysfs_devplen; /* Selectors for this interface */ int active[SELECT_NUM]; /* Selectors active */ /* Selector data */ - struct ether_addr mac; /* Exact MAC address, hex */ - char mac_filter[6*3 + 1]; /* WildCard, ascii */ + unsigned char mac[MAX_MAC_LEN]; /* Exact MAC address, hex */ + int mac_len; /* Length (usually 6) */ + char mac_filter[16*3 + 1]; /* WildCard, ascii */ unsigned short hw_type; /* Link/ARP type */ char driver[32]; /* driver short name */ char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */ @@ -151,13 +180,23 @@ typedef struct if_mapping unsigned char irq; /* HW irq line */ char iwproto[IFNAMSIZ + 1]; /* Wireless/protocol name */ int pcmcia_slot; /* Pcmcia slot */ -} if_mapping; + char * sysfs[SYSFS_MAX_FILE]; /* sysfs selectors */ + char prevname[IFNAMSIZ+1]; /* previous interface name */ +} if_mapping; + +/* Extra parsing information when adding a mapping */ +typedef struct add_extra +{ + char * modif_pos; /* Descriptor modifier */ + size_t modif_len; +} parsing_extra; /* Prototype for adding a selector to a mapping. Return -1 if invalid value. */ typedef int (*mapping_add)(struct if_mapping * ifnode, int * active, char * pos, size_t len, + struct add_extra * extra, int linenum); /* Prototype for comparing the selector of two mapping. Return 0 if matches. */ @@ -178,6 +217,15 @@ typedef struct mapping_selector mapping_get get_fn; } mapping_selector; +/* sysfs global data */ +typedef struct sysfs_metadata +{ + char * root; /* Root of the sysfs */ + int rlen; /* Size of it */ + int filenum; /* Number of files */ + char * filename[SYSFS_MAX_FILE]; /* Name of files */ +} sysfs_metadata; + /**************************** PROTOTYPES ****************************/ static int @@ -185,6 +233,7 @@ static int int * active, char * pos, size_t len, + struct add_extra * extra, int linenum); static int mapping_cmpmac(struct if_mapping * ifnode, @@ -199,6 +248,7 @@ static int int * active, char * pos, size_t len, + struct add_extra * extra, int linenum); static int mapping_cmparp(struct if_mapping * ifnode, @@ -213,6 +263,7 @@ static int int * active, char * pos, size_t len, + struct add_extra * extra, int linenum); static int mapping_cmpdriver(struct if_mapping * ifnode, @@ -222,6 +273,7 @@ static int int * active, char * pos, size_t len, + struct add_extra * extra, int linenum); static int mapping_cmpbusinfo(struct if_mapping * ifnode, @@ -231,6 +283,7 @@ static int int * active, char * pos, size_t len, + struct add_extra * extra, int linenum); static int mapping_cmpfirmware(struct if_mapping * ifnode, @@ -245,6 +298,7 @@ static int int * active, char * pos, size_t len, + struct add_extra * extra, int linenum); static int mapping_cmpbaseaddr(struct if_mapping * ifnode, @@ -254,6 +308,7 @@ static int int * active, char * pos, size_t len, + struct add_extra * extra, int linenum); static int mapping_cmpirq(struct if_mapping * ifnode, @@ -268,6 +323,7 @@ static int int * active, char * pos, size_t len, + struct add_extra * extra, int linenum); static int mapping_cmpiwproto(struct if_mapping * ifnode, @@ -282,6 +338,7 @@ static int int * active, char * pos, size_t len, + struct add_extra * extra, int linenum); static int mapping_cmppcmciaslot(struct if_mapping * ifnode, @@ -291,6 +348,36 @@ static int const char * ifname, struct if_mapping * target, int flag); +static int + mapping_addsysfs(struct if_mapping * ifnode, + int * active, + char * pos, + size_t len, + struct add_extra * extra, + int linenum); +static int + mapping_cmpsysfs(struct if_mapping * ifnode, + struct if_mapping * target); +static int + mapping_getsysfs(int skfd, + const char * ifname, + struct if_mapping * target, + int flag); +static int + mapping_addprevname(struct if_mapping * ifnode, + int * active, + char * pos, + size_t len, + struct add_extra * extra, + int linenum); +static int + mapping_cmpprevname(struct if_mapping * ifnode, + struct if_mapping * target); +static int + mapping_getprevname(int skfd, + const char * ifname, + struct if_mapping * target, + int flag); /**************************** VARIABLES ****************************/ @@ -321,6 +408,10 @@ const struct mapping_selector selector_list[] = { "iwproto", &mapping_addiwproto, &mapping_cmpiwproto, &mapping_getiwproto }, /* Pcmcia slot from cardmgr */ { "pcmciaslot", &mapping_addpcmciaslot, &mapping_cmppcmciaslot, &mapping_getpcmciaslot }, + /* sysfs file (udev emulation) */ + { "sysfs", &mapping_addsysfs, &mapping_cmpsysfs, &mapping_getsysfs }, + /* previous interface name */ + { "prevname", &mapping_addprevname, &mapping_cmpprevname, &mapping_getprevname }, /* The Terminator */ { NULL, NULL, NULL, NULL }, }; @@ -329,16 +420,40 @@ const int selector_num = sizeof(selector_list)/sizeof(selector_list[0]); /* List of active selectors */ int selector_active[SELECT_NUM]; /* Selectors active */ +/* + * All the following flags are controlled by the command line switches... + * It's a bit hackish to have them all as global, so maybe we should pass + * them in a big struct as function arguments... More complex and + * probably not worth it ? + */ + +/* Invocation type */ +int print_newname = 0; +char * new_name = NULL; + /* Takeover support */ -int force_takeover = 0; /* Takeover name from other interface */ +int force_takeover = 0; /* Takeover name from other interfaces */ int num_takeover = 0; /* Number of takeover done */ +/* Number of mapping matched */ +int num_mapping_match = 0; + /* Dry-run support */ int dry_run = 0; /* Just print new name, don't rename */ /* Verbose support (i.e. debugging) */ int verbose = 0; +/* udev output support (print new DEVPATH) */ +int udev_output = 0; + +/* sysfs global data */ +struct sysfs_metadata sysfs_global = +{ + NULL, 0, + 0, { NULL, NULL, NULL, NULL, NULL }, +}; + /******************** INTERFACE NAME MANAGEMENT ********************/ /* * Bunch of low level function for managing interface names. @@ -362,8 +477,8 @@ if_match_ifname(const char * pattern, int n; int ret; - /* Check for a wildcard (converted from '*' to '%d' in mapping_create()) */ - p = strstr(pattern, "%d"); + /* Check for a wildcard */ + p = strchr(pattern, '*'); /* No wildcard, simple comparison */ if(p == NULL) @@ -386,7 +501,7 @@ if_match_ifname(const char * pattern, while(isdigit(*v)); /* Pattern suffix */ - p += 2; + p += 1; /* Compare suffixes */ return(strcmp(p, v)); @@ -452,6 +567,7 @@ if_set_name(int skfd, char * retname) { struct ifreq ifr; + char * star; int ret; /* The kernel doesn't check is the interface already has the correct @@ -475,6 +591,22 @@ if_set_name(int skfd, strncpy(ifr.ifr_name, oldname, IFNAMSIZ); strncpy(ifr.ifr_newname, newname, IFNAMSIZ); + /* Check for wildcard interface name, such as 'eth*' or 'wlan*'... + * This require specific kernel support (2.6.2-rc1 and later). + * We externally use '*', but the kernel doesn't know about that, + * so convert it to something it knows about... */ + star = strchr(newname, '*'); + if(star != NULL) + { + int slen = star - newname; + /* Replace '*' with '%d' in the new buffer */ + star = ifr.ifr_newname + slen; + /* Size was checked in process_rename() and mapping_create() */ + memmove(star + 2, star + 1, IFNAMSIZ - slen - 2); + star[0] = '%'; + star[1] = 'd'; + } + /* Do it */ ret = ioctl(skfd, SIOCSIFNAME, &ifr); @@ -515,14 +647,18 @@ mapping_addmac(struct if_mapping * ifnode, int * active, char * string, size_t len, + struct add_extra * extra, int linenum) { size_t n; + /* Avoid "Unused parameter" warning */ + extra = extra; + /* Verify validity of string */ if(len >= sizeof(ifnode->mac_filter)) { - fprintf(stderr, "MAC address too long at line %d\n", linenum); + fprintf(stderr, "Error : MAC address too long at line %d\n", linenum); return(-1); } n = strspn(string, "0123456789ABCDEFabcdef:*"); @@ -547,7 +683,9 @@ mapping_addmac(struct if_mapping * ifnode, else { /* Not a wildcard : "01:23:45:67:89:AB" */ - if(iw_ether_aton(ifnode->mac_filter, &ifnode->mac) != 1) + ifnode->mac_len = iw_mac_aton(ifnode->mac_filter, + ifnode->mac, MAX_MAC_LEN); + if(ifnode->mac_len == 0) { fprintf(stderr, "Error: Invalid MAC address `%s' at line %d\n", ifnode->mac_filter, linenum); @@ -555,7 +693,7 @@ mapping_addmac(struct if_mapping * ifnode, } /* Check that it's not NULL */ - if(!memcmp(&ifnode->mac, &zero_mac, 6)) + if((ifnode->mac_len == 6) && (!memcmp(&ifnode->mac, &zero_mac, 6))) { fprintf(stderr, "Warning: MAC address is null at line %d, this is dangerous...\n", @@ -590,8 +728,8 @@ mapping_cmpmac(struct if_mapping * ifnode, return(fnmatch(ifnode->mac_filter, target->mac_filter, FNM_CASEFOLD)); else /* Exact matching, in hex */ - return(memcmp(&ifnode->mac.ether_addr_octet, &target->mac.ether_addr_octet, - 6)); + return((ifnode->mac_len != target->mac_len) || + memcmp(ifnode->mac, target->mac, ifnode->mac_len)); } /*------------------------------------------------------------------*/ @@ -604,10 +742,14 @@ mapping_getmac(int skfd, struct if_mapping * target, int flag) { - int ret; + struct ifreq ifr; + int ret; + int i; - /* Extract MAC address */ - ret = iw_get_mac_addr(skfd, ifname, &target->mac, &target->hw_type); + /* Get MAC address */ + bzero(&ifr, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ret = ioctl(skfd, SIOCGIFHWADDR, &ifr); if(ret < 0) { fprintf(stderr, "Error: Can't read MAC address on interface `%s' : %s\n", @@ -615,11 +757,25 @@ mapping_getmac(int skfd, return(-1); } + /* Extract ARP type */ + target->hw_type = ifr.ifr_hwaddr.sa_family; + /* Calculate address length */ + target->mac_len = 6; + for(i = 0; i < weird_mac_len_num; i++) + if(weird_mac_len[i][0] == ifr.ifr_hwaddr.sa_family) + { + target->mac_len = weird_mac_len[i][1]; + break; + } + /* Extract MAC address bytes */ + memcpy(target->mac, ifr.ifr_hwaddr.sa_data, target->mac_len); + /* Check the type of comparison */ if((flag == HAS_MAC_FILTER) || verbose) { /* Convert to ASCII */ - iw_ether_ntop(&target->mac, target->mac_filter); + iw_mac_ntop(target->mac, target->mac_len, + target->mac_filter, sizeof(target->mac_filter)); } target->active[SELECT_MAC] = flag; @@ -642,11 +798,15 @@ mapping_addarp(struct if_mapping * ifnode, int * active, char * string, size_t len, + struct add_extra * extra, int linenum) { size_t n; unsigned int type; + /* Avoid "Unused parameter" warning */ + extra = extra; + /* Verify validity of string, convert to int */ n = strspn(string, "0123456789"); if((n < len) || (sscanf(string, "%d", &type) != 1)) @@ -705,12 +865,16 @@ mapping_adddriver(struct if_mapping * ifnode, int * active, char * string, size_t len, + struct add_extra * extra, int linenum) { + /* Avoid "Unused parameter" warning */ + extra = extra; + /* Plain string, minimal verification */ if(len >= sizeof(ifnode->driver)) { - fprintf(stderr, "Driver name too long at line %d\n", linenum); + fprintf(stderr, "Error: Driver name too long at line %d\n", linenum); return(-1); } @@ -750,12 +914,16 @@ mapping_addbusinfo(struct if_mapping * ifnode, int * active, char * string, size_t len, + struct add_extra * extra, int linenum) { #if 0 size_t n; #endif + /* Avoid "Unused parameter" warning */ + extra = extra; + /* Verify validity of string */ if(len >= sizeof(ifnode->bus_info)) { @@ -809,8 +977,12 @@ mapping_addfirmware(struct if_mapping * ifnode, int * active, char * string, size_t len, + struct add_extra * extra, int linenum) { + /* Avoid "Unused parameter" warning */ + extra = extra; + /* Verify validity of string */ if(len >= sizeof(ifnode->fw_version)) { @@ -913,11 +1085,15 @@ mapping_addbaseaddr(struct if_mapping * ifnode, int * active, char * string, size_t len, + struct add_extra * extra, int linenum) { size_t n; unsigned int address; + /* Avoid "Unused parameter" warning */ + extra = extra; + /* Verify validity of string */ n = strspn(string, "0123456789ABCDEFabcdefx"); if((n < len) || (sscanf(string, "0x%X", &address) != 1)) @@ -963,11 +1139,15 @@ mapping_addirq(struct if_mapping * ifnode, int * active, char * string, size_t len, + struct add_extra * extra, int linenum) { size_t n; unsigned int irq; + /* Avoid "Unused parameter" warning */ + extra = extra; + /* Verify validity of string */ n = strspn(string, "0123456789"); if((n < len) || (sscanf(string, "%d", &irq) != 1)) @@ -1068,8 +1248,12 @@ mapping_addiwproto(struct if_mapping * ifnode, int * active, char * string, size_t len, + struct add_extra * extra, int linenum) { + /* Avoid "Unused parameter" warning */ + extra = extra; + /* Verify validity of string */ if(len >= sizeof(ifnode->iwproto)) { @@ -1144,13 +1328,17 @@ mapping_getiwproto(int skfd, */ static int mapping_addpcmciaslot(struct if_mapping * ifnode, - int * active, - char * string, - size_t len, - int linenum) + int * active, + char * string, + size_t len, + struct add_extra * extra, + int linenum) { size_t n; + /* Avoid "Unused parameter" warning */ + extra = extra; + /* Verify validity of string, convert to int */ n = strspn(string, "0123456789"); if((n < len) || (sscanf(string, "%d", &ifnode->pcmcia_slot) != 1)) @@ -1176,7 +1364,7 @@ mapping_addpcmciaslot(struct if_mapping * ifnode, */ static int mapping_cmppcmciaslot(struct if_mapping * ifnode, - struct if_mapping * target) + struct if_mapping * target) { return(!(ifnode->pcmcia_slot == target->pcmcia_slot)); } @@ -1290,10 +1478,450 @@ mapping_getpcmciaslot(int skfd, /* Cleanup */ free(linebuf); + fclose(stream); return(target->active[SELECT_PCMCIASLOT] ? 0 : -1); } +/*------------------------------------------------------------------*/ +/* + * Add a sysfs selector to a mapping + */ +static int +mapping_addsysfs(struct if_mapping * ifnode, + int * active, + char * string, + size_t len, + struct add_extra * extra, + int linenum) +{ + int findex; /* filename index */ + char * sdup; + + /* Check if we have a modifier */ + if((extra == NULL) || (extra->modif_pos == NULL)) + { + fprintf(stderr, "Error: No SYSFS filename at line %d\n", linenum); + return(-1); + } + + /* Search if the filename already exist */ + for(findex = 0; findex < sysfs_global.filenum; findex++) + { + if(!strcmp(extra->modif_pos, sysfs_global.filename[findex])) + break; + } + + /* If filename does not exist, creates it */ + if(findex == sysfs_global.filenum) + { + if(findex == SYSFS_MAX_FILE) + { + fprintf(stderr, "Error: Too many SYSFS filenames at line %d\n", linenum); + return(-1); + } + sdup = strndup(extra->modif_pos, extra->modif_len); + if(sdup == NULL) + { + fprintf(stderr, "Error: Can't allocate SYSFS file\n"); + return(-1); + } + sysfs_global.filename[findex] = sdup; + sysfs_global.filenum++; + } + + /* Store value */ + sdup = strndup(string, len); + if(sdup == NULL) + { + fprintf(stderr, "Error: Can't allocate SYSFS value\n"); + return(-1); + } + ifnode->sysfs[findex] = sdup; + + /* Activate */ + ifnode->active[SELECT_SYSFS] = 1; + active[SELECT_SYSFS] = 1; + + if(verbose) + fprintf(stderr, + "Parsing : Added SYSFS filename `%s' value `%s' from line %d.\n", + sysfs_global.filename[findex], ifnode->sysfs[findex], linenum); + + return(0); +} + +/*------------------------------------------------------------------*/ +/* + * Compare all the sysfs values of two mappings + */ +static int +mapping_cmpsysfs(struct if_mapping * ifnode, + struct if_mapping * target) +{ + int findex; /* filename index */ + int match = 1; + + /* Loop on all sysfs selector */ + for(findex = 0; findex < sysfs_global.filenum; findex++) + { + /* If the mapping defines this sysfs selector.. */ + if(ifnode->sysfs[findex] != NULL) + /* And if the sysfs values don't match */ + if((target->sysfs[findex] == NULL) || + (fnmatch(ifnode->sysfs[findex], target->sysfs[findex], + FNM_CASEFOLD))) + /* Then the sysfs selector doesn't match */ + match = 0; + } + + return(!match); +} + +/*------------------------------------------------------------------*/ +/* + * Extract all the sysfs values of an interface + */ +static int +mapping_getsysfs(int skfd, + const char * ifname, + struct if_mapping * target, + int flag) +{ + FILE * stream; + char * fname; + int fnsize; + char * linebuf = NULL; + size_t linelen = 0; + char * sdup; + int findex; /* filename index */ + + /* Avoid "Unused parameter" warning */ + skfd = skfd; + flag = flag; + + /* Check if we know the devpath of this device */ + if(target->sysfs_devpath == NULL) + { + /* Check if we know the root of the sysfs filesystem */ + if(sysfs_global.root == NULL) + { + /* Open the mount file for reading */ + stream = fopen("/proc/mounts", "r"); + if(!stream) + { + fprintf(stderr, "Error: Can't open /proc/mounts file: %s\n", + strerror(errno)); + return(-1); + } + + /* Read each line of file + * getline is a GNU extension :-( The buffer is recycled and + * increased as needed by getline. */ + while(getline(&linebuf, &linelen, stream) > 0) + { + int i; + char * p; + size_t n; + char * token[3]; + size_t toklen[3]; + + /* The format of /proc/mounts is similar to /etc/fstab (5). + * The first argument is the device. For sysfs, there is no + * associated device, so this argument is ignored. + * The second argument is the mount point. + * The third argument is the filesystem type. + */ + + /* Extract the first 3 tokens */ + p = linebuf; + for(i = 0; i < 3; i++) + { + while(isspace(*p)) + ++p; + token[i] = p; + n = strcspn(p, " \t\n"); + toklen[i] = n; + p += n; + } + /* Get the filesystem which type is "sysfs" */ + if((n == 5) && (!strncasecmp(token[2], "sysfs", 5))) + { + /* Get its mount point */ + n = toklen[1]; + sdup = strndup(token[1], n); + if((n == 0) || (sdup == NULL)) + { + fprintf(stderr, + "Error: Can't parse /proc/mounts file: %s\n", + strerror(errno)); + return(-1); + } + /* Store it */ + sysfs_global.root = sdup; + sysfs_global.rlen = n; + break; + } + /* Finished -> next line */ + } + + /* Cleanup */ + fclose(stream); + + /* Check if we found it */ + if(sysfs_global.root == NULL) + { + fprintf(stderr, + "Error: Can't find sysfs in /proc/mounts file\n"); + free(linebuf); + return(-1); + } + } + + /* Construct devpath for this interface. + * Reserve enough space to replace name without realloc. */ + fnsize = (sysfs_global.rlen + 11 + IFNAMSIZ + 1); + fname = malloc(fnsize); + if(fname == NULL) + { + fprintf(stderr, "Error: Can't allocate SYSFS devpath\n"); + return(-1); + } + /* Not true devpath for 2.6.20+, but this syslink should work */ + target->sysfs_devplen = sprintf(fname, "%s/class/net/%s", + sysfs_global.root, ifname); + target->sysfs_devpath = fname; + } + + /* Loop on all sysfs selector */ + for(findex = 0; findex < sysfs_global.filenum; findex++) + { + char * p; + ssize_t n; + + /* Construct complete filename for the sysfs selector */ + fnsize = (target->sysfs_devplen + 1 + + strlen(sysfs_global.filename[findex]) + 1); + fname = malloc(fnsize); + if(fname == NULL) + { + fprintf(stderr, "Error: Can't allocate SYSFS filename\n"); + free(linebuf); + return(-1); + } + sprintf(fname, "%s/%s", target->sysfs_devpath, + sysfs_global.filename[findex]); + + /* Open the sysfs file for reading */ + stream = fopen(fname, "r"); + if(!stream) + { + /* Some sysfs attribute may no exist for some interface */ + if(verbose) + fprintf(stderr, "Error: Can't open file `%s': %s\n", fname, + strerror(errno)); + /* Next sysfs selector */ + continue; + } + + /* Read file. Only one line in file. */ + n = getline(&linebuf, &linelen, stream); + fclose(stream); + if(n <= 0) + { + /* Some attributes are just symlinks to another directory. + * We can read the attributes in that other directory + * just fine, but sometimes the symlink itself gives a lot + * of information. + * Examples : SYSFS{device} and SYSFS{device/driver} + * In such cases, get the name of the directory pointed to... + */ + /* + * I must note that the API for readlink() is very bad, + * which force us to have this ugly code. Yuck ! + */ + int allocsize = 128; /* 256 = Good start */ + int retry = 16; + char * linkpath = NULL; + int pathlen; + + /* Try reading the link with increased buffer size */ + do + { + allocsize *= 2; + linkpath = realloc(linkpath, allocsize); + pathlen = readlink(fname, linkpath, allocsize); + /* If we did not hit the buffer limit, success */ + if(pathlen < allocsize) + break; + } + while(retry-- > 0); + + /* Check for error, most likely ENOENT */ + if(pathlen > 0) + /* We have a symlink ;-) Terminate the string. */ + linkpath[pathlen] = '\0'; + else + { + /* Error ! */ + free(linkpath); + + /* A lot of information in the sysfs is implicit, given + * by the position of a file in the tree. It is therefore + * important to be able to read the various components + * of a path. For this reason, we resolve '..' to the + * real name of the parent directory... */ + /* We have at least 11 char, see above */ + if(!strcmp(fname + fnsize - 4, "/..")) + //if(!strcmp(fname + strlen(fname) - 3, "/..")) + { + /* This procedure to get the realpath is not very + * nice, but it's the "best practice". Hmm... */ + int cwd_fd = open(".", O_RDONLY); + linkpath = NULL; + if(cwd_fd > 0) + { + int ret = chdir(fname); + if(ret == 0) + /* Using getcwd with NULL is a GNU extension. Nice. */ + linkpath = getcwd(NULL, 0); + /* This may fail, but it's not fatal */ + fchdir(cwd_fd); + } + /* Check if we suceeded */ + if(!linkpath) + { + free(linkpath); + if(verbose) + fprintf(stderr, "Error: Can't read parent directory `%s'\n", fname); + /* Next sysfs selector */ + continue; + } + } + else + { + /* Some sysfs attribute are void for some interface, + * we may have a real directory, or we may have permission + * issues... */ + if(verbose) + fprintf(stderr, "Error: Can't read file `%s'\n", fname); + /* Next sysfs selector */ + continue; + } + } + + /* Here, we have a link name or a parent directory name */ + + /* Keep only the last component of path name, save it */ + p = basename(linkpath); + sdup = strdup(p); + free(linkpath); + } + else + { + /* This is a regular file (well, pseudo file) */ + /* Get content, remove trailing '/n', save it */ + p = linebuf; + if(p[n - 1] == '\n') + n--; + sdup = strndup(p, n); + } + if(sdup == NULL) + { + fprintf(stderr, "Error: Can't allocate SYSFS value\n"); + free(linebuf); + return(-1); + } + target->sysfs[findex] = sdup; + + /* Activate */ + target->active[SELECT_SYSFS] = 1; + + if(verbose) + fprintf(stderr, + "Querying %s : Got SYSFS filename `%s' value `%s'.\n", + ifname, sysfs_global.filename[findex], target->sysfs[findex]); + + /* Finished : Next sysfs selector */ + } + + /* Cleanup */ + free(linebuf); + + return(target->active[SELECT_SYSFS] ? 0 : -1); +} + +/*------------------------------------------------------------------*/ +/* + * Add a Previous Interface Name selector to a mapping + */ +static int +mapping_addprevname(struct if_mapping * ifnode, + int * active, + char * string, + size_t len, + struct add_extra * extra, + int linenum) +{ + /* Avoid "Unused parameter" warning */ + extra = extra; + + /* Verify validity of string */ + if(len >= sizeof(ifnode->prevname)) + { + fprintf(stderr, "Old Interface Name too long at line %d\n", linenum); + return(-1); + } + + /* Copy */ + memcpy(ifnode->prevname, string, len + 1); + + /* Activate */ + ifnode->active[SELECT_PREVNAME] = 1; + active[SELECT_PREVNAME] = 1; + + if(verbose) + fprintf(stderr, + "Parsing : Added Old Interface Name `%s' from line %d.\n", + ifnode->prevname, linenum); + + return(0); +} + +/*------------------------------------------------------------------*/ +/* + * Compare the Previous Interface Name of two mappings + * Note : this one is special. + */ +static int +mapping_cmpprevname(struct if_mapping * ifnode, + struct if_mapping * target) +{ + /* Do wildcard matching, case insensitive */ + return(fnmatch(ifnode->prevname, target->ifname, FNM_CASEFOLD)); +} + +/*------------------------------------------------------------------*/ +/* + * Extract the Previous Interface Name from a live interface + */ +static int +mapping_getprevname(int skfd, + const char * ifname, + struct if_mapping * target, + int flag) +{ + /* Avoid "Unused parameter" warning */ + skfd = skfd; ifname = ifname; flag = flag; + + /* Don't do anything, it's already in target->ifname ;-) */ + + /* Activate */ + target->active[SELECT_PREVNAME] = 1; + + return(0); +} + /*********************** MAPPING MANAGEMENTS ***********************/ /* @@ -1314,8 +1942,10 @@ mapping_create(char * pos, struct if_mapping * ifnode; char * star; - /* Check overflow. */ - if(len > IFNAMSIZ) + star = memchr(pos, '*', len); + + /* Check overflow, need one extra char for wildcard */ + if((len + (star != NULL)) > IFNAMSIZ) { fprintf(stderr, "Error: Interface name `%.*s' too long at line %d\n", (int) len, pos, linenum); @@ -1334,8 +1964,11 @@ mapping_create(char * pos, memcpy(ifnode->ifname, pos, len); ifnode->ifname[len] = '\0'; - /* Check the interface name and issue various pedantic warnings */ - if((!strcmp(ifnode->ifname, "eth0")) || (!strcmp(ifnode->ifname, "wlan0"))) + /* Check the interface name and issue various pedantic warnings. + * We assume people using takeover want to force interfaces to those + * names and know what they are doing, so don't bother them... */ + if((!force_takeover) && + ((!strcmp(ifnode->ifname, "eth0")) || (!strcmp(ifnode->ifname, "wlan0")))) fprintf(stderr, "Warning: Interface name is `%s' at line %d, can't be mapped reliably.\n", ifnode->ifname, linenum); @@ -1343,29 +1976,6 @@ mapping_create(char * pos, fprintf(stderr, "Warning: Alias device `%s' at line %d probably can't be mapped.\n", ifnode->ifname, linenum); - /* Check for wildcard interface name, such as 'eth*' or 'wlan*'... - * This require specific kernel support (2.6.2-rc1 and later). - * We externally use '*', but the kernel doesn't know about that, - * so convert it to something it knows about... */ - star = strchr(ifnode->ifname, '*'); - if(star != NULL) - { - /* We need an extra char */ - if(len >= IFNAMSIZ) - { - fprintf(stderr, - "Error: Interface wildcard `%s' too long at line %d\n", - ifnode->ifname, linenum); - free(ifnode); - return(NULL); - } - - /* Replace '*' with '%d' */ - memmove(star + 2, star + 1, len + 1 - (star - ifnode->ifname)); - star[0] = '%'; - star[1] = 'd'; - } - if(verbose) fprintf(stderr, "Parsing : Added Mapping `%s' from line %d.\n", ifnode->ifname, linenum); @@ -1433,10 +2043,11 @@ selector_find(const char * string, static int mapping_readfile(const char * filename) { - FILE * stream; - char * linebuf = NULL; - size_t linelen = 0; - int linenum = 0; + FILE * stream; + char * linebuf = NULL; + size_t linelen = 0; + int linenum = 0; + struct add_extra extrainfo; /* Reset the list of filters */ bzero(selector_active, sizeof(selector_active)); @@ -1497,9 +2108,10 @@ mapping_readfile(const char * filename) while(*p != '\0') { const struct mapping_selector * selector = NULL; + struct add_extra * extra = NULL; - /* Selector name length */ - n = strcspn(p, " \t\n"); + /* Selector name length - stop at modifier start */ + n = strcspn(p, " \t\n{"); /* Find it */ selector = selector_find(p, n, linenum); @@ -1508,9 +2120,33 @@ mapping_readfile(const char * filename) ret = -1; break; } + p += n; + + /* Check for modifier */ + if(*p == '{') + { + p++; + /* Find end of modifier */ + e = strchr(p, '}'); + if(e == NULL) + { + fprintf(stderr, + "Error: unterminated selector modifier value on line %d\n", + linenum); + ret = -1; + break; /* Line ended */ + } + /* Fill in struct and hook it */ + extrainfo.modif_pos = p; + extrainfo.modif_len = e - p; + extra = &extrainfo; + /* Terminate modifier value */ + e[0] = '\0'; + /* Skip it */ + p = e + 1; + } /* Get to selector value */ - p += n; p += strspn(p, " \t\n"); if(*p == '\0') { @@ -1548,7 +2184,8 @@ mapping_readfile(const char * filename) p[n] = '\0'; /* Add it to the mapping */ - ret = selector->add_fn(ifnode, selector_active, p, n, linenum); + ret = selector->add_fn(ifnode, selector_active, p, n, + extra, linenum); if(ret < 0) break; @@ -1679,22 +2316,21 @@ mapping_find(struct if_mapping * target) * Probe interfaces based on our list of mappings. * This is the default, but usually not the best way to do it. */ -static void +static inline void probe_mappings(int skfd) { struct if_mapping * ifnode; - struct ether_addr mac; /* Exact MAC address, hex */ - unsigned short hw_type; + struct ifreq ifr; /* Look over all our mappings */ for(ifnode = mapping_list; ifnode != NULL; ifnode = ifnode->next) { /* Can't load wildcards interface name :-( */ - if(strchr(ifnode->ifname, '%') != NULL) + if(strchr(ifnode->ifname, '*') != NULL) continue; if(verbose) - fprintf(stderr, "Probing : Trying to load interface [%s]\n", + fprintf(stderr, "Probing : Trying to load/probe interface [%s]\n", ifnode->ifname); /* Trick the kernel into loading the interface. @@ -1703,7 +2339,8 @@ probe_mappings(int skfd) * Obviously, we expect this command to 'fail', as * the interface will load with the old/wrong name. */ - iw_get_mac_addr(skfd, ifnode->ifname, &mac, &hw_type); + strncpy(ifr.ifr_name, ifnode->ifname, IFNAMSIZ); + ioctl(skfd, SIOCGIFHWADDR, &ifr); } } @@ -1714,14 +2351,13 @@ probe_mappings(int skfd) * all built-in interfaces that should remain unconfigured won't * be probed (and can have mappings). */ -static void +static inline void probe_debian(int skfd) { FILE * stream; char * linebuf = NULL; size_t linelen = 0; - struct ether_addr mac; /* Exact MAC address, hex */ - unsigned short hw_type; + struct ifreq ifr; /* Open Debian config file */ stream = fopen(DEBIAN_CONFIG_FILE, "r"); @@ -1769,8 +2405,9 @@ probe_debian(int skfd) fprintf(stderr, "Probing : Trying to load interface [%s]\n", p); - /* Do it ! */ - iw_get_mac_addr(skfd, p, &mac, &hw_type); + /* Load interface */ + strncpy(ifr.ifr_name, p, IFNAMSIZ); + ioctl(skfd, SIOCGIFHWADDR, &ifr); /* Go to next interface name */ p = e; @@ -1793,38 +2430,23 @@ probe_debian(int skfd) static int process_rename(int skfd, char * ifname, - char * pattern) + char * newname) { - char newname[IFNAMSIZ+1]; char retname[IFNAMSIZ+1]; int len; char * star; - len = strlen(pattern); - star = strchr(pattern, '*'); + len = strlen(newname); + star = strchr(newname, '*'); /* Check newname length, need one extra char for wildcard */ if((len + (star != NULL)) > IFNAMSIZ) { fprintf(stderr, "Error: Interface name `%s' too long.\n", - pattern); + newname); return(-1); } - /* Copy to local buffer */ - memcpy(newname, pattern, len + 1); - - /* Convert wildcard to the proper format */ - if(star != NULL) - { - /* Replace '*' with '%d' in the new buffer */ - star += newname - pattern; - memmove(star + 2, star + 1, len + 1 - (star - newname)); - star[0] = '%'; - star[1] = 'd'; - } - - /* Change the name of the interface */ if(if_set_name(skfd, ifname, newname, retname) < 0) { @@ -1864,36 +2486,83 @@ process_ifname(int skfd, if(target == NULL) return(-1); + /* If udev is calling us, get the real devpath. */ + if(udev_output) + { + const char *env; + /* It's passed to us as an environment variable */ + env = getenv("DEVPATH"); + if(env) + { + int env_len = strlen(env); + target->sysfs_devplen = env_len; + /* Make enough space for new interface name */ + target->sysfs_devpath = malloc(env_len + IFNAMSIZ + 1); + if(target->sysfs_devpath != NULL) + memcpy(target->sysfs_devpath, env, env_len + 1); + } + /* We will get a second chance is the user has some sysfs selectors */ + } + /* Find matching mapping */ mapping = mapping_find(target); if(mapping == NULL) return(-1); + /* If user specified a new name, keep only interfaces that would + * match the new name... */ + if((new_name != NULL) && (if_match_ifname(mapping->ifname, new_name) != 0)) + return(-1); + /* Check if user want only dry-run. * Note that, in the case of wildcard, we don't resolve the wildcard. * That would be tricky to do... */ if(dry_run) { - printf("Dry-run : Would rename %s to %s.\n", - target->ifname, mapping->ifname); - return(0); + strcpy(retname, mapping->ifname); + fprintf(stderr, "Dry-run : Would rename %s to %s.\n", + target->ifname, mapping->ifname); } - - /* Change the name of the interface */ - if(if_set_name(skfd, target->ifname, mapping->ifname, retname) < 0) + else { - fprintf(stderr, "Error: cannot change name of %s to %s: %s\n", - target->ifname, mapping->ifname, strerror(errno)); - return(-1); + /* Change the name of the interface */ + if(if_set_name(skfd, target->ifname, mapping->ifname, retname) < 0) + { + fprintf(stderr, "Error: cannot change name of %s to %s: %s\n", + target->ifname, mapping->ifname, strerror(errno)); + return(-1); + } } + /* This one matched */ + num_mapping_match++; + /* Check if called with an explicit interface name */ - if(!count) + if(print_newname) { - /* Always print out the *new* interface name so that - * the calling script can pick it up and know where its interface - * has gone. */ - printf("%s\n", retname); + if(!udev_output) + /* Always print out the *new* interface name so that + * the calling script can pick it up and know where its interface + * has gone. */ + printf("%s\n", retname); + else + /* udev likes to call us as an IMPORT action. This means that + * we need to return udev the environment variables changed. + * Obviously, we don't want to return anything is nothing changed. */ + if(strcmp(target->ifname, retname)) + { + char * pos; + /* Hack */ + if(!target->sysfs_devpath) + mapping_getsysfs(skfd, ifname, target, 0); + /* Update devpath. Size is large enough. */ + pos = strrchr(target->sysfs_devpath, '/'); + if((pos != NULL) && (!strcmp(target->ifname, pos + 1))) + strcpy(pos + 1, retname); + /* Return new environment variables */ + printf("DEVPATH=%s\nINTERFACE=%s\nINTERFACE_OLD=%s\n", + target->sysfs_devpath, retname, target->ifname); + } } /* Done */ @@ -1904,28 +2573,28 @@ process_ifname(int skfd, /* * Process all network interface present on the system. */ -static inline int +static int process_iflist(int skfd, char * args[], - int count) + int count, + int use_probe, + int is_debian) { num_takeover = 0; + num_mapping_match = 0; + + /* Load all the necesary modules */ + if(use_probe) + { + if(is_debian) + probe_debian(skfd); + else + probe_mappings(skfd); + } /* Just do it */ iw_enum_devices(skfd, &process_ifname, args, count); - /* If we do any takeover, the interface list grabbed with - * iw_enum_devices() may get out of sync with the real interfaces, - * and we may miss the victim interface. So, let's go through the - * list again. - * On the other hand, we may have ping pong between two interfaces, - * each claiming the same name, so let's not do it forever... - * Two time should be enough for most configs... - * Jean II */ - if(force_takeover && num_takeover) - /* Play it again, Sam... */ - iw_enum_devices(skfd, &process_ifname, args, count); - /* Done */ return(0); } @@ -1953,16 +2622,16 @@ main(int argc, { const char * conf_file = DEFAULT_CONF; char * ifname = NULL; - char * newname = NULL; - int use_probe = 0; - int is_debian = 0; + int use_probe = 0; /* Probe for modules */ + int is_debian = 0; /* Debian quirks (probing) */ + int print_num_match = 0; /* Print/Return num of matches */ int skfd; int ret; /* Loop over all command line options */ while(1) { - int c = getopt_long(argc, argv, "c:dDi:n:ptvV", long_opt, NULL); + int c = getopt_long(argc, argv, "c:CdDi:n:ptuvV", long_opt, NULL); if(c == -1) break; @@ -1974,6 +2643,9 @@ main(int argc, case 'c': conf_file = optarg; break; + case 'C': + print_num_match = 1; + break; case 'd': is_debian = 1; break; @@ -1984,7 +2656,7 @@ main(int argc, ifname = optarg; break; case 'n': - newname = optarg; + new_name = optarg; break; case 'p': use_probe = 1; @@ -1992,6 +2664,9 @@ main(int argc, case 't': force_takeover = 1; break; + case 'u': + udev_output = 1; + break; case 'v': printf("%-8.16s Wireless-Tools version %d\n", "ifrename", WT_VERSION); return(0); @@ -2001,10 +2676,6 @@ main(int argc, } } - /* Read the specified/default config file, or stdin. */ - if(mapping_readfile(conf_file) < 0) - return(-1); - /* Create a channel to the NET kernel. */ if((skfd = iw_sockets_open()) < 0) { @@ -2013,39 +2684,64 @@ main(int argc, } /* Check if interface name was specified with -i. */ - if(ifname) + if(ifname != NULL) { /* Check is target name specified */ - if(newname != NULL) + if(new_name != NULL) { /* User want to simply rename an interface to a specified name */ - ret = process_rename(skfd, ifname, newname); + ret = process_rename(skfd, ifname, new_name); } else { + /* Read the specified/default config file, or stdin. */ + if(mapping_readfile(conf_file) < 0) + return(-1); + /* Rename only this interface based on mappings - * Mostly used for HotPlug processing (from /etc/hotplug/net.agent). + * Mostly used for HotPlug processing (from /etc/hotplug/net.agent) + * or udev processing (from a udev IMPORT rule). * Process the network interface specified on the command line, * and return the new name on stdout. */ + print_newname = 1; ret = process_ifname(skfd, ifname, NULL, 0); } } else { - /* Load all the necesary modules */ - if(use_probe) - { - if(is_debian) - probe_debian(skfd); - else - probe_mappings(skfd); - } + /* Read the specified/default config file, or stdin. */ + if(mapping_readfile(conf_file) < 0) + return(-1); /* Rename all system interfaces * Mostly used for boot time processing (from init scripts). */ - ret = process_iflist(skfd, &newname, 1); + ret = process_iflist(skfd, NULL, 0, use_probe, is_debian); + + /* If we do any takeover, the interface list grabbed with + * iw_enum_devices() may get out of sync with the real interfaces, + * and we may miss the victim interface. So, let's go through the + * list again. + * On the other hand, we may have ping pong between two interfaces, + * each claiming the same name, so let's not do it forever... + * Two time should be enough for most configs... + * Note also that takeover is usually done with eth0, and many time + * we fail to probe eth0 because an unrenamed interface was using it, + * so we redo everything also when probing... + * Jean II */ + if(force_takeover && (num_takeover || use_probe)) + { + /* Play it again, Sam... */ + ret = process_iflist(skfd, NULL, 0, use_probe, is_debian); + } + + /* Print number of mapping that matched */ + if(print_num_match) + { + fprintf(stderr, "Setting : %d mapping matched.\n", num_mapping_match); + ret = num_mapping_match; + } } /* Cleanup */