2722 lines
69 KiB
C
Executable File
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);
|
|
}
|