/*
 *	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 $
 * -------------------------------------------------------
 *
 *	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...
 *	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.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 Pcmcia Slot */
const int SELECT_SYSFS		= 12;	/* Select by sysfs file */
#define SELECT_NUM		13

#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' },
  {"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 we support */
#define SYSFS_MAX_FILE	5

/* 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];

  /* 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 */
} 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);

/**************************** 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 },
  /* 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;

/* 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 (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,
	       struct add_extra *	extra,
	       int			linenum)
{
  size_t	n;

  /* Avoid "Unused parameter" warning */
  extra = extra;

  /* Verify validity of string */
  if(len >= sizeof(ifnode->mac_filter))
    { 
      fprintf(stderr, "MAC address too long at line %d\n", linenum);  
      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 *	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 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)
	{
	  char *	p;
	  size_t	n;

	  /* Get the line starting with sysfs */
	  p = linebuf;
	  while(isspace(*p))
	    ++p; 
	  if(!strncasecmp(p, "sysfs ", 6))
	    {
	      /* Find the mount point */
	      p += 6;
	      while(isspace(*p))
		++p;
	      n = strcspn(p, " \t\n");
	      sdup = strndup(p, 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);
	}
    }

  /* Loop on all sysfs selector */
  for(findex = 0; findex < sysfs_global.filenum; findex++)
    {
      char *	fname;
      int	flen;
      char *	p;
      ssize_t	n;

      /* Construct complete filename for the sysfs selector */
      flen = (sysfs_global.rlen + 11 + strlen(ifname) + 1 +
	      strlen(sysfs_global.filename[findex]) + 1);
      fname = malloc(flen);
      if(fname == NULL)
	{
	  fprintf(stderr, "Error: Can't allocate SYSFS filename\n");  
	  free(linebuf);
	  return(-1);
	}
      sprintf(fname, "%s/class/net/%s/%s", sysfs_global.root, ifname,
	      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 sysfs attribute are void for some interface */
	  if(verbose)
	    fprintf(stderr, "Error: Can't read file `%s'\n", fname);
	  /* Next sysfs selector */
	  continue;
	}

      /* 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);
}


/*********************** 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.
   * 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);

  /* 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; 
  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 *	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);

  /* If user specified a new name, keep only interfaces that would
   * match the new name... */
  if((new_name != NULL) && (if_match_ifname(mapping->ifname, new_name) != 0))
    return(-1);

  /* Check if user want only dry-run.
   * Note that, in the case of wildcard, we don't resolve the wildcard.
   * That would be tricky to do... */
  if(dry_run)
    {
      printf("Dry-run : Would rename %s to %s.\n",
	     target->ifname, mapping->ifname);
      return(0);
    }

  /* 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)
    {
      /* 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;
  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':
	  new_name = 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 != 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).
	   * 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);
} 
