1
0
This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
TP-Link_Archer-XR500v/EN7526G_3.18Kernel_SDK/apps/public/wireless_tools.29/ifrename.c
2024-07-22 01:58:46 -03:00

2722 lines
69 KiB
C
Executable File

/*
* Wireless Tools
*
* Jean II - HPL 04 -> 07
*
* Main code for "ifrename". This is tool allows to rename network
* interfaces based on various criteria (not only wireless).
* You need to link this code against "iwlib.c" and "-lm".
*
* This file is released under the GPL license.
* Copyright (c) 2007 Jean Tourrilhes <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 and sysfs symlinks...
* o hotplug invocation support.
* o module loading support.
* o MAC address wildcard.
* o Interface name wildcard ('eth*' or 'wlan*').
* o Non-Ethernet MAC addresses (any size, not just 48 bits)
*/
/***************************** INCLUDES *****************************/
/* This is needed to enable GNU extensions such as getline & FNM_CASEFOLD */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <getopt.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 */
const int SELECT_PREVNAME = 13; /* Select by previous interface name */
#define SELECT_NUM 14
#define HAS_MAC_EXACT 1
#define HAS_MAC_FILTER 2
#define MAX_MAC_LEN 16 /* Maximum lenght of MAC address */
const struct ether_addr zero_mac = {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
const struct option long_opt[] =
{
{"config-file", 1, NULL, 'c' },
{"debian", 0, NULL, 'd' },
{"dry-run", 0, NULL, 'D' },
{"help", 0, NULL, '?' },
{"interface", 1, NULL, 'i' },
{"newname", 1, NULL, 'n' },
{"takeover", 0, NULL, 't' },
{"udev", 0, NULL, 'u' },
{"version", 0, NULL, 'v' },
{"verbose", 0, NULL, 'V' },
{NULL, 0, NULL, '\0' },
};
/* Pcmcia stab files */
#define PCMCIA_STAB1 "/var/lib/pcmcia/stab"
#define PCMCIA_STAB2 "/var/run/stab"
/* Max number of sysfs file types we support */
#define SYSFS_MAX_FILE 8
/* Userspace headers lag, fix that... */
#ifndef ARPHRD_IEEE1394
#define ARPHRD_IEEE1394 24
#endif
#ifndef ARPHRD_EUI64
#define ARPHRD_EUI64 27
#endif
#ifndef ARPHRD_IRDA
#define ARPHRD_IRDA 783
#endif
/* Length of various non-standard MAC addresses */
const int weird_mac_len[][2] =
{
{ ARPHRD_IEEE1394, 8 },
{ ARPHRD_EUI64, 8 },
{ ARPHRD_IRDA, 4 },
};
const int weird_mac_len_num = sizeof(weird_mac_len) / sizeof(weird_mac_len[0]);
/****************************** TYPES ******************************/
/* Cut'n'paste from ethtool.h */
#define ETHTOOL_BUSINFO_LEN 32
/* these strings are set to whatever the driver author decides... */
struct ethtool_drvinfo {
__u32 cmd;
char driver[32]; /* driver short name, "tulip", "eepro100" */
char version[32]; /* driver version string */
char fw_version[32]; /* firmware version string, if applicable */
char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */
/* For PCI devices, use pci_dev->slot_name. */
char reserved1[32];
char reserved2[16];
__u32 n_stats; /* number of u64's from ETHTOOL_GSTATS */
__u32 testinfo_len;
__u32 eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */
__u32 regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */
};
#define ETHTOOL_GDRVINFO 0x00000003 /* Get driver info. */
/* Description of an interface mapping */
typedef struct if_mapping
{
/* Linked list */
struct if_mapping * next;
/* Name of this interface */
char ifname[IFNAMSIZ+1];
char * sysfs_devpath;
int sysfs_devplen;
/* Selectors for this interface */
int active[SELECT_NUM]; /* Selectors active */
/* Selector data */
unsigned char mac[MAX_MAC_LEN]; /* Exact MAC address, hex */
int mac_len; /* Length (usually 6) */
char mac_filter[16*3 + 1]; /* WildCard, ascii */
unsigned short hw_type; /* Link/ARP type */
char driver[32]; /* driver short name */
char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */
char fw_version[32]; /* Firmware revision */
unsigned short base_addr; /* HW Base I/O address */
unsigned char irq; /* HW irq line */
char iwproto[IFNAMSIZ + 1]; /* Wireless/protocol name */
int pcmcia_slot; /* Pcmcia slot */
char * sysfs[SYSFS_MAX_FILE]; /* sysfs selectors */
char prevname[IFNAMSIZ+1]; /* previous interface name */
} if_mapping;
/* Extra parsing information when adding a mapping */
typedef struct add_extra
{
char * modif_pos; /* Descriptor modifier */
size_t modif_len;
} parsing_extra;
/* Prototype for adding a selector to a mapping. Return -1 if invalid value. */
typedef int (*mapping_add)(struct if_mapping * ifnode,
int * active,
char * pos,
size_t len,
struct add_extra * extra,
int linenum);
/* Prototype for comparing the selector of two mapping. Return 0 if matches. */
typedef int (*mapping_cmp)(struct if_mapping * ifnode,
struct if_mapping * target);
/* Prototype for extracting selector value from live interface */
typedef int (*mapping_get)(int skfd,
const char * ifname,
struct if_mapping * target,
int flag);
/* How to handle a selector */
typedef struct mapping_selector
{
char * name;
mapping_add add_fn;
mapping_cmp cmp_fn;
mapping_get get_fn;
} mapping_selector;
/* sysfs global data */
typedef struct sysfs_metadata
{
char * root; /* Root of the sysfs */
int rlen; /* Size of it */
int filenum; /* Number of files */
char * filename[SYSFS_MAX_FILE]; /* Name of files */
} sysfs_metadata;
/**************************** PROTOTYPES ****************************/
static int
mapping_addmac(struct if_mapping * ifnode,
int * active,
char * pos,
size_t len,
struct add_extra * extra,
int linenum);
static int
mapping_cmpmac(struct if_mapping * ifnode,
struct if_mapping * target);
static int
mapping_getmac(int skfd,
const char * ifname,
struct if_mapping * target,
int flag);
static int
mapping_addarp(struct if_mapping * ifnode,
int * active,
char * pos,
size_t len,
struct add_extra * extra,
int linenum);
static int
mapping_cmparp(struct if_mapping * ifnode,
struct if_mapping * target);
static int
mapping_getarp(int skfd,
const char * ifname,
struct if_mapping * target,
int flag);
static int
mapping_adddriver(struct if_mapping * ifnode,
int * active,
char * pos,
size_t len,
struct add_extra * extra,
int linenum);
static int
mapping_cmpdriver(struct if_mapping * ifnode,
struct if_mapping * target);
static int
mapping_addbusinfo(struct if_mapping * ifnode,
int * active,
char * pos,
size_t len,
struct add_extra * extra,
int linenum);
static int
mapping_cmpbusinfo(struct if_mapping * ifnode,
struct if_mapping * target);
static int
mapping_addfirmware(struct if_mapping * ifnode,
int * active,
char * pos,
size_t len,
struct add_extra * extra,
int linenum);
static int
mapping_cmpfirmware(struct if_mapping * ifnode,
struct if_mapping * target);
static int
mapping_getdriverbusinfo(int skfd,
const char * ifname,
struct if_mapping * target,
int flag);
static int
mapping_addbaseaddr(struct if_mapping * ifnode,
int * active,
char * pos,
size_t len,
struct add_extra * extra,
int linenum);
static int
mapping_cmpbaseaddr(struct if_mapping * ifnode,
struct if_mapping * target);
static int
mapping_addirq(struct if_mapping * ifnode,
int * active,
char * pos,
size_t len,
struct add_extra * extra,
int linenum);
static int
mapping_cmpirq(struct if_mapping * ifnode,
struct if_mapping * target);
static int
mapping_getbaseaddrirq(int skfd,
const char * ifname,
struct if_mapping * target,
int flag);
static int
mapping_addiwproto(struct if_mapping * ifnode,
int * active,
char * pos,
size_t len,
struct add_extra * extra,
int linenum);
static int
mapping_cmpiwproto(struct if_mapping * ifnode,
struct if_mapping * target);
static int
mapping_getiwproto(int skfd,
const char * ifname,
struct if_mapping * target,
int flag);
static int
mapping_addpcmciaslot(struct if_mapping * ifnode,
int * active,
char * pos,
size_t len,
struct add_extra * extra,
int linenum);
static int
mapping_cmppcmciaslot(struct if_mapping * ifnode,
struct if_mapping * target);
static int
mapping_getpcmciaslot(int skfd,
const char * ifname,
struct if_mapping * target,
int flag);
static int
mapping_addsysfs(struct if_mapping * ifnode,
int * active,
char * pos,
size_t len,
struct add_extra * extra,
int linenum);
static int
mapping_cmpsysfs(struct if_mapping * ifnode,
struct if_mapping * target);
static int
mapping_getsysfs(int skfd,
const char * ifname,
struct if_mapping * target,
int flag);
static int
mapping_addprevname(struct if_mapping * ifnode,
int * active,
char * pos,
size_t len,
struct add_extra * extra,
int linenum);
static int
mapping_cmpprevname(struct if_mapping * ifnode,
struct if_mapping * target);
static int
mapping_getprevname(int skfd,
const char * ifname,
struct if_mapping * target,
int flag);
/**************************** VARIABLES ****************************/
/* List of mapping read for config file */
struct if_mapping * mapping_list = NULL;
/* List of selectors we can handle */
const struct mapping_selector selector_list[] =
{
/* MAC address and ARP/Link type from ifconfig */
{ "mac", &mapping_addmac, &mapping_cmpmac, &mapping_getmac },
{ "ethaddr", &mapping_addmac, &mapping_cmpmac, &mapping_getmac },
{ "arp", &mapping_addarp, &mapping_cmparp, &mapping_getarp },
{ "linktype", &mapping_addarp, &mapping_cmparp, &mapping_getarp },
/* Driver name, Bus-Info and firmware rev from ethtool -i */
{ "driver", &mapping_adddriver, &mapping_cmpdriver,
&mapping_getdriverbusinfo },
{ "businfo", &mapping_addbusinfo, &mapping_cmpbusinfo,
&mapping_getdriverbusinfo },
{ "firmware", &mapping_addfirmware, &mapping_cmpfirmware,
&mapping_getdriverbusinfo },
/* Base Address and IRQ from ifconfig */
{ "baseaddress", &mapping_addbaseaddr, &mapping_cmpbaseaddr,
&mapping_getbaseaddrirq },
{ "irq", &mapping_addirq, &mapping_cmpirq, &mapping_getbaseaddrirq },
{ "interrupt", &mapping_addirq, &mapping_cmpirq, &mapping_getbaseaddrirq },
/* Wireless Protocol from iwconfig */
{ "iwproto", &mapping_addiwproto, &mapping_cmpiwproto, &mapping_getiwproto },
/* Pcmcia slot from cardmgr */
{ "pcmciaslot", &mapping_addpcmciaslot, &mapping_cmppcmciaslot, &mapping_getpcmciaslot },
/* sysfs file (udev emulation) */
{ "sysfs", &mapping_addsysfs, &mapping_cmpsysfs, &mapping_getsysfs },
/* previous interface name */
{ "prevname", &mapping_addprevname, &mapping_cmpprevname, &mapping_getprevname },
/* The Terminator */
{ NULL, NULL, NULL, NULL },
};
const int selector_num = sizeof(selector_list)/sizeof(selector_list[0]);
/* List of active selectors */
int selector_active[SELECT_NUM]; /* Selectors active */
/*
* All the following flags are controlled by the command line switches...
* It's a bit hackish to have them all as global, so maybe we should pass
* them in a big struct as function arguments... More complex and
* probably not worth it ?
*/
/* Invocation type */
int print_newname = 0;
char * new_name = NULL;
/* Takeover support */
int force_takeover = 0; /* Takeover name from other interface */
int num_takeover = 0; /* Number of takeover done */
/* Dry-run support */
int dry_run = 0; /* Just print new name, don't rename */
/* Verbose support (i.e. debugging) */
int verbose = 0;
/* udev output support (print new DEVPATH) */
int udev_output = 0;
/* sysfs global data */
struct sysfs_metadata sysfs_global =
{
NULL, 0,
0, { NULL, NULL, NULL, NULL, NULL },
};
/******************** INTERFACE NAME MANAGEMENT ********************/
/*
* Bunch of low level function for managing interface names.
*/
/*------------------------------------------------------------------*/
/*
* Compare two interface names, with wildcards.
* We can't use fnmatch() because we don't want expansion of '[...]'
* expressions, '\' sequences and matching of '.'.
* We only want to match a single '*' (converted to a %d at that point)
* to a numerical value (no ascii).
* Return 0 is matches.
*/
static int
if_match_ifname(const char * pattern,
const char * value)
{
const char * p;
const char * v;
int n;
int ret;
/* Check for a wildcard */
p = strchr(pattern, '*');
/* No wildcard, simple comparison */
if(p == NULL)
return(strcmp(pattern, value));
/* Check is prefixes match */
n = (p - pattern);
ret = strncmp(pattern, value, n);
if(ret)
return(ret);
/* Check that value has some digits at this point */
v = value + n;
if(!isdigit(*v))
return(-1);
/* Skip digits to go to value suffix */
do
v++;
while(isdigit(*v));
/* Pattern suffix */
p += 1;
/* Compare suffixes */
return(strcmp(p, v));
}
/*------------------------------------------------------------------*/
/*
* Steal interface name from another interface. This enable interface
* name swapping.
* This will work :
* 1) with kernel 2.6.X
* 2) if other interface is down
* Because of (2), it won't work with hotplug, but we don't need it
* with hotplug, only with static ifaces...
*/
static int
if_takeover_name(int skfd,
const char * victimname)
{
char autoname[IFNAMSIZ+1];
int len;
struct ifreq ifr;
int ret;
/* Compute name for victim interface */
len = strlen(victimname);
memcpy(autoname, victimname, len + 1);
if(len > (IFNAMSIZ - 2))
len = IFNAMSIZ - 2; /* Make sure we have at least two char */
len--; /* Convert to index */
while(isdigit(autoname[len]))
len--; /* Scrap all trailing digits */
strcpy(autoname + len + 1, "%d");
if(verbose)
fprintf(stderr, "Takeover : moving interface `%s' to `%s'.\n",
victimname, autoname);
/* Prepare request */
bzero(&ifr, sizeof(struct ifreq));
strncpy(ifr.ifr_name, victimname, IFNAMSIZ);
strncpy(ifr.ifr_newname, autoname, IFNAMSIZ);
/* Rename victim interface */
ret = ioctl(skfd, SIOCSIFNAME, &ifr);
if(!ret)
num_takeover++;
return(ret);
}
/*------------------------------------------------------------------*/
/*
* Ask the kernel to change the name of an interface.
* That's what we want to do. All the rest is to make sure we call this
* appropriately.
*/
static int
if_set_name(int skfd,
const char * oldname,
const char * newname,
char * retname)
{
struct ifreq ifr;
char * star;
int ret;
/* The kernel doesn't check is the interface already has the correct
* name and may return an error, so check ourselves.
* In the case of wildcard, the result can be weird : if oldname='eth0'
* and newname='eth*', retname would be 'eth1'.
* So, if the oldname value matches the newname pattern, just return
* success. */
if(!if_match_ifname(newname, oldname))
{
if(verbose)
fprintf(stderr, "Setting : Interface `%s' already matches `%s'.\n",
oldname, newname);
strcpy(retname, oldname);
return(0);
}
/* Prepare request */
bzero(&ifr, sizeof(struct ifreq));
strncpy(ifr.ifr_name, oldname, IFNAMSIZ);
strncpy(ifr.ifr_newname, newname, IFNAMSIZ);
/* Check for wildcard interface name, such as 'eth*' or 'wlan*'...
* This require specific kernel support (2.6.2-rc1 and later).
* We externally use '*', but the kernel doesn't know about that,
* so convert it to something it knows about... */
star = strchr(newname, '*');
if(star != NULL)
{
int slen = star - newname;
/* Replace '*' with '%d' in the new buffer */
star = ifr.ifr_newname + slen;
/* Size was checked in process_rename() and mapping_create() */
memmove(star + 2, star + 1, IFNAMSIZ - slen - 2);
star[0] = '%';
star[1] = 'd';
}
/* Do it */
ret = ioctl(skfd, SIOCSIFNAME, &ifr);
/* Takeover support : grab interface name from another interface */
if(ret && (errno == EEXIST) && force_takeover)
{
/* Push things around */
ret = if_takeover_name(skfd, newname);
if(!ret)
/* Second try */
ret = ioctl(skfd, SIOCSIFNAME, &ifr);
}
if(!ret)
{
/* Get the real new name (in case newname is a wildcard) */
strcpy(retname, ifr.ifr_newname);
if(verbose)
fprintf(stderr, "Setting : Interface `%s' renamed to `%s'.\n",
oldname, retname);
}
return(ret);
}
/************************ SELECTOR HANDLING ************************/
/*
* Handle the various selector we support
*/
/*------------------------------------------------------------------*/
/*
* Add a MAC address selector to a mapping
*/
static int
mapping_addmac(struct if_mapping * ifnode,
int * active,
char * string,
size_t len,
struct add_extra * extra,
int linenum)
{
size_t n;
/* Avoid "Unused parameter" warning */
extra = extra;
/* Verify validity of string */
if(len >= sizeof(ifnode->mac_filter))
{
fprintf(stderr, "Error : MAC address too long at line %d\n", linenum);
return(-1);
}
n = strspn(string, "0123456789ABCDEFabcdef:*");
if(n < len)
{
fprintf(stderr, "Error: Invalid MAC address `%s' at line %d\n",
string, linenum);
return(-1);
}
/* Copy as filter in all cases */
memcpy(ifnode->mac_filter, string, len + 1);
/* Check the type of MAC address */
if (strchr(ifnode->mac_filter, '*') != NULL)
{
/* This is a wilcard. Usual format : "01:23:45:*"
* Unfortunately, we can't do proper parsing. */
ifnode->active[SELECT_MAC] = HAS_MAC_FILTER;
active[SELECT_MAC] = HAS_MAC_FILTER;
}
else
{
/* Not a wildcard : "01:23:45:67:89:AB" */
ifnode->mac_len = iw_mac_aton(ifnode->mac_filter,
ifnode->mac, MAX_MAC_LEN);
if(ifnode->mac_len == 0)
{
fprintf(stderr, "Error: Invalid MAC address `%s' at line %d\n",
ifnode->mac_filter, linenum);
return(-1);
}
/* Check that it's not NULL */
if((ifnode->mac_len == 6) && (!memcmp(&ifnode->mac, &zero_mac, 6)))
{
fprintf(stderr,
"Warning: MAC address is null at line %d, this is dangerous...\n",
linenum);
}
ifnode->active[SELECT_MAC] = HAS_MAC_EXACT;
if(active[SELECT_MAC] == 0)
active[SELECT_MAC] = HAS_MAC_EXACT;
}
if(verbose)
fprintf(stderr,
"Parsing : Added %s MAC address `%s' from line %d.\n",
ifnode->active[SELECT_MAC] == HAS_MAC_FILTER ? "filter" : "exact",
ifnode->mac_filter, linenum);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Compare the mac address of two mappings
*/
static int
mapping_cmpmac(struct if_mapping * ifnode,
struct if_mapping * target)
{
/* Check for wildcard matching */
if(ifnode->active[SELECT_MAC] == HAS_MAC_FILTER)
/* Do wildcard matching, case insensitive */
return(fnmatch(ifnode->mac_filter, target->mac_filter, FNM_CASEFOLD));
else
/* Exact matching, in hex */
return((ifnode->mac_len != target->mac_len) ||
memcmp(ifnode->mac, target->mac, ifnode->mac_len));
}
/*------------------------------------------------------------------*/
/*
* Extract the MAC address and Link Type of an interface
*/
static int
mapping_getmac(int skfd,
const char * ifname,
struct if_mapping * target,
int flag)
{
struct ifreq ifr;
int ret;
int i;
/* Get MAC address */
bzero(&ifr, sizeof(struct ifreq));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
ret = ioctl(skfd, SIOCGIFHWADDR, &ifr);
if(ret < 0)
{
fprintf(stderr, "Error: Can't read MAC address on interface `%s' : %s\n",
ifname, strerror(errno));
return(-1);
}
/* Extract ARP type */
target->hw_type = ifr.ifr_hwaddr.sa_family;
/* Calculate address length */
target->mac_len = 6;
for(i = 0; i < weird_mac_len_num; i++)
if(weird_mac_len[i][0] == ifr.ifr_hwaddr.sa_family)
{
target->mac_len = weird_mac_len[i][1];
break;
}
/* Extract MAC address bytes */
memcpy(target->mac, ifr.ifr_hwaddr.sa_data, target->mac_len);
/* Check the type of comparison */
if((flag == HAS_MAC_FILTER) || verbose)
{
/* Convert to ASCII */
iw_mac_ntop(target->mac, target->mac_len,
target->mac_filter, sizeof(target->mac_filter));
}
target->active[SELECT_MAC] = flag;
target->active[SELECT_ARP] = 1;
if(verbose)
fprintf(stderr,
"Querying %s : Got MAC address `%s' and ARP/Link Type `%d'.\n",
ifname, target->mac_filter, target->hw_type);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Add a ARP/Link type selector to a mapping
*/
static int
mapping_addarp(struct if_mapping * ifnode,
int * active,
char * string,
size_t len,
struct add_extra * extra,
int linenum)
{
size_t n;
unsigned int type;
/* Avoid "Unused parameter" warning */
extra = extra;
/* Verify validity of string, convert to int */
n = strspn(string, "0123456789");
if((n < len) || (sscanf(string, "%d", &type) != 1))
{
fprintf(stderr, "Error: Invalid ARP/Link Type `%s' at line %d\n",
string, linenum);
return(-1);
}
ifnode->hw_type = (unsigned short) type;
ifnode->active[SELECT_ARP] = 1;
active[SELECT_ARP] = 1;
if(verbose)
fprintf(stderr, "Parsing : Added ARP/Link Type `%d' from line %d.\n",
ifnode->hw_type, linenum);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Compare the ARP/Link type of two mappings
*/
static int
mapping_cmparp(struct if_mapping * ifnode,
struct if_mapping * target)
{
return(!(ifnode->hw_type == target->hw_type));
}
/*------------------------------------------------------------------*/
/*
* Extract the ARP/Link type of an interface
*/
static int
mapping_getarp(int skfd,
const char * ifname,
struct if_mapping * target,
int flag)
{
/* We may have already extracted the MAC address */
if(target->active[SELECT_MAC])
return(0);
/* Otherwise just do it */
return(mapping_getmac(skfd, ifname, target, flag));
}
/*------------------------------------------------------------------*/
/*
* Add a Driver name selector to a mapping
*/
static int
mapping_adddriver(struct if_mapping * ifnode,
int * active,
char * string,
size_t len,
struct add_extra * extra,
int linenum)
{
/* Avoid "Unused parameter" warning */
extra = extra;
/* Plain string, minimal verification */
if(len >= sizeof(ifnode->driver))
{
fprintf(stderr, "Error: Driver name too long at line %d\n", linenum);
return(-1);
}
/* Copy */
memcpy(ifnode->driver, string, len + 1);
/* Activate */
ifnode->active[SELECT_DRIVER] = 1;
active[SELECT_DRIVER] = 1;
if(verbose)
fprintf(stderr,
"Parsing : Added Driver name `%s' from line %d.\n",
ifnode->driver, linenum);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Compare the Driver name of two mappings
*/
static int
mapping_cmpdriver(struct if_mapping * ifnode,
struct if_mapping * target)
{
/* Do wildcard matching, case insensitive */
return(fnmatch(ifnode->driver, target->driver, FNM_CASEFOLD));
}
/*------------------------------------------------------------------*/
/*
* Add a Bus-Info selector to a mapping
*/
static int
mapping_addbusinfo(struct if_mapping * ifnode,
int * active,
char * string,
size_t len,
struct add_extra * extra,
int linenum)
{
#if 0
size_t n;
#endif
/* Avoid "Unused parameter" warning */
extra = extra;
/* Verify validity of string */
if(len >= sizeof(ifnode->bus_info))
{
fprintf(stderr, "Bus Info too long at line %d\n", linenum);
return(-1);
}
#if 0
/* Hum... This doesn's seem true for non-PCI bus-info */
n = strspn(string, "0123456789ABCDEFabcdef:.*");
if(n < len)
{
fprintf(stderr, "Error: Invalid Bus Info `%s' at line %d\n",
string, linenum);
return(-1);
}
#endif
/* Copy */
memcpy(ifnode->bus_info, string, len + 1);
/* Activate */
ifnode->active[SELECT_BUSINFO] = 1;
active[SELECT_BUSINFO] = 1;
if(verbose)
fprintf(stderr,
"Parsing : Added Bus Info `%s' from line %d.\n",
ifnode->bus_info, linenum);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Compare the Bus-Info of two mappings
*/
static int
mapping_cmpbusinfo(struct if_mapping * ifnode,
struct if_mapping * target)
{
/* Do wildcard matching, case insensitive */
return(fnmatch(ifnode->bus_info, target->bus_info, FNM_CASEFOLD));
}
/*------------------------------------------------------------------*/
/*
* Add a Firmare revision selector to a mapping
*/
static int
mapping_addfirmware(struct if_mapping * ifnode,
int * active,
char * string,
size_t len,
struct add_extra * extra,
int linenum)
{
/* Avoid "Unused parameter" warning */
extra = extra;
/* Verify validity of string */
if(len >= sizeof(ifnode->fw_version))
{
fprintf(stderr, "Firmware revision too long at line %d\n", linenum);
return(-1);
}
/* Copy */
memcpy(ifnode->fw_version, string, len + 1);
/* Activate */
ifnode->active[SELECT_FIRMWARE] = 1;
active[SELECT_FIRMWARE] = 1;
if(verbose)
fprintf(stderr,
"Parsing : Added Firmware Revision `%s' from line %d.\n",
ifnode->fw_version, linenum);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Compare the Bus-Info of two mappings
*/
static int
mapping_cmpfirmware(struct if_mapping * ifnode,
struct if_mapping * target)
{
/* Do wildcard matching, case insensitive */
return(fnmatch(ifnode->fw_version, target->fw_version, FNM_CASEFOLD));
}
/*------------------------------------------------------------------*/
/*
* Extract the Driver name and Bus-Info from a live interface
*/
static int
mapping_getdriverbusinfo(int skfd,
const char * ifname,
struct if_mapping * target,
int flag)
{
struct ifreq ifr;
struct ethtool_drvinfo drvinfo;
int ret;
/* Avoid "Unused parameter" warning */
flag = flag;
/* We may come here twice or more, so do the job only once */
if(target->active[SELECT_DRIVER] || target->active[SELECT_BUSINFO]
|| target->active[SELECT_FIRMWARE])
return(0);
/* Prepare request */
bzero(&ifr, sizeof(struct ifreq));
bzero(&drvinfo, sizeof(struct ethtool_drvinfo));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
drvinfo.cmd = ETHTOOL_GDRVINFO;
ifr.ifr_data = (caddr_t) &drvinfo;
/* Do it */
ret = ioctl(skfd, SIOCETHTOOL, &ifr);
if(ret < 0)
{
/* Most drivers don't support that, keep quiet for now */
if(verbose)
fprintf(stderr,
"Error: Can't read driver/bus-info on interface `%s' : %s\n",
ifname, strerror(errno));
return(-1);
}
/* Copy over */
strcpy(target->driver, drvinfo.driver);
strcpy(target->bus_info, drvinfo.bus_info);
strcpy(target->fw_version, drvinfo.fw_version);
/* Activate */
target->active[SELECT_DRIVER] = 1;
target->active[SELECT_BUSINFO] = 1;
target->active[SELECT_FIRMWARE] = 1;
if(verbose)
fprintf(stderr,
"Querying %s : Got Driver name `%s', Bus Info `%s' and Firmware `%s'.\n",
ifname, target->driver, target->bus_info, target->fw_version);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Add a Base Address selector to a mapping
*/
static int
mapping_addbaseaddr(struct if_mapping * ifnode,
int * active,
char * string,
size_t len,
struct add_extra * extra,
int linenum)
{
size_t n;
unsigned int address;
/* Avoid "Unused parameter" warning */
extra = extra;
/* Verify validity of string */
n = strspn(string, "0123456789ABCDEFabcdefx");
if((n < len) || (sscanf(string, "0x%X", &address) != 1))
{
fprintf(stderr, "Error: Invalid Base Address `%s' at line %d\n",
string, linenum);
return(-1);
}
/* Copy */
ifnode->base_addr = (unsigned short) address;
/* Activate */
ifnode->active[SELECT_BASEADDR] = 1;
active[SELECT_BASEADDR] = 1;
if(verbose)
fprintf(stderr,
"Parsing : Added Base Address `0x%X' from line %d.\n",
ifnode->base_addr, linenum);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Compare the Base Address of two mappings
*/
static int
mapping_cmpbaseaddr(struct if_mapping * ifnode,
struct if_mapping * target)
{
/* Do wildcard matching, case insensitive */
return(!(ifnode->base_addr == target->base_addr));
}
/*------------------------------------------------------------------*/
/*
* Add a IRQ selector to a mapping
*/
static int
mapping_addirq(struct if_mapping * ifnode,
int * active,
char * string,
size_t len,
struct add_extra * extra,
int linenum)
{
size_t n;
unsigned int irq;
/* Avoid "Unused parameter" warning */
extra = extra;
/* Verify validity of string */
n = strspn(string, "0123456789");
if((n < len) || (sscanf(string, "%d", &irq) != 1))
{
fprintf(stderr, "Error: Invalid Base Address `%s' at line %d\n",
string, linenum);
return(-1);
}
/* Copy */
ifnode->irq = (unsigned char) irq;
/* Activate */
ifnode->active[SELECT_IRQ] = 1;
active[SELECT_IRQ] = 1;
if(verbose)
fprintf(stderr,
"Parsing : Added IRQ `%d' from line %d.\n",
ifnode->irq, linenum);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Compare the IRQ of two mappings
*/
static int
mapping_cmpirq(struct if_mapping * ifnode,
struct if_mapping * target)
{
/* Do wildcard matching, case insensitive */
return(!(ifnode->irq == target->irq));
}
/*------------------------------------------------------------------*/
/*
* Extract the Driver name and Bus-Info from a live interface
*/
static int
mapping_getbaseaddrirq(int skfd,
const char * ifname,
struct if_mapping * target,
int flag)
{
struct ifreq ifr;
struct ifmap map; /* hardware setup */
int ret;
/* Avoid "Unused parameter" warning */
flag = flag;
/* We may come here twice, so do the job only once */
if(target->active[SELECT_BASEADDR] || target->active[SELECT_IRQ])
return(0);
/* Prepare request */
bzero(&ifr, sizeof(struct ifreq));
bzero(&map, sizeof(struct ifmap));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
/* Do it */
ret = ioctl(skfd, SIOCGIFMAP, &ifr);
if(ret < 0)
{
/* Don't know if every interface has that, so keep quiet... */
if(verbose)
fprintf(stderr,
"Error: Can't read base address/irq on interface `%s' : %s\n",
ifname, strerror(errno));
return(-1);
}
/* Copy over, activate */
if(ifr.ifr_map.base_addr >= 0x100)
{
target->base_addr = ifr.ifr_map.base_addr;
target->active[SELECT_BASEADDR] = 1;
}
target->irq = ifr.ifr_map.irq;
target->active[SELECT_IRQ] = 1;
if(verbose)
fprintf(stderr,
"Querying %s : Got Base Address `0x%X' and IRQ `%d'.\n",
ifname, target->base_addr, target->irq);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Add a Wireless Protocol selector to a mapping
*/
static int
mapping_addiwproto(struct if_mapping * ifnode,
int * active,
char * string,
size_t len,
struct add_extra * extra,
int linenum)
{
/* Avoid "Unused parameter" warning */
extra = extra;
/* Verify validity of string */
if(len >= sizeof(ifnode->iwproto))
{
fprintf(stderr, "Wireless Protocol too long at line %d\n", linenum);
return(-1);
}
/* Copy */
memcpy(ifnode->iwproto, string, len + 1);
/* Activate */
ifnode->active[SELECT_IWPROTO] = 1;
active[SELECT_IWPROTO] = 1;
if(verbose)
fprintf(stderr,
"Parsing : Added Wireless Protocol `%s' from line %d.\n",
ifnode->iwproto, linenum);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Compare the Wireless Protocol of two mappings
*/
static int
mapping_cmpiwproto(struct if_mapping * ifnode,
struct if_mapping * target)
{
/* Do wildcard matching, case insensitive */
return(fnmatch(ifnode->iwproto, target->iwproto, FNM_CASEFOLD));
}
/*------------------------------------------------------------------*/
/*
* Extract the Wireless Protocol from a live interface
*/
static int
mapping_getiwproto(int skfd,
const char * ifname,
struct if_mapping * target,
int flag)
{
struct iwreq wrq;
/* Avoid "Unused parameter" warning */
flag = flag;
/* Get wireless name */
if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0)
/* Don't complain about it, Ethernet cards will never support this */
return(-1);
strncpy(target->iwproto, wrq.u.name, IFNAMSIZ);
target->iwproto[IFNAMSIZ] = '\0';
/* Activate */
target->active[SELECT_IWPROTO] = 1;
if(verbose)
fprintf(stderr,
"Querying %s : Got Wireless Protocol `%s'.\n",
ifname, target->iwproto);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Add a Pcmcia Slot selector to a mapping
*/
static int
mapping_addpcmciaslot(struct if_mapping * ifnode,
int * active,
char * string,
size_t len,
struct add_extra * extra,
int linenum)
{
size_t n;
/* Avoid "Unused parameter" warning */
extra = extra;
/* Verify validity of string, convert to int */
n = strspn(string, "0123456789");
if((n < len) || (sscanf(string, "%d", &ifnode->pcmcia_slot) != 1))
{
fprintf(stderr, "Error: Invalid Pcmcia Slot `%s' at line %d\n",
string, linenum);
return(-1);
}
ifnode->active[SELECT_PCMCIASLOT] = 1;
active[SELECT_PCMCIASLOT] = 1;
if(verbose)
fprintf(stderr, "Parsing : Added Pcmcia Slot `%d' from line %d.\n",
ifnode->pcmcia_slot, linenum);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Compare the Pcmcia Slot of two mappings
*/
static int
mapping_cmppcmciaslot(struct if_mapping * ifnode,
struct if_mapping * target)
{
return(!(ifnode->pcmcia_slot == target->pcmcia_slot));
}
/*------------------------------------------------------------------*/
/*
* Extract the Pcmcia Slot of an interface
* Note that this works only for cards fully managed by cardmgr.
* With the kernel pcmcia modules, 32 bits cards (CardBus) are not managed
* by cardmgr, and therefore won't have a valid slot number. For those
* cards, you should use Bus Info (when the driver exports it).
* In the long term, 16 bits card as well will no longer be managed by
* cardmgr. Currently, Bus Info for 16 bit cards don't have any information
* enabling to locate their physical location on the system, but I hope that
* this will change.
* When that happen, we can drop this code...
*/
static int
mapping_getpcmciaslot(int skfd,
const char * ifname,
struct if_mapping * target,
int flag)
{
FILE * stream;
char * linebuf = NULL;
size_t linelen = 0;
int linenum = 0;
/* Avoid "Unused parameter" warning */
skfd = skfd;
flag = flag;
/* Open the stab file for reading */
stream = fopen(PCMCIA_STAB1, "r");
if(!stream)
{
/* Try again, alternate location */
stream = fopen(PCMCIA_STAB2, "r");
if(!stream)
{
fprintf(stderr, "Error: Can't open PCMCIA Stab file `%s' or `%s': %s\n",
PCMCIA_STAB1, PCMCIA_STAB2, strerror(errno));
return(-1);
}
}
/* Read each line of file
* getline is a GNU extension :-( The buffer is recycled and increased
* as needed by getline. */
while(getline(&linebuf, &linelen, stream) > 0)
{
char * p;
size_t n;
size_t k;
int pcmcia_slot;
int i;
/* Keep track of line number */
linenum++;
/* Get Pcmcia socket number */
p = linebuf;
while(isspace(*p))
++p;
if(*p == '\0')
continue; /* Line ended */
n = strcspn(p, " \t\n");
k = strspn(p, "0123456789");
if((k < n) || (sscanf(p, "%d", &pcmcia_slot) != 1))
/* Next line */
continue;
/* Skip socket number */
/* Skip socket number ; device class ; driver name ; instance */
for(i = 0; i < 4; i++)
{
/* Skip item */
p += n;
/* Skip space */
p += strspn(p, " \t\n");
if(*p == '\0')
break; /* Line ended */
/* Next item size */
n = strcspn(p, " \t\n");
}
if(*p == '\0')
continue; /* Line ended */
/* Terminate dev name */
p[n] = '\0';
/* Compare to interface name */
if(!strcmp(p, ifname))
{
/* Save */
target->pcmcia_slot = pcmcia_slot;
/* Activate */
target->active[SELECT_PCMCIASLOT] = 1;
if(verbose)
fprintf(stderr,
"Querying %s : Got Pcmcia Slot `%d'.\n",
ifname, target->pcmcia_slot);
/* Exit loop, found it */
break;
}
/* Finished -> next line */
}
/* Cleanup */
free(linebuf);
fclose(stream);
return(target->active[SELECT_PCMCIASLOT] ? 0 : -1);
}
/*------------------------------------------------------------------*/
/*
* Add a sysfs selector to a mapping
*/
static int
mapping_addsysfs(struct if_mapping * ifnode,
int * active,
char * string,
size_t len,
struct add_extra * extra,
int linenum)
{
int findex; /* filename index */
char * sdup;
/* Check if we have a modifier */
if((extra == NULL) || (extra->modif_pos == NULL))
{
fprintf(stderr, "Error: No SYSFS filename at line %d\n", linenum);
return(-1);
}
/* Search if the filename already exist */
for(findex = 0; findex < sysfs_global.filenum; findex++)
{
if(!strcmp(extra->modif_pos, sysfs_global.filename[findex]))
break;
}
/* If filename does not exist, creates it */
if(findex == sysfs_global.filenum)
{
if(findex == SYSFS_MAX_FILE)
{
fprintf(stderr, "Error: Too many SYSFS filenames at line %d\n", linenum);
return(-1);
}
sdup = strndup(extra->modif_pos, extra->modif_len);
if(sdup == NULL)
{
fprintf(stderr, "Error: Can't allocate SYSFS file\n");
return(-1);
}
sysfs_global.filename[findex] = sdup;
sysfs_global.filenum++;
}
/* Store value */
sdup = strndup(string, len);
if(sdup == NULL)
{
fprintf(stderr, "Error: Can't allocate SYSFS value\n");
return(-1);
}
ifnode->sysfs[findex] = sdup;
/* Activate */
ifnode->active[SELECT_SYSFS] = 1;
active[SELECT_SYSFS] = 1;
if(verbose)
fprintf(stderr,
"Parsing : Added SYSFS filename `%s' value `%s' from line %d.\n",
sysfs_global.filename[findex], ifnode->sysfs[findex], linenum);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Compare all the sysfs values of two mappings
*/
static int
mapping_cmpsysfs(struct if_mapping * ifnode,
struct if_mapping * target)
{
int findex; /* filename index */
int match = 1;
/* Loop on all sysfs selector */
for(findex = 0; findex < sysfs_global.filenum; findex++)
{
/* If the mapping defines this sysfs selector.. */
if(ifnode->sysfs[findex] != NULL)
/* And if the sysfs values don't match */
if((target->sysfs[findex] == NULL) ||
(fnmatch(ifnode->sysfs[findex], target->sysfs[findex],
FNM_CASEFOLD)))
/* Then the sysfs selector doesn't match */
match = 0;
}
return(!match);
}
/*------------------------------------------------------------------*/
/*
* Extract all the sysfs values of an interface
*/
static int
mapping_getsysfs(int skfd,
const char * ifname,
struct if_mapping * target,
int flag)
{
FILE * stream;
char * fname;
int fnsize;
char * linebuf = NULL;
size_t linelen = 0;
char * sdup;
int findex; /* filename index */
/* Avoid "Unused parameter" warning */
skfd = skfd;
flag = flag;
/* Check if we know the devpath of this device */
if(target->sysfs_devpath == NULL)
{
/* Check if we know the root of the sysfs filesystem */
if(sysfs_global.root == NULL)
{
/* Open the mount file for reading */
stream = fopen("/proc/mounts", "r");
if(!stream)
{
fprintf(stderr, "Error: Can't open /proc/mounts file: %s\n",
strerror(errno));
return(-1);
}
/* Read each line of file
* getline is a GNU extension :-( The buffer is recycled and
* increased as needed by getline. */
while(getline(&linebuf, &linelen, stream) > 0)
{
int i;
char * p;
size_t n;
char * token[3];
size_t toklen[3];
/* The format of /proc/mounts is similar to /etc/fstab (5).
* The first argument is the device. For sysfs, there is no
* associated device, so this argument is ignored.
* The second argument is the mount point.
* The third argument is the filesystem type.
*/
/* Extract the first 3 tokens */
p = linebuf;
for(i = 0; i < 3; i++)
{
while(isspace(*p))
++p;
token[i] = p;
n = strcspn(p, " \t\n");
toklen[i] = n;
p += n;
}
/* Get the filesystem which type is "sysfs" */
if((n == 5) && (!strncasecmp(token[2], "sysfs", 5)))
{
/* Get its mount point */
n = toklen[1];
sdup = strndup(token[1], n);
if((n == 0) || (sdup == NULL))
{
fprintf(stderr,
"Error: Can't parse /proc/mounts file: %s\n",
strerror(errno));
return(-1);
}
/* Store it */
sysfs_global.root = sdup;
sysfs_global.rlen = n;
break;
}
/* Finished -> next line */
}
/* Cleanup */
fclose(stream);
/* Check if we found it */
if(sysfs_global.root == NULL)
{
fprintf(stderr,
"Error: Can't find sysfs in /proc/mounts file\n");
free(linebuf);
return(-1);
}
}
/* Construct devpath for this interface.
* Reserve enough space to replace name without realloc. */
fnsize = (sysfs_global.rlen + 11 + IFNAMSIZ + 1);
fname = malloc(fnsize);
if(fname == NULL)
{
fprintf(stderr, "Error: Can't allocate SYSFS devpath\n");
return(-1);
}
/* Not true devpath for 2.6.20+, but this syslink should work */
target->sysfs_devplen = sprintf(fname, "%s/class/net/%s",
sysfs_global.root, ifname);
target->sysfs_devpath = fname;
}
/* Loop on all sysfs selector */
for(findex = 0; findex < sysfs_global.filenum; findex++)
{
char * p;
ssize_t n;
/* Construct complete filename for the sysfs selector */
fnsize = (target->sysfs_devplen + 1 +
strlen(sysfs_global.filename[findex]) + 1);
fname = malloc(fnsize);
if(fname == NULL)
{
fprintf(stderr, "Error: Can't allocate SYSFS filename\n");
free(linebuf);
return(-1);
}
sprintf(fname, "%s/%s", target->sysfs_devpath,
sysfs_global.filename[findex]);
/* Open the sysfs file for reading */
stream = fopen(fname, "r");
if(!stream)
{
/* Some sysfs attribute may no exist for some interface */
if(verbose)
fprintf(stderr, "Error: Can't open file `%s': %s\n", fname,
strerror(errno));
/* Next sysfs selector */
continue;
}
/* Read file. Only one line in file. */
n = getline(&linebuf, &linelen, stream);
fclose(stream);
if(n <= 0)
{
/* Some attributes are just symlinks to another directory.
* We can read the attributes in that other directory
* just fine, but sometimes the symlink itself gives a lot
* of information.
* Examples : SYSFS{device} and SYSFS{device/driver}
* In such cases, get the name of the directory pointed to...
*/
/*
* I must note that the API for readlink() is very bad,
* which force us to have this ugly code. Yuck !
*/
int allocsize = 128; /* 256 = Good start */
int retry = 16;
char * linkpath = NULL;
int pathlen;
/* Try reading the link with increased buffer size */
do
{
allocsize *= 2;
linkpath = realloc(linkpath, allocsize);
pathlen = readlink(fname, linkpath, allocsize);
/* If we did not hit the buffer limit, success */
if(pathlen < allocsize)
break;
}
while(retry-- > 0);
/* Check for error, most likely ENOENT */
if(pathlen > 0)
/* We have a symlink ;-) Terminate the string. */
linkpath[pathlen] = '\0';
else
{
/* Error ! */
free(linkpath);
/* A lot of information in the sysfs is implicit, given
* by the position of a file in the tree. It is therefore
* important to be able to read the various components
* of a path. For this reason, we resolve '..' to the
* real name of the parent directory... */
/* We have at least 11 char, see above */
if(!strcmp(fname + fnsize - 4, "/.."))
//if(!strcmp(fname + strlen(fname) - 3, "/.."))
{
/* This procedure to get the realpath is not very
* nice, but it's the "best practice". Hmm... */
int cwd_fd = open(".", O_RDONLY);
linkpath = NULL;
if(cwd_fd > 0)
{
int ret = chdir(fname);
if(ret == 0)
/* Using getcwd with NULL is a GNU extension. Nice. */
linkpath = getcwd(NULL, 0);
/* This may fail, but it's not fatal */
fchdir(cwd_fd);
}
/* Check if we suceeded */
if(!linkpath)
{
free(linkpath);
if(verbose)
fprintf(stderr, "Error: Can't read parent directory `%s'\n", fname);
/* Next sysfs selector */
continue;
}
}
else
{
/* Some sysfs attribute are void for some interface,
* we may have a real directory, or we may have permission
* issues... */
if(verbose)
fprintf(stderr, "Error: Can't read file `%s'\n", fname);
/* Next sysfs selector */
continue;
}
}
/* Here, we have a link name or a parent directory name */
/* Keep only the last component of path name, save it */
p = basename(linkpath);
sdup = strdup(p);
free(linkpath);
}
else
{
/* This is a regular file (well, pseudo file) */
/* Get content, remove trailing '/n', save it */
p = linebuf;
if(p[n - 1] == '\n')
n--;
sdup = strndup(p, n);
}
if(sdup == NULL)
{
fprintf(stderr, "Error: Can't allocate SYSFS value\n");
free(linebuf);
return(-1);
}
target->sysfs[findex] = sdup;
/* Activate */
target->active[SELECT_SYSFS] = 1;
if(verbose)
fprintf(stderr,
"Querying %s : Got SYSFS filename `%s' value `%s'.\n",
ifname, sysfs_global.filename[findex], target->sysfs[findex]);
/* Finished : Next sysfs selector */
}
/* Cleanup */
free(linebuf);
return(target->active[SELECT_SYSFS] ? 0 : -1);
}
/*------------------------------------------------------------------*/
/*
* Add a Previous Interface Name selector to a mapping
*/
static int
mapping_addprevname(struct if_mapping * ifnode,
int * active,
char * string,
size_t len,
struct add_extra * extra,
int linenum)
{
/* Avoid "Unused parameter" warning */
extra = extra;
/* Verify validity of string */
if(len >= sizeof(ifnode->prevname))
{
fprintf(stderr, "Old Interface Name too long at line %d\n", linenum);
return(-1);
}
/* Copy */
memcpy(ifnode->prevname, string, len + 1);
/* Activate */
ifnode->active[SELECT_PREVNAME] = 1;
active[SELECT_PREVNAME] = 1;
if(verbose)
fprintf(stderr,
"Parsing : Added Old Interface Name `%s' from line %d.\n",
ifnode->prevname, linenum);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Compare the Previous Interface Name of two mappings
* Note : this one is special.
*/
static int
mapping_cmpprevname(struct if_mapping * ifnode,
struct if_mapping * target)
{
/* Do wildcard matching, case insensitive */
return(fnmatch(ifnode->prevname, target->ifname, FNM_CASEFOLD));
}
/*------------------------------------------------------------------*/
/*
* Extract the Previous Interface Name from a live interface
*/
static int
mapping_getprevname(int skfd,
const char * ifname,
struct if_mapping * target,
int flag)
{
/* Avoid "Unused parameter" warning */
skfd = skfd; ifname = ifname; flag = flag;
/* Don't do anything, it's already in target->ifname ;-) */
/* Activate */
target->active[SELECT_PREVNAME] = 1;
return(0);
}
/*********************** MAPPING MANAGEMENTS ***********************/
/*
* Manage interface mappings.
* Each mapping tell us how to identify a specific interface name.
* It is composed of a bunch of selector values.
*/
/*------------------------------------------------------------------*/
/*
* Create a new interface mapping and verify its name
*/
static struct if_mapping *
mapping_create(char * pos,
int len,
int linenum)
{
struct if_mapping * ifnode;
char * star;
star = memchr(pos, '*', len);
/* Check overflow, need one extra char for wildcard */
if((len + (star != NULL)) > IFNAMSIZ)
{
fprintf(stderr, "Error: Interface name `%.*s' too long at line %d\n",
(int) len, pos, linenum);
return(NULL);
}
/* Create mapping, zero it */
ifnode = calloc(1, sizeof(if_mapping));
if(!ifnode)
{
fprintf(stderr, "Error: Can't allocate interface mapping.\n");
return(NULL);
}
/* Set the name, terminates it */
memcpy(ifnode->ifname, pos, len);
ifnode->ifname[len] = '\0';
/* Check the interface name and issue various pedantic warnings.
* We assume people using takeover want to force interfaces to those
* names and know what they are doing, so don't bother them... */
if((!force_takeover) &&
((!strcmp(ifnode->ifname, "eth0")) || (!strcmp(ifnode->ifname, "wlan0"))))
fprintf(stderr,
"Warning: Interface name is `%s' at line %d, can't be mapped reliably.\n",
ifnode->ifname, linenum);
if(strchr(ifnode->ifname, ':'))
fprintf(stderr, "Warning: Alias device `%s' at line %d probably can't be mapped.\n",
ifnode->ifname, linenum);
if(verbose)
fprintf(stderr, "Parsing : Added Mapping `%s' from line %d.\n",
ifnode->ifname, linenum);
/* Done */
return(ifnode);
}
/*------------------------------------------------------------------*/
/*
* Find the most appropriate selector matching a given selector name
*/
static inline const struct mapping_selector *
selector_find(const char * string,
size_t slen,
int linenum)
{
const struct mapping_selector * found = NULL;
int ambig = 0;
int i;
/* Go through all selectors */
for(i = 0; selector_list[i].name != NULL; ++i)
{
/* No match -> next one */
if(strncasecmp(selector_list[i].name, string, slen) != 0)
continue;
/* Exact match -> perfect */
if(slen == strlen(selector_list[i].name))
return &selector_list[i];
/* Partial match */
if(found == NULL)
/* First time */
found = &selector_list[i];
else
/* Another time */
if (selector_list[i].add_fn != found->add_fn)
ambig = 1;
}
if(found == NULL)
{
fprintf(stderr, "Error: Unknown selector `%.*s' at line %d.\n",
(int) slen, string, linenum);
return NULL;
}
if(ambig)
{
fprintf(stderr, "Selector `%.*s'at line %d is ambiguous.\n",
(int) slen, string, linenum);
return NULL;
}
return found;
}
/*------------------------------------------------------------------*/
/*
* Read the configuration file and extract all valid mappings and their
* selectors.
*/
static int
mapping_readfile(const char * filename)
{
FILE * stream;
char * linebuf = NULL;
size_t linelen = 0;
int linenum = 0;
struct add_extra extrainfo;
/* Reset the list of filters */
bzero(selector_active, sizeof(selector_active));
/* Check filename */
if(!strcmp(filename, "-"))
{
/* Read from stdin */
stream = stdin;
}
else
{
/* Open the file for reading */
stream = fopen(filename, "r");
if(!stream)
{
fprintf(stderr, "Error: Can't open configuration file `%s': %s\n",
filename, strerror(errno));
return(-1);
}
}
/* Read each line of file
* getline is a GNU extension :-( The buffer is recycled and increased
* as needed by getline. */
while(getline(&linebuf, &linelen, stream) > 0)
{
struct if_mapping * ifnode;
char * p;
char * e;
size_t n;
int ret = -13; /* Complain if no selectors */
/* Keep track of line number */
linenum++;
/* Every comments terminates parsing */
if((p = strchr(linebuf,'#')) != NULL)
*p = '\0';
/* Get interface name */
p = linebuf;
while(isspace(*p))
++p;
if(*p == '\0')
continue; /* Line ended */
n = strcspn(p, " \t\n");
/* Create mapping */
ifnode = mapping_create(p, n, linenum);
if(!ifnode)
continue; /* Ignore this line */
p += n;
p += strspn(p, " \t\n");
/* Loop on all selectors */
while(*p != '\0')
{
const struct mapping_selector * selector = NULL;
struct add_extra * extra = NULL;
/* Selector name length - stop at modifier start */
n = strcspn(p, " \t\n{");
/* Find it */
selector = selector_find(p, n, linenum);
if(!selector)
{
ret = -1;
break;
}
p += n;
/* Check for modifier */
if(*p == '{')
{
p++;
/* Find end of modifier */
e = strchr(p, '}');
if(e == NULL)
{
fprintf(stderr,
"Error: unterminated selector modifier value on line %d\n",
linenum);
ret = -1;
break; /* Line ended */
}
/* Fill in struct and hook it */
extrainfo.modif_pos = p;
extrainfo.modif_len = e - p;
extra = &extrainfo;
/* Terminate modifier value */
e[0] = '\0';
/* Skip it */
p = e + 1;
}
/* Get to selector value */
p += strspn(p, " \t\n");
if(*p == '\0')
{
fprintf(stderr, "Error: no value for selector `%s' on line %d\n",
selector->name, linenum);
ret = -1;
break; /* Line ended */
}
/* Check for quoted arguments */
if(*p == '"')
{
p++;
e = strchr(p, '"');
if(e == NULL)
{
fprintf(stderr,
"Error: unterminated quoted value on line %d\n",
linenum);
ret = -1;
break; /* Line ended */
}
n = e - p;
e++;
}
else
{
/* Just end at next blank */
n = strcspn(p, " \t\n");
e = p + n;
}
/* Make 'e' point past the '\0' we are going to add */
if(*e != '\0')
e++;
/* Terminate selector value */
p[n] = '\0';
/* Add it to the mapping */
ret = selector->add_fn(ifnode, selector_active, p, n,
extra, linenum);
if(ret < 0)
break;
/* Go to next selector */
p = e;
p += strspn(p, " \t\n");
}
/* We add a mapping only if it has at least one selector and if all
* selectors were parsed properly. */
if(ret < 0)
{
/* If we have not yet printed an error, now is a good time ;-) */
if(ret == -13)
fprintf(stderr, "Error: Line %d ignored, no valid selectors\n",
linenum);
else
fprintf(stderr, "Error: Line %d ignored due to prior errors\n",
linenum);
free(ifnode);
}
else
{
/* Link it in the list */
ifnode->next = mapping_list;
mapping_list = ifnode;
}
}
/* Cleanup */
free(linebuf);
/* Finished reading, close the file */
if(stream != stdin)
fclose(stream);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Extract all the interesting selectors for the interface in consideration
*/
static struct if_mapping *
mapping_extract(int skfd,
const char * ifname)
{
struct if_mapping * target;
int i;
/* Create mapping, zero it */
target = calloc(1, sizeof(if_mapping));
if(!target)
{
fprintf(stderr, "Error: Can't allocate interface mapping.\n");
return(NULL);
}
/* Set the interface name */
strcpy(target->ifname, ifname);
/* Loop on all active selectors */
for(i = 0; i < SELECT_NUM; i++)
{
/* Check if this selector is active */
if(selector_active[i] != 0)
{
/* Extract selector */
selector_list[i].get_fn(skfd, ifname, target, selector_active[i]);
/* Ignore errors. Some mapping may not need all selectors */
}
}
return(target);
}
/*------------------------------------------------------------------*/
/*
* Find the first mapping in the list matching the one we want.
*/
static struct if_mapping *
mapping_find(struct if_mapping * target)
{
struct if_mapping * ifnode;
int i;
/* Look over all our mappings */
for(ifnode = mapping_list; ifnode != NULL; ifnode = ifnode->next)
{
int matches = 1;
/* Look over all our selectors, all must match */
for(i = 0; i < SELECT_NUM; i++)
{
/* Check if this selector is active */
if(ifnode->active[i] != 0)
{
/* If this selector doesn't match, game over for this mapping */
if((target->active[i] == 0) ||
(selector_list[i].cmp_fn(ifnode, target) != 0))
{
matches = 0;
break;
}
}
}
/* Check is this mapping was "the one" */
if(matches)
return(ifnode);
}
/* Not found */
return(NULL);
}
/************************** MODULE SUPPORT **************************/
/*
* Load all necessary module so that interfaces do exist.
* This is necessary for system that are fully modular when
* doing the boot time processing, because we need to run before
* 'ifup -a'.
*/
/*------------------------------------------------------------------*/
/*
* Probe interfaces based on our list of mappings.
* This is the default, but usually not the best way to do it.
*/
static void
probe_mappings(int skfd)
{
struct if_mapping * ifnode;
struct ifreq ifr;
/* Look over all our mappings */
for(ifnode = mapping_list; ifnode != NULL; ifnode = ifnode->next)
{
/* Can't load wildcards interface name :-( */
if(strchr(ifnode->ifname, '%') != NULL)
continue;
if(verbose)
fprintf(stderr, "Probing : Trying to load interface [%s]\n",
ifnode->ifname);
/* Trick the kernel into loading the interface.
* This allow us to not depend on the exact path and
* name of the '/sbin/modprobe' command.
* Obviously, we expect this command to 'fail', as
* the interface will load with the old/wrong name.
*/
strncpy(ifr.ifr_name, ifnode->ifname, IFNAMSIZ);
ioctl(skfd, SIOCGIFHWADDR, &ifr);
}
}
/*------------------------------------------------------------------*/
/*
* Probe interfaces based on Debian's config files.
* This allow to enly load modules for interfaces the user want active,
* all built-in interfaces that should remain unconfigured won't
* be probed (and can have mappings).
*/
static void
probe_debian(int skfd)
{
FILE * stream;
char * linebuf = NULL;
size_t linelen = 0;
struct ifreq ifr;
/* Open Debian config file */
stream = fopen(DEBIAN_CONFIG_FILE, "r");
if(stream == NULL)
{
fprintf(stderr, "Error: can't open file [%s]\n", DEBIAN_CONFIG_FILE);
return;
}
/* Read each line of file
* getline is a GNU extension :-( The buffer is recycled and increased
* as needed by getline. */
while(getline(&linebuf, &linelen, stream) > 0)
{
char * p;
char * e;
size_t n;
/* Check for auto keyword, ignore when commented out */
if(!strncasecmp(linebuf, "auto ", 5))
{
/* Skip "auto" keyword */
p = linebuf + 5;
/* Terminate at first comment */
e = strchr(p, '#');
if(e != NULL)
*e = '\0';
/* Loop on all interfaces given */
while(*p != '\0')
{
/* Interface name length */
n = strcspn(p, " \t\n");
/* Look for end of interface name */
e = p + n;
/* Make 'e' point past the '\0' we are going to add */
if(*e != '\0')
e++;
/* Terminate interface name */
p[n] = '\0';
if(verbose)
fprintf(stderr, "Probing : Trying to load interface [%s]\n",
p);
/* Load interface */
strncpy(ifr.ifr_name, p, IFNAMSIZ);
ioctl(skfd, SIOCGIFHWADDR, &ifr);
/* Go to next interface name */
p = e;
p += strspn(p, " \t\n");
}
}
}
/* Done */
fclose(stream);
return;
}
/**************************** MAIN LOGIC ****************************/
/*------------------------------------------------------------------*/
/*
* Rename an interface to a specified new name.
*/
static int
process_rename(int skfd,
char * ifname,
char * newname)
{
char retname[IFNAMSIZ+1];
int len;
char * star;
len = strlen(newname);
star = strchr(newname, '*');
/* Check newname length, need one extra char for wildcard */
if((len + (star != NULL)) > IFNAMSIZ)
{
fprintf(stderr, "Error: Interface name `%s' too long.\n",
newname);
return(-1);
}
/* Change the name of the interface */
if(if_set_name(skfd, ifname, newname, retname) < 0)
{
fprintf(stderr, "Error: cannot change name of %s to %s: %s\n",
ifname, newname, strerror(errno));
return(-1);
}
/* Always print out the *new* interface name so that
* the calling script can pick it up and know where its interface
* has gone. */
printf("%s\n", retname);
/* Done */
return(0);
}
/*------------------------------------------------------------------*/
/*
* Process a specified interface.
*/
static int
process_ifname(int skfd,
char * ifname,
char * args[],
int count)
{
struct if_mapping * target;
const struct if_mapping * mapping;
char retname[IFNAMSIZ+1];
/* Avoid "Unused parameter" warning */
args = args; count = count;
/* Get description of this interface */
target = mapping_extract(skfd, ifname);
if(target == NULL)
return(-1);
/* If udev is calling us, get the real devpath. */
if(udev_output)
{
const char *env;
/* It's passed to us as an environment variable */
env = getenv("DEVPATH");
if(env)
{
int env_len = strlen(env);
target->sysfs_devplen = env_len;
/* Make enough space for new interface name */
target->sysfs_devpath = malloc(env_len + IFNAMSIZ + 1);
if(target->sysfs_devpath != NULL)
memcpy(target->sysfs_devpath, env, env_len + 1);
}
/* We will get a second chance is the user has some sysfs selectors */
}
/* Find matching mapping */
mapping = mapping_find(target);
if(mapping == NULL)
return(-1);
/* If user specified a new name, keep only interfaces that would
* match the new name... */
if((new_name != NULL) && (if_match_ifname(mapping->ifname, new_name) != 0))
return(-1);
/* Check if user want only dry-run.
* Note that, in the case of wildcard, we don't resolve the wildcard.
* That would be tricky to do... */
if(dry_run)
{
strcpy(retname, mapping->ifname);
fprintf(stderr, "Dry-run : Would rename %s to %s.\n",
target->ifname, mapping->ifname);
}
else
{
/* Change the name of the interface */
if(if_set_name(skfd, target->ifname, mapping->ifname, retname) < 0)
{
fprintf(stderr, "Error: cannot change name of %s to %s: %s\n",
target->ifname, mapping->ifname, strerror(errno));
return(-1);
}
}
/* Check if called with an explicit interface name */
if(print_newname)
{
if(!udev_output)
/* Always print out the *new* interface name so that
* the calling script can pick it up and know where its interface
* has gone. */
printf("%s\n", retname);
else
/* udev likes to call us as an IMPORT action. This means that
* we need to return udev the environment variables changed.
* Obviously, we don't want to return anything is nothing changed. */
if(strcmp(target->ifname, retname))
{
char * pos;
/* Hack */
if(!target->sysfs_devpath)
mapping_getsysfs(skfd, ifname, target, 0);
/* Update devpath. Size is large enough. */
pos = strrchr(target->sysfs_devpath, '/');
if((pos != NULL) && (!strcmp(target->ifname, pos + 1)))
strcpy(pos + 1, retname);
/* Return new environment variables */
printf("DEVPATH=%s\nINTERFACE=%s\nINTERFACE_OLD=%s\n",
target->sysfs_devpath, retname, target->ifname);
}
}
/* Done */
return(0);
}
/*------------------------------------------------------------------*/
/*
* Process all network interface present on the system.
*/
static inline int
process_iflist(int skfd,
char * args[],
int count)
{
num_takeover = 0;
/* Just do it */
iw_enum_devices(skfd, &process_ifname, args, count);
/* If we do any takeover, the interface list grabbed with
* iw_enum_devices() may get out of sync with the real interfaces,
* and we may miss the victim interface. So, let's go through the
* list again.
* On the other hand, we may have ping pong between two interfaces,
* each claiming the same name, so let's not do it forever...
* Two time should be enough for most configs...
* Jean II */
if(force_takeover && num_takeover)
/* Play it again, Sam... */
iw_enum_devices(skfd, &process_ifname, args, count);
/* Done */
return(0);
}
/******************************* MAIN *******************************/
/*------------------------------------------------------------------*/
/*
*/
static void
usage(void)
{
fprintf(stderr, "usage: ifrename [-c configurationfile] [-i ifname] [-p] [-t] [-d] [-D]\n");
exit(1);
}
/*------------------------------------------------------------------*/
/*
* The main !
*/
int
main(int argc,
char * argv[])
{
const char * conf_file = DEFAULT_CONF;
char * ifname = NULL;
int use_probe = 0;
int is_debian = 0;
int skfd;
int ret;
/* Loop over all command line options */
while(1)
{
int c = getopt_long(argc, argv, "c:dDi:n:ptuvV", long_opt, NULL);
if(c == -1)
break;
switch(c)
{
default:
case '?':
usage();
case 'c':
conf_file = optarg;
break;
case 'd':
is_debian = 1;
break;
case 'D':
dry_run = 1;
break;
case 'i':
ifname = optarg;
break;
case 'n':
new_name = optarg;
break;
case 'p':
use_probe = 1;
break;
case 't':
force_takeover = 1;
break;
case 'u':
udev_output = 1;
break;
case 'v':
printf("%-8.16s Wireless-Tools version %d\n", "ifrename", WT_VERSION);
return(0);
case 'V':
verbose = 1;
break;
}
}
/* Read the specified/default config file, or stdin. */
if(mapping_readfile(conf_file) < 0)
return(-1);
/* Create a channel to the NET kernel. */
if((skfd = iw_sockets_open()) < 0)
{
perror("socket");
return(-1);
}
/* Check if interface name was specified with -i. */
if(ifname != NULL)
{
/* Check is target name specified */
if(new_name != NULL)
{
/* User want to simply rename an interface to a specified name */
ret = process_rename(skfd, ifname, new_name);
}
else
{
/* Rename only this interface based on mappings
* Mostly used for HotPlug processing (from /etc/hotplug/net.agent)
* or udev processing (from a udev IMPORT rule).
* Process the network interface specified on the command line,
* and return the new name on stdout.
*/
print_newname = 1;
ret = process_ifname(skfd, ifname, NULL, 0);
}
}
else
{
/* Load all the necesary modules */
if(use_probe)
{
if(is_debian)
probe_debian(skfd);
else
probe_mappings(skfd);
}
/* Rename all system interfaces
* Mostly used for boot time processing (from init scripts).
*/
ret = process_iflist(skfd, NULL, 0);
}
/* Cleanup */
iw_sockets_close(skfd);
return(ret);
}