/* * Wireless Tools * * 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) 2007 Jean Tourrilhes */ /* * The changelog for ifrename is in the file CHANGELOG.h ;-) * * This work is a nearly complete rewrite of 'nameif.c'. * Original CopyRight of version of 'nameif' I used is : * ------------------------------------------------------- * Name Interfaces based on MAC address. * Writen 2000 by Andi Kleen. * 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 $ * ------------------------------------------------------- * * 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 *****************************/ /* This is needed to enable GNU extensions such as getline & FNM_CASEFOLD */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include /* getopt_long() */ #include /* SIOCSIFNAME */ #include /* fnmatch() */ //#include #include "iwlib.h" /* Wireless Tools library */ // This would be cool, unfortunately... //#include /* Ethtool stuff -> struct ethtool_drvinfo */ /************************ CONSTANTS & MACROS ************************/ /* Our default configuration file */ const char DEFAULT_CONF[] = "/etc/iftab"; /* Debian stuff */ const char DEBIAN_CONFIG_FILE[] = "/etc/network/interfaces"; /* Backward compatibility */ #ifndef ifr_newname #define ifr_newname ifr_ifru.ifru_slave #endif /* Types of selector we support. Must match selector_list */ const int SELECT_MAC = 0; /* Select by MAC address */ const int SELECT_ETHADDR = 1; /* Select by MAC address */ const int SELECT_ARP = 2; /* Select by ARP type */ const int SELECT_LINKTYPE = 3; /* Select by ARP type */ const int SELECT_DRIVER = 4; /* Select by Driver name */ const int SELECT_BUSINFO = 5; /* Select by Bus-Info */ const int SELECT_FIRMWARE = 6; /* Select by Firmware revision */ 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 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}}; const struct option long_opt[] = { {"config-file", 1, NULL, 'c' }, {"debian", 0, NULL, 'd' }, {"dry-run", 0, NULL, 'D' }, {"help", 0, NULL, '?' }, {"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' }, }; /* Pcmcia stab files */ #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 */ #define ETHTOOL_BUSINFO_LEN 32 /* these strings are set to whatever the driver author decides... */ struct ethtool_drvinfo { __u32 cmd; char driver[32]; /* driver short name, "tulip", "eepro100" */ char version[32]; /* driver version string */ char fw_version[32]; /* firmware version string, if applicable */ char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */ /* For PCI devices, use pci_dev->slot_name. */ char reserved1[32]; char reserved2[16]; __u32 n_stats; /* number of u64's from ETHTOOL_GSTATS */ __u32 testinfo_len; __u32 eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */ __u32 regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */ }; #define ETHTOOL_GDRVINFO 0x00000003 /* Get driver info. */ /* Description of an interface mapping */ typedef struct if_mapping { /* Linked list */ struct if_mapping * next; /* 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 */ 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. */ char fw_version[32]; /* Firmware revision */ unsigned short base_addr; /* HW Base I/O address */ unsigned char irq; /* HW irq line */ char iwproto[IFNAMSIZ + 1]; /* Wireless/protocol name */ int pcmcia_slot; /* Pcmcia slot */ 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. */ typedef int (*mapping_cmp)(struct if_mapping * ifnode, struct if_mapping * target); /* Prototype for extracting selector value from live interface */ typedef int (*mapping_get)(int skfd, const char * ifname, struct if_mapping * target, int flag); /* How to handle a selector */ typedef struct mapping_selector { char * name; mapping_add add_fn; mapping_cmp cmp_fn; 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 mapping_addmac(struct if_mapping * ifnode, int * active, char * pos, size_t len, struct add_extra * extra, int linenum); static int mapping_cmpmac(struct if_mapping * ifnode, struct if_mapping * target); static int mapping_getmac(int skfd, const char * ifname, struct if_mapping * target, int flag); static int mapping_addarp(struct if_mapping * ifnode, int * active, char * pos, size_t len, struct add_extra * extra, int linenum); static int mapping_cmparp(struct if_mapping * ifnode, struct if_mapping * target); static int mapping_getarp(int skfd, const char * ifname, struct if_mapping * target, int flag); static int mapping_adddriver(struct if_mapping * ifnode, int * active, char * pos, size_t len, struct add_extra * extra, int linenum); static int mapping_cmpdriver(struct if_mapping * ifnode, struct if_mapping * target); static int mapping_addbusinfo(struct if_mapping * ifnode, int * active, char * pos, size_t len, struct add_extra * extra, int linenum); static int mapping_cmpbusinfo(struct if_mapping * ifnode, struct if_mapping * target); static int mapping_addfirmware(struct if_mapping * ifnode, int * active, char * pos, size_t len, struct add_extra * extra, int linenum); static int mapping_cmpfirmware(struct if_mapping * ifnode, struct if_mapping * target); static int mapping_getdriverbusinfo(int skfd, const char * ifname, struct if_mapping * target, int flag); static int mapping_addbaseaddr(struct if_mapping * ifnode, int * active, char * pos, size_t len, struct add_extra * extra, int linenum); static int mapping_cmpbaseaddr(struct if_mapping * ifnode, struct if_mapping * target); static int mapping_addirq(struct if_mapping * ifnode, int * active, char * pos, size_t len, struct add_extra * extra, int linenum); static int mapping_cmpirq(struct if_mapping * ifnode, struct if_mapping * target); static int mapping_getbaseaddrirq(int skfd, const char * ifname, struct if_mapping * target, int flag); static int mapping_addiwproto(struct if_mapping * ifnode, int * active, char * pos, size_t len, struct add_extra * extra, int linenum); static int mapping_cmpiwproto(struct if_mapping * ifnode, struct if_mapping * target); static int mapping_getiwproto(int skfd, const char * ifname, struct if_mapping * target, int flag); static int mapping_addpcmciaslot(struct if_mapping * ifnode, int * active, char * pos, size_t len, struct add_extra * extra, int linenum); static int mapping_cmppcmciaslot(struct if_mapping * ifnode, struct if_mapping * target); static int mapping_getpcmciaslot(int skfd, 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 ****************************/ /* List of mapping read for config file */ struct if_mapping * mapping_list = NULL; /* List of selectors we can handle */ const struct mapping_selector selector_list[] = { /* MAC address and ARP/Link type from ifconfig */ { "mac", &mapping_addmac, &mapping_cmpmac, &mapping_getmac }, { "ethaddr", &mapping_addmac, &mapping_cmpmac, &mapping_getmac }, { "arp", &mapping_addarp, &mapping_cmparp, &mapping_getarp }, { "linktype", &mapping_addarp, &mapping_cmparp, &mapping_getarp }, /* Driver name, Bus-Info and firmware rev from ethtool -i */ { "driver", &mapping_adddriver, &mapping_cmpdriver, &mapping_getdriverbusinfo }, { "businfo", &mapping_addbusinfo, &mapping_cmpbusinfo, &mapping_getdriverbusinfo }, { "firmware", &mapping_addfirmware, &mapping_cmpfirmware, &mapping_getdriverbusinfo }, /* Base Address and IRQ from ifconfig */ { "baseaddress", &mapping_addbaseaddr, &mapping_cmpbaseaddr, &mapping_getbaseaddrirq }, { "irq", &mapping_addirq, &mapping_cmpirq, &mapping_getbaseaddrirq }, { "interrupt", &mapping_addirq, &mapping_cmpirq, &mapping_getbaseaddrirq }, /* Wireless Protocol from iwconfig */ { "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 }, }; 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 num_takeover = 0; /* Number of takeover done */ /* 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. */ /*------------------------------------------------------------------*/ /* * Compare two interface names, with wildcards. * We can't use fnmatch() because we don't want expansion of '[...]' * expressions, '\' sequences and matching of '.'. * We only want to match a single '*' (converted to a %d at that point) * to a numerical value (no ascii). * Return 0 is matches. */ static int if_match_ifname(const char * pattern, const char * value) { const char * p; const char * v; int n; int ret; /* Check for a wildcard */ p = strchr(pattern, '*'); /* No wildcard, simple comparison */ if(p == NULL) return(strcmp(pattern, value)); /* Check is prefixes match */ n = (p - pattern); ret = strncmp(pattern, value, n); if(ret) return(ret); /* Check that value has some digits at this point */ v = value + n; if(!isdigit(*v)) return(-1); /* Skip digits to go to value suffix */ do v++; while(isdigit(*v)); /* Pattern suffix */ p += 1; /* Compare suffixes */ return(strcmp(p, v)); } /*------------------------------------------------------------------*/ /* * Steal interface name from another interface. This enable interface * name swapping. * This will work : * 1) with kernel 2.6.X * 2) if other interface is down * Because of (2), it won't work with hotplug, but we don't need it * with hotplug, only with static ifaces... */ static int if_takeover_name(int skfd, const char * victimname) { char autoname[IFNAMSIZ+1]; int len; struct ifreq ifr; int ret; /* Compute name for victim interface */ len = strlen(victimname); memcpy(autoname, victimname, len + 1); if(len > (IFNAMSIZ - 2)) len = IFNAMSIZ - 2; /* Make sure we have at least two char */ len--; /* Convert to index */ while(isdigit(autoname[len])) len--; /* Scrap all trailing digits */ strcpy(autoname + len + 1, "%d"); if(verbose) fprintf(stderr, "Takeover : moving interface `%s' to `%s'.\n", victimname, autoname); /* Prepare request */ bzero(&ifr, sizeof(struct ifreq)); strncpy(ifr.ifr_name, victimname, IFNAMSIZ); strncpy(ifr.ifr_newname, autoname, IFNAMSIZ); /* Rename victim interface */ ret = ioctl(skfd, SIOCSIFNAME, &ifr); if(!ret) num_takeover++; return(ret); } /*------------------------------------------------------------------*/ /* * Ask the kernel to change the name of an interface. * That's what we want to do. All the rest is to make sure we call this * appropriately. */ static int if_set_name(int skfd, const char * oldname, const char * newname, char * retname) { struct ifreq ifr; char * star; int ret; /* The kernel doesn't check is the interface already has the correct * name and may return an error, so check ourselves. * In the case of wildcard, the result can be weird : if oldname='eth0' * and newname='eth*', retname would be 'eth1'. * So, if the oldname value matches the newname pattern, just return * success. */ if(!if_match_ifname(newname, oldname)) { if(verbose) fprintf(stderr, "Setting : Interface `%s' already matches `%s'.\n", oldname, newname); strcpy(retname, oldname); return(0); } /* Prepare request */ bzero(&ifr, sizeof(struct ifreq)); 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); /* Takeover support : grab interface name from another interface */ if(ret && (errno == EEXIST) && force_takeover) { /* Push things around */ ret = if_takeover_name(skfd, newname); if(!ret) /* Second try */ ret = ioctl(skfd, SIOCSIFNAME, &ifr); } if(!ret) { /* Get the real new name (in case newname is a wildcard) */ strcpy(retname, ifr.ifr_newname); if(verbose) fprintf(stderr, "Setting : Interface `%s' renamed to `%s'.\n", oldname, retname); } return(ret); } /************************ SELECTOR HANDLING ************************/ /* * Handle the various selector we support */ /*------------------------------------------------------------------*/ /* * Add a MAC address selector to a mapping */ static int 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, "Error : MAC address too long at line %d\n", linenum); return(-1); } n = strspn(string, "0123456789ABCDEFabcdef:*"); if(n < len) { fprintf(stderr, "Error: Invalid MAC address `%s' at line %d\n", string, linenum); return(-1); } /* Copy as filter in all cases */ memcpy(ifnode->mac_filter, string, len + 1); /* Check the type of MAC address */ if (strchr(ifnode->mac_filter, '*') != NULL) { /* This is a wilcard. Usual format : "01:23:45:*" * Unfortunately, we can't do proper parsing. */ ifnode->active[SELECT_MAC] = HAS_MAC_FILTER; active[SELECT_MAC] = HAS_MAC_FILTER; } else { /* Not a wildcard : "01:23:45:67:89:AB" */ 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); return(-1); } /* Check that it's not NULL */ 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", linenum); } ifnode->active[SELECT_MAC] = HAS_MAC_EXACT; if(active[SELECT_MAC] == 0) active[SELECT_MAC] = HAS_MAC_EXACT; } if(verbose) fprintf(stderr, "Parsing : Added %s MAC address `%s' from line %d.\n", ifnode->active[SELECT_MAC] == HAS_MAC_FILTER ? "filter" : "exact", ifnode->mac_filter, linenum); return(0); } /*------------------------------------------------------------------*/ /* * Compare the mac address of two mappings */ static int mapping_cmpmac(struct if_mapping * ifnode, struct if_mapping * target) { /* Check for wildcard matching */ if(ifnode->active[SELECT_MAC] == HAS_MAC_FILTER) /* Do wildcard matching, case insensitive */ return(fnmatch(ifnode->mac_filter, target->mac_filter, FNM_CASEFOLD)); else /* Exact matching, in hex */ return((ifnode->mac_len != target->mac_len) || memcmp(ifnode->mac, target->mac, ifnode->mac_len)); } /*------------------------------------------------------------------*/ /* * Extract the MAC address and Link Type of an interface */ static int mapping_getmac(int skfd, const char * ifname, struct if_mapping * target, int flag) { struct ifreq ifr; int ret; int i; /* 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", ifname, strerror(errno)); 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_mac_ntop(target->mac, target->mac_len, target->mac_filter, sizeof(target->mac_filter)); } target->active[SELECT_MAC] = flag; target->active[SELECT_ARP] = 1; if(verbose) fprintf(stderr, "Querying %s : Got MAC address `%s' and ARP/Link Type `%d'.\n", ifname, target->mac_filter, target->hw_type); return(0); } /*------------------------------------------------------------------*/ /* * Add a ARP/Link type selector to a mapping */ static int 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)) { fprintf(stderr, "Error: Invalid ARP/Link Type `%s' at line %d\n", string, linenum); return(-1); } ifnode->hw_type = (unsigned short) type; ifnode->active[SELECT_ARP] = 1; active[SELECT_ARP] = 1; if(verbose) fprintf(stderr, "Parsing : Added ARP/Link Type `%d' from line %d.\n", ifnode->hw_type, linenum); return(0); } /*------------------------------------------------------------------*/ /* * Compare the ARP/Link type of two mappings */ static int mapping_cmparp(struct if_mapping * ifnode, struct if_mapping * target) { return(!(ifnode->hw_type == target->hw_type)); } /*------------------------------------------------------------------*/ /* * Extract the ARP/Link type of an interface */ static int mapping_getarp(int skfd, const char * ifname, struct if_mapping * target, int flag) { /* We may have already extracted the MAC address */ if(target->active[SELECT_MAC]) return(0); /* Otherwise just do it */ return(mapping_getmac(skfd, ifname, target, flag)); } /*------------------------------------------------------------------*/ /* * Add a Driver name selector to a mapping */ static int 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, "Error: Driver name too long at line %d\n", linenum); return(-1); } /* Copy */ memcpy(ifnode->driver, string, len + 1); /* Activate */ ifnode->active[SELECT_DRIVER] = 1; active[SELECT_DRIVER] = 1; if(verbose) fprintf(stderr, "Parsing : Added Driver name `%s' from line %d.\n", ifnode->driver, linenum); return(0); } /*------------------------------------------------------------------*/ /* * Compare the Driver name of two mappings */ static int mapping_cmpdriver(struct if_mapping * ifnode, struct if_mapping * target) { /* Do wildcard matching, case insensitive */ return(fnmatch(ifnode->driver, target->driver, FNM_CASEFOLD)); } /*------------------------------------------------------------------*/ /* * Add a Bus-Info selector to a mapping */ static int 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)) { fprintf(stderr, "Bus Info too long at line %d\n", linenum); return(-1); } #if 0 /* Hum... This doesn's seem true for non-PCI bus-info */ n = strspn(string, "0123456789ABCDEFabcdef:.*"); if(n < len) { fprintf(stderr, "Error: Invalid Bus Info `%s' at line %d\n", string, linenum); return(-1); } #endif /* Copy */ memcpy(ifnode->bus_info, string, len + 1); /* Activate */ ifnode->active[SELECT_BUSINFO] = 1; active[SELECT_BUSINFO] = 1; if(verbose) fprintf(stderr, "Parsing : Added Bus Info `%s' from line %d.\n", ifnode->bus_info, linenum); return(0); } /*------------------------------------------------------------------*/ /* * Compare the Bus-Info of two mappings */ static int mapping_cmpbusinfo(struct if_mapping * ifnode, struct if_mapping * target) { /* Do wildcard matching, case insensitive */ return(fnmatch(ifnode->bus_info, target->bus_info, FNM_CASEFOLD)); } /*------------------------------------------------------------------*/ /* * Add a Firmare revision selector to a mapping */ static int 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)) { fprintf(stderr, "Firmware revision too long at line %d\n", linenum); return(-1); } /* Copy */ memcpy(ifnode->fw_version, string, len + 1); /* Activate */ ifnode->active[SELECT_FIRMWARE] = 1; active[SELECT_FIRMWARE] = 1; if(verbose) fprintf(stderr, "Parsing : Added Firmware Revision `%s' from line %d.\n", ifnode->fw_version, linenum); return(0); } /*------------------------------------------------------------------*/ /* * Compare the Bus-Info of two mappings */ static int mapping_cmpfirmware(struct if_mapping * ifnode, struct if_mapping * target) { /* Do wildcard matching, case insensitive */ return(fnmatch(ifnode->fw_version, target->fw_version, FNM_CASEFOLD)); } /*------------------------------------------------------------------*/ /* * Extract the Driver name and Bus-Info from a live interface */ static int mapping_getdriverbusinfo(int skfd, const char * ifname, struct if_mapping * target, int flag) { struct ifreq ifr; struct ethtool_drvinfo drvinfo; int ret; /* Avoid "Unused parameter" warning */ flag = flag; /* We may come here twice or more, so do the job only once */ if(target->active[SELECT_DRIVER] || target->active[SELECT_BUSINFO] || target->active[SELECT_FIRMWARE]) return(0); /* Prepare request */ bzero(&ifr, sizeof(struct ifreq)); bzero(&drvinfo, sizeof(struct ethtool_drvinfo)); strncpy(ifr.ifr_name, ifname, IFNAMSIZ); drvinfo.cmd = ETHTOOL_GDRVINFO; ifr.ifr_data = (caddr_t) &drvinfo; /* Do it */ ret = ioctl(skfd, SIOCETHTOOL, &ifr); if(ret < 0) { /* Most drivers don't support that, keep quiet for now */ if(verbose) fprintf(stderr, "Error: Can't read driver/bus-info on interface `%s' : %s\n", ifname, strerror(errno)); return(-1); } /* Copy over */ strcpy(target->driver, drvinfo.driver); strcpy(target->bus_info, drvinfo.bus_info); strcpy(target->fw_version, drvinfo.fw_version); /* Activate */ target->active[SELECT_DRIVER] = 1; target->active[SELECT_BUSINFO] = 1; target->active[SELECT_FIRMWARE] = 1; if(verbose) fprintf(stderr, "Querying %s : Got Driver name `%s', Bus Info `%s' and Firmware `%s'.\n", ifname, target->driver, target->bus_info, target->fw_version); return(0); } /*------------------------------------------------------------------*/ /* * Add a Base Address selector to a mapping */ static int 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)) { fprintf(stderr, "Error: Invalid Base Address `%s' at line %d\n", string, linenum); return(-1); } /* Copy */ ifnode->base_addr = (unsigned short) address; /* Activate */ ifnode->active[SELECT_BASEADDR] = 1; active[SELECT_BASEADDR] = 1; if(verbose) fprintf(stderr, "Parsing : Added Base Address `0x%X' from line %d.\n", ifnode->base_addr, linenum); return(0); } /*------------------------------------------------------------------*/ /* * Compare the Base Address of two mappings */ static int mapping_cmpbaseaddr(struct if_mapping * ifnode, struct if_mapping * target) { /* Do wildcard matching, case insensitive */ return(!(ifnode->base_addr == target->base_addr)); } /*------------------------------------------------------------------*/ /* * Add a IRQ selector to a mapping */ static int 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)) { fprintf(stderr, "Error: Invalid Base Address `%s' at line %d\n", string, linenum); return(-1); } /* Copy */ ifnode->irq = (unsigned char) irq; /* Activate */ ifnode->active[SELECT_IRQ] = 1; active[SELECT_IRQ] = 1; if(verbose) fprintf(stderr, "Parsing : Added IRQ `%d' from line %d.\n", ifnode->irq, linenum); return(0); } /*------------------------------------------------------------------*/ /* * Compare the IRQ of two mappings */ static int mapping_cmpirq(struct if_mapping * ifnode, struct if_mapping * target) { /* Do wildcard matching, case insensitive */ return(!(ifnode->irq == target->irq)); } /*------------------------------------------------------------------*/ /* * Extract the Driver name and Bus-Info from a live interface */ static int mapping_getbaseaddrirq(int skfd, const char * ifname, struct if_mapping * target, int flag) { struct ifreq ifr; struct ifmap map; /* hardware setup */ int ret; /* Avoid "Unused parameter" warning */ flag = flag; /* We may come here twice, so do the job only once */ if(target->active[SELECT_BASEADDR] || target->active[SELECT_IRQ]) return(0); /* Prepare request */ bzero(&ifr, sizeof(struct ifreq)); bzero(&map, sizeof(struct ifmap)); strncpy(ifr.ifr_name, ifname, IFNAMSIZ); /* Do it */ ret = ioctl(skfd, SIOCGIFMAP, &ifr); if(ret < 0) { /* Don't know if every interface has that, so keep quiet... */ if(verbose) fprintf(stderr, "Error: Can't read base address/irq on interface `%s' : %s\n", ifname, strerror(errno)); return(-1); } /* Copy over, activate */ if(ifr.ifr_map.base_addr >= 0x100) { target->base_addr = ifr.ifr_map.base_addr; target->active[SELECT_BASEADDR] = 1; } target->irq = ifr.ifr_map.irq; target->active[SELECT_IRQ] = 1; if(verbose) fprintf(stderr, "Querying %s : Got Base Address `0x%X' and IRQ `%d'.\n", ifname, target->base_addr, target->irq); return(0); } /*------------------------------------------------------------------*/ /* * Add a Wireless Protocol selector to a mapping */ static int 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)) { fprintf(stderr, "Wireless Protocol too long at line %d\n", linenum); return(-1); } /* Copy */ memcpy(ifnode->iwproto, string, len + 1); /* Activate */ ifnode->active[SELECT_IWPROTO] = 1; active[SELECT_IWPROTO] = 1; if(verbose) fprintf(stderr, "Parsing : Added Wireless Protocol `%s' from line %d.\n", ifnode->iwproto, linenum); return(0); } /*------------------------------------------------------------------*/ /* * Compare the Wireless Protocol of two mappings */ static int mapping_cmpiwproto(struct if_mapping * ifnode, struct if_mapping * target) { /* Do wildcard matching, case insensitive */ return(fnmatch(ifnode->iwproto, target->iwproto, FNM_CASEFOLD)); } /*------------------------------------------------------------------*/ /* * Extract the Wireless Protocol from a live interface */ static int mapping_getiwproto(int skfd, const char * ifname, struct if_mapping * target, int flag) { struct iwreq wrq; /* Avoid "Unused parameter" warning */ flag = flag; /* Get wireless name */ if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0) /* Don't complain about it, Ethernet cards will never support this */ return(-1); strncpy(target->iwproto, wrq.u.name, IFNAMSIZ); target->iwproto[IFNAMSIZ] = '\0'; /* Activate */ target->active[SELECT_IWPROTO] = 1; if(verbose) fprintf(stderr, "Querying %s : Got Wireless Protocol `%s'.\n", ifname, target->iwproto); return(0); } /*------------------------------------------------------------------*/ /* * Add a Pcmcia Slot selector to a mapping */ static int mapping_addpcmciaslot(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, convert to int */ n = strspn(string, "0123456789"); if((n < len) || (sscanf(string, "%d", &ifnode->pcmcia_slot) != 1)) { fprintf(stderr, "Error: Invalid Pcmcia Slot `%s' at line %d\n", string, linenum); return(-1); } ifnode->active[SELECT_PCMCIASLOT] = 1; active[SELECT_PCMCIASLOT] = 1; if(verbose) fprintf(stderr, "Parsing : Added Pcmcia Slot `%d' from line %d.\n", ifnode->pcmcia_slot, linenum); return(0); } /*------------------------------------------------------------------*/ /* * Compare the Pcmcia Slot of two mappings */ static int mapping_cmppcmciaslot(struct if_mapping * ifnode, struct if_mapping * target) { return(!(ifnode->pcmcia_slot == target->pcmcia_slot)); } /*------------------------------------------------------------------*/ /* * Extract the Pcmcia Slot of an interface * Note that this works only for cards fully managed by cardmgr. * With the kernel pcmcia modules, 32 bits cards (CardBus) are not managed * by cardmgr, and therefore won't have a valid slot number. For those * cards, you should use Bus Info (when the driver exports it). * In the long term, 16 bits card as well will no longer be managed by * cardmgr. Currently, Bus Info for 16 bit cards don't have any information * enabling to locate their physical location on the system, but I hope that * this will change. * When that happen, we can drop this code... */ static int mapping_getpcmciaslot(int skfd, const char * ifname, struct if_mapping * target, int flag) { FILE * stream; char * linebuf = NULL; size_t linelen = 0; int linenum = 0; /* Avoid "Unused parameter" warning */ skfd = skfd; flag = flag; /* Open the stab file for reading */ stream = fopen(PCMCIA_STAB1, "r"); if(!stream) { /* Try again, alternate location */ stream = fopen(PCMCIA_STAB2, "r"); if(!stream) { fprintf(stderr, "Error: Can't open PCMCIA Stab file `%s' or `%s': %s\n", PCMCIA_STAB1, PCMCIA_STAB2, 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) { char * p; size_t n; size_t k; int pcmcia_slot; int i; /* Keep track of line number */ linenum++; /* Get Pcmcia socket number */ p = linebuf; while(isspace(*p)) ++p; if(*p == '\0') continue; /* Line ended */ n = strcspn(p, " \t\n"); k = strspn(p, "0123456789"); if((k < n) || (sscanf(p, "%d", &pcmcia_slot) != 1)) /* Next line */ continue; /* Skip socket number */ /* Skip socket number ; device class ; driver name ; instance */ for(i = 0; i < 4; i++) { /* Skip item */ p += n; /* Skip space */ p += strspn(p, " \t\n"); if(*p == '\0') break; /* Line ended */ /* Next item size */ n = strcspn(p, " \t\n"); } if(*p == '\0') continue; /* Line ended */ /* Terminate dev name */ p[n] = '\0'; /* Compare to interface name */ if(!strcmp(p, ifname)) { /* Save */ target->pcmcia_slot = pcmcia_slot; /* Activate */ target->active[SELECT_PCMCIASLOT] = 1; if(verbose) fprintf(stderr, "Querying %s : Got Pcmcia Slot `%d'.\n", ifname, target->pcmcia_slot); /* Exit loop, found it */ break; } /* Finished -> next line */ } /* 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 ***********************/ /* * Manage interface mappings. * Each mapping tell us how to identify a specific interface name. * It is composed of a bunch of selector values. */ /*------------------------------------------------------------------*/ /* * Create a new interface mapping and verify its name */ static struct if_mapping * mapping_create(char * pos, int len, int linenum) { struct if_mapping * ifnode; char * star; 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); return(NULL); } /* Create mapping, zero it */ ifnode = calloc(1, sizeof(if_mapping)); if(!ifnode) { fprintf(stderr, "Error: Can't allocate interface mapping.\n"); return(NULL); } /* Set the name, terminates it */ memcpy(ifnode->ifname, pos, len); ifnode->ifname[len] = '\0'; /* 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); if(strchr(ifnode->ifname, ':')) fprintf(stderr, "Warning: Alias device `%s' at line %d probably can't be mapped.\n", ifnode->ifname, linenum); if(verbose) fprintf(stderr, "Parsing : Added Mapping `%s' from line %d.\n", ifnode->ifname, linenum); /* Done */ return(ifnode); } /*------------------------------------------------------------------*/ /* * Find the most appropriate selector matching a given selector name */ static inline const struct mapping_selector * selector_find(const char * string, size_t slen, int linenum) { const struct mapping_selector * found = NULL; int ambig = 0; int i; /* Go through all selectors */ for(i = 0; selector_list[i].name != NULL; ++i) { /* No match -> next one */ if(strncasecmp(selector_list[i].name, string, slen) != 0) continue; /* Exact match -> perfect */ if(slen == strlen(selector_list[i].name)) return &selector_list[i]; /* Partial match */ if(found == NULL) /* First time */ found = &selector_list[i]; else /* Another time */ if (selector_list[i].add_fn != found->add_fn) ambig = 1; } if(found == NULL) { fprintf(stderr, "Error: Unknown selector `%.*s' at line %d.\n", (int) slen, string, linenum); return NULL; } if(ambig) { fprintf(stderr, "Selector `%.*s'at line %d is ambiguous.\n", (int) slen, string, linenum); return NULL; } return found; } /*------------------------------------------------------------------*/ /* * Read the configuration file and extract all valid mappings and their * selectors. */ static int mapping_readfile(const char * filename) { 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)); /* Check filename */ if(!strcmp(filename, "-")) { /* Read from stdin */ stream = stdin; } else { /* Open the file for reading */ stream = fopen(filename, "r"); if(!stream) { fprintf(stderr, "Error: Can't open configuration file `%s': %s\n", filename, 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) { struct if_mapping * ifnode; char * p; char * e; size_t n; int ret = -13; /* Complain if no selectors */ /* Keep track of line number */ linenum++; /* Every comments terminates parsing */ if((p = strchr(linebuf,'#')) != NULL) *p = '\0'; /* Get interface name */ p = linebuf; while(isspace(*p)) ++p; if(*p == '\0') continue; /* Line ended */ n = strcspn(p, " \t\n"); /* Create mapping */ ifnode = mapping_create(p, n, linenum); if(!ifnode) continue; /* Ignore this line */ p += n; p += strspn(p, " \t\n"); /* Loop on all selectors */ while(*p != '\0') { const struct mapping_selector * selector = NULL; struct add_extra * extra = NULL; /* Selector name length - stop at modifier start */ n = strcspn(p, " \t\n{"); /* Find it */ selector = selector_find(p, n, linenum); if(!selector) { 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 += strspn(p, " \t\n"); if(*p == '\0') { fprintf(stderr, "Error: no value for selector `%s' on line %d\n", selector->name, linenum); ret = -1; break; /* Line ended */ } /* Check for quoted arguments */ if(*p == '"') { p++; e = strchr(p, '"'); if(e == NULL) { fprintf(stderr, "Error: unterminated quoted value on line %d\n", linenum); ret = -1; break; /* Line ended */ } n = e - p; e++; } else { /* Just end at next blank */ n = strcspn(p, " \t\n"); e = p + n; } /* Make 'e' point past the '\0' we are going to add */ if(*e != '\0') e++; /* Terminate selector value */ p[n] = '\0'; /* Add it to the mapping */ ret = selector->add_fn(ifnode, selector_active, p, n, extra, linenum); if(ret < 0) break; /* Go to next selector */ p = e; p += strspn(p, " \t\n"); } /* We add a mapping only if it has at least one selector and if all * selectors were parsed properly. */ if(ret < 0) { /* If we have not yet printed an error, now is a good time ;-) */ if(ret == -13) fprintf(stderr, "Error: Line %d ignored, no valid selectors\n", linenum); else fprintf(stderr, "Error: Line %d ignored due to prior errors\n", linenum); free(ifnode); } else { /* Link it in the list */ ifnode->next = mapping_list; mapping_list = ifnode; } } /* Cleanup */ free(linebuf); /* Finished reading, close the file */ if(stream != stdin) fclose(stream); return(0); } /*------------------------------------------------------------------*/ /* * Extract all the interesting selectors for the interface in consideration */ static struct if_mapping * mapping_extract(int skfd, const char * ifname) { struct if_mapping * target; int i; /* Create mapping, zero it */ target = calloc(1, sizeof(if_mapping)); if(!target) { fprintf(stderr, "Error: Can't allocate interface mapping.\n"); return(NULL); } /* Set the interface name */ strcpy(target->ifname, ifname); /* Loop on all active selectors */ for(i = 0; i < SELECT_NUM; i++) { /* Check if this selector is active */ if(selector_active[i] != 0) { /* Extract selector */ selector_list[i].get_fn(skfd, ifname, target, selector_active[i]); /* Ignore errors. Some mapping may not need all selectors */ } } return(target); } /*------------------------------------------------------------------*/ /* * Find the first mapping in the list matching the one we want. */ static struct if_mapping * mapping_find(struct if_mapping * target) { struct if_mapping * ifnode; int i; /* Look over all our mappings */ for(ifnode = mapping_list; ifnode != NULL; ifnode = ifnode->next) { int matches = 1; /* Look over all our selectors, all must match */ for(i = 0; i < SELECT_NUM; i++) { /* Check if this selector is active */ if(ifnode->active[i] != 0) { /* If this selector doesn't match, game over for this mapping */ if((target->active[i] == 0) || (selector_list[i].cmp_fn(ifnode, target) != 0)) { matches = 0; break; } } } /* Check is this mapping was "the one" */ if(matches) return(ifnode); } /* Not found */ return(NULL); } /************************** MODULE SUPPORT **************************/ /* * Load all necessary module so that interfaces do exist. * This is necessary for system that are fully modular when * doing the boot time processing, because we need to run before * 'ifup -a'. */ /*------------------------------------------------------------------*/ /* * Probe interfaces based on our list of mappings. * This is the default, but usually not the best way to do it. */ static void probe_mappings(int skfd) { struct if_mapping * ifnode; 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) continue; if(verbose) fprintf(stderr, "Probing : Trying to load interface [%s]\n", ifnode->ifname); /* Trick the kernel into loading the interface. * This allow us to not depend on the exact path and * name of the '/sbin/modprobe' command. * Obviously, we expect this command to 'fail', as * the interface will load with the old/wrong name. */ strncpy(ifr.ifr_name, ifnode->ifname, IFNAMSIZ); ioctl(skfd, SIOCGIFHWADDR, &ifr); } } /*------------------------------------------------------------------*/ /* * Probe interfaces based on Debian's config files. * This allow to enly load modules for interfaces the user want active, * all built-in interfaces that should remain unconfigured won't * be probed (and can have mappings). */ static void probe_debian(int skfd) { FILE * stream; char * linebuf = NULL; size_t linelen = 0; struct ifreq ifr; /* Open Debian config file */ stream = fopen(DEBIAN_CONFIG_FILE, "r"); if(stream == NULL) { fprintf(stderr, "Error: can't open file [%s]\n", DEBIAN_CONFIG_FILE); return; } /* 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) { char * p; char * e; size_t n; /* Check for auto keyword, ignore when commented out */ if(!strncasecmp(linebuf, "auto ", 5)) { /* Skip "auto" keyword */ p = linebuf + 5; /* Terminate at first comment */ e = strchr(p, '#'); if(e != NULL) *e = '\0'; /* Loop on all interfaces given */ while(*p != '\0') { /* Interface name length */ n = strcspn(p, " \t\n"); /* Look for end of interface name */ e = p + n; /* Make 'e' point past the '\0' we are going to add */ if(*e != '\0') e++; /* Terminate interface name */ p[n] = '\0'; if(verbose) fprintf(stderr, "Probing : Trying to load interface [%s]\n", p); /* Load interface */ strncpy(ifr.ifr_name, p, IFNAMSIZ); ioctl(skfd, SIOCGIFHWADDR, &ifr); /* Go to next interface name */ p = e; p += strspn(p, " \t\n"); } } } /* Done */ fclose(stream); return; } /**************************** MAIN LOGIC ****************************/ /*------------------------------------------------------------------*/ /* * Rename an interface to a specified new name. */ static int process_rename(int skfd, char * ifname, char * newname) { char retname[IFNAMSIZ+1]; int len; char * star; 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", newname); return(-1); } /* Change the name of the interface */ if(if_set_name(skfd, ifname, newname, retname) < 0) { fprintf(stderr, "Error: cannot change name of %s to %s: %s\n", ifname, newname, strerror(errno)); return(-1); } /* 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); /* Done */ return(0); } /*------------------------------------------------------------------*/ /* * Process a specified interface. */ static int process_ifname(int skfd, char * ifname, char * args[], int count) { struct if_mapping * target; const struct if_mapping * mapping; char retname[IFNAMSIZ+1]; /* Avoid "Unused parameter" warning */ args = args; count = count; /* Get description of this interface */ target = mapping_extract(skfd, ifname); 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) { strcpy(retname, mapping->ifname); fprintf(stderr, "Dry-run : Would rename %s to %s.\n", target->ifname, mapping->ifname); } else { /* 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); } } /* Check if called with an explicit interface name */ if(print_newname) { 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 */ return(0); } /*------------------------------------------------------------------*/ /* * Process all network interface present on the system. */ static inline int process_iflist(int skfd, char * args[], int count) { num_takeover = 0; /* 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); } /******************************* MAIN *******************************/ /*------------------------------------------------------------------*/ /* */ static void usage(void) { fprintf(stderr, "usage: ifrename [-c configurationfile] [-i ifname] [-p] [-t] [-d] [-D]\n"); exit(1); } /*------------------------------------------------------------------*/ /* * The main ! */ int main(int argc, char * argv[]) { const char * conf_file = DEFAULT_CONF; char * ifname = NULL; int use_probe = 0; int is_debian = 0; int skfd; int ret; /* Loop over all command line options */ while(1) { int c = getopt_long(argc, argv, "c:dDi:n:ptuvV", long_opt, NULL); if(c == -1) break; switch(c) { default: case '?': usage(); case 'c': conf_file = optarg; break; case 'd': is_debian = 1; break; case 'D': dry_run = 1; break; case 'i': ifname = optarg; break; case 'n': new_name = optarg; break; case 'p': use_probe = 1; break; 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); case 'V': verbose = 1; break; } } /* 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) { perror("socket"); return(-1); } /* Check if interface name was specified with -i. */ if(ifname != NULL) { /* Check is target name specified */ if(new_name != NULL) { /* User want to simply rename an interface to a specified name */ ret = process_rename(skfd, ifname, new_name); } else { /* Rename only this interface based on mappings * 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); } /* Rename all system interfaces * Mostly used for boot time processing (from init scripts). */ ret = process_iflist(skfd, NULL, 0); } /* Cleanup */ iw_sockets_close(skfd); return(ret); }