--- /dev/null
+/*
+ * Wireless Tools
+ *
+ * Jean II - HPL 04
+ *
+ * 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 <jt@hpl.hp.com>
+ */
+
+/*
+ * 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 $
+ * 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.
+ * Difference with standard 'nameif' :
+ * o 'nameif' has only a single selector, the interface MAC address.
+ * o Modular selector architecture, easily add new selectors.
+ * o hotplug invocation support.
+ * o module loading support.
+ * o MAC address wildcard.
+ * o Interface name wildcard ('eth*' or 'wlan*').
+ */
+
+/***************************** INCLUDES *****************************/
+
+/* This is needed to enable GNU extensions such as getline & FNM_CASEFOLD */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <getopt.h> /* getopt_long() */
+#include <linux/sockios.h> /* SIOCSIFNAME */
+#include <fnmatch.h> /* fnmatch() */
+//#include <sys/syslog.h>
+
+#include "iwlib.h" /* Wireless Tools library */
+
+// This would be cool, unfortunately...
+//#include <linux/ethtool.h> /* 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 Wireless Protocol */
+#define SELECT_NUM 12
+
+#define HAS_MAC_EXACT 1
+#define HAS_MAC_FILTER 2
+
+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' },
+ {"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"
+
+/****************************** 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];
+
+ /* 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 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 */
+} if_mapping;
+
+/* 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,
+ 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;
+
+/**************************** PROTOTYPES ****************************/
+
+static int
+ mapping_addmac(struct if_mapping * ifnode,
+ int * active,
+ char * pos,
+ size_t len,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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);
+
+/**************************** 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 },
+ /* 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 */
+
+/* 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;
+
+/******************** 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 (converted from '*' to '%d' in mapping_create()) */
+ p = strstr(pattern, "%d");
+
+ /* 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 += 2;
+
+ /* 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;
+ 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);
+
+ /* 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,
+ int linenum)
+{
+ size_t n;
+
+ /* Verify validity of string */
+ if(len >= sizeof(ifnode->mac_filter))
+ {
+ fprintf(stderr, "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" */
+ if(iw_ether_aton(ifnode->mac_filter, &ifnode->mac) != 1)
+ {
+ fprintf(stderr, "Error: Invalid MAC address `%s' at line %d\n",
+ ifnode->mac_filter, linenum);
+ return(-1);
+ }
+
+ /* Check that it's not NULL */
+ if(!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(memcmp(&ifnode->mac.ether_addr_octet, &target->mac.ether_addr_octet,
+ 6));
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * 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)
+{
+ int ret;
+
+ /* Extract MAC address */
+ ret = iw_get_mac_addr(skfd, ifname, &target->mac, &target->hw_type);
+ if(ret < 0)
+ {
+ fprintf(stderr, "Error: Can't read MAC address on interface `%s' : %s\n",
+ ifname, strerror(errno));
+ return(-1);
+ }
+
+ /* Check the type of comparison */
+ if((flag == HAS_MAC_FILTER) || verbose)
+ {
+ /* Convert to ASCII */
+ iw_ether_ntop(&target->mac, 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,
+ int linenum)
+{
+ size_t n;
+ unsigned int type;
+
+ /* 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,
+ int linenum)
+{
+ /* Plain string, minimal verification */
+ if(len >= sizeof(ifnode->driver))
+ {
+ fprintf(stderr, "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,
+ int linenum)
+{
+#if 0
+ size_t n;
+#endif
+
+ /* 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,
+ int linenum)
+{
+ /* 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,
+ int linenum)
+{
+ size_t n;
+ unsigned int address;
+
+ /* 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,
+ int linenum)
+{
+ size_t n;
+ unsigned int irq;
+
+ /* 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,
+ int linenum)
+{
+ /* 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,
+ int linenum)
+{
+ size_t n;
+
+ /* 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);
+
+ return(target->active[SELECT_PCMCIASLOT] ? 0 : -1);
+}
+
+
+/*********************** 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;
+
+ /* Check overflow. */
+ if(len > 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 */
+ if((!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);
+
+ /* 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);
+
+ /* 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;
+
+ /* 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;
+
+ /* Selector name length */
+ n = strcspn(p, " \t\n");
+
+ /* Find it */
+ selector = selector_find(p, n, linenum);
+ if(!selector)
+ {
+ ret = -1;
+ break;
+ }
+
+ /* Get to selector value */
+ p += n;
+ 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, 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 ether_addr mac; /* Exact MAC address, hex */
+ unsigned short hw_type;
+
+ /* 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.
+ */
+ iw_get_mac_addr(skfd, ifnode->ifname, &mac, &hw_type);
+ }
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * 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 ether_addr mac; /* Exact MAC address, hex */
+ unsigned short hw_type;
+
+ /* 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);
+
+ /* Do it ! */
+ iw_get_mac_addr(skfd, p, &mac, &hw_type);
+
+ /* 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 * pattern)
+{
+ char newname[IFNAMSIZ+1];
+ char retname[IFNAMSIZ+1];
+ int len;
+ char * star;
+
+ len = strlen(pattern);
+ star = strchr(pattern, '*');
+
+ /* Check newname length, need one extra char for wildcard */
+ if((len + (star != NULL)) > IFNAMSIZ)
+ {
+ fprintf(stderr, "Error: Interface name `%s' too long.\n",
+ pattern);
+ 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)
+ {
+ 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);
+
+ /* Find matching mapping */
+ mapping = mapping_find(target);
+ if(mapping == NULL)
+ 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);
+ }
+
+ /* 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(!count)
+ {
+ /* 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 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;
+ char * newname = 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:ptvV", 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':
+ newname = optarg;
+ break;
+ case 'p':
+ use_probe = 1;
+ break;
+ case 't':
+ force_takeover = 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)
+ {
+ /* Check is target name specified */
+ if(newname != NULL)
+ {
+ /* User want to simply rename an interface to a specified name */
+ ret = process_rename(skfd, ifname, newname);
+ }
+ else
+ {
+ /* Rename only this interface based on mappings
+ * Mostly used for HotPlug processing (from /etc/hotplug/net.agent).
+ * Process the network interface specified on the command line,
+ * and return the new name on stdout.
+ */
+ 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, &newname, 1);
+ }
+
+ /* Cleanup */
+ iw_sockets_close(skfd);
+ return(ret);
+}