2012-11-09 15:37:40 +04:00
/***
This file is part of systemd .
Copyright 2012 Kay Sievers < kay @ vrfy . org >
systemd is free software ; you can redistribute it and / or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
2012-11-20 21:10:45 +04:00
/*
2012-12-02 05:00:57 +04:00
* predictable network interface device names based on :
* - firmware / bios - provided index numbers for on - board devices
* - firmware - provided pci - express hotplug slot index number
* - physical / geographical location of the hardware
* - the interface ' s MAC address
*
* two character prefixes based on the type of interface :
2012-11-26 17:07:16 +04:00
* en - - ethernet
* wl - - wlan
* ww - - wwan
2012-11-20 21:10:45 +04:00
*
2012-12-02 05:00:57 +04:00
* type of names :
* o < index > - - on - board device index number
* s < slot > [ f < function > ] - - hotplug slot index number
2012-11-30 22:27:42 +04:00
* x < MAC > - - MAC address
2012-12-02 05:00:57 +04:00
* p < bus > s < slot > [ f < function > ] - - PCI geographical location
*
* All multi - function devices will carry the [ f < function > ] number in the
* device name , including the function 0 device .
2012-11-20 21:10:45 +04:00
*
2012-12-02 05:00:57 +04:00
* examples :
2012-11-26 17:07:16 +04:00
* ID_NET_NAME_ONBOARD = eno1
2012-11-30 22:27:42 +04:00
* ID_NET_NAME_SLOT = ens1
* ID_NET_NAME_SLOT = ens2f0
* ID_NET_NAME_SLOT = ens2f1
2012-11-26 17:07:16 +04:00
* ID_NET_NAME_MAC = enxf0def180d479
2012-11-30 22:27:42 +04:00
* ID_NET_NAME_PATH = enp0s25
* ID_NET_NAME_PATH = enp19s3f0
* ID_NET_NAME_PATH = enp19s3f1
2012-11-20 21:10:45 +04:00
*/
2012-11-09 15:37:40 +04:00
# include <stdio.h>
# include <stdlib.h>
# include <stdarg.h>
# include <unistd.h>
# include <string.h>
# include <errno.h>
2012-12-06 23:42:49 +04:00
# include <net/if.h>
2012-11-30 22:27:42 +04:00
# include <linux/pci_regs.h>
2012-11-09 15:37:40 +04:00
# include "udev.h"
2012-12-06 23:42:49 +04:00
enum netname_type {
NET_UNDEF ,
NET_PCI ,
NET_USB ,
} ;
struct netnames {
enum netname_type type ;
uint8_t mac [ 6 ] ;
bool mac_valid ;
struct udev_device * pcidev ;
char pci_slot [ IFNAMSIZ ] ;
char pci_path [ IFNAMSIZ ] ;
char pci_onboard [ IFNAMSIZ ] ;
const char * pci_onboard_label ;
struct udev_device * usbdev ;
char usb_ports [ IFNAMSIZ ] ;
} ;
2012-11-26 17:07:16 +04:00
/* retrieve on-board index number and label from firmware */
2012-12-06 23:42:49 +04:00
static int dev_pci_onboard ( struct udev_device * dev , struct netnames * names ) {
2012-11-20 21:10:45 +04:00
const char * index ;
2012-11-26 17:07:16 +04:00
int idx ;
2012-11-09 15:37:40 +04:00
2012-11-26 17:07:16 +04:00
/* ACPI _DSM -- device specific method for naming a PCI or PCI Express device */
2012-12-06 23:42:49 +04:00
index = udev_device_get_sysattr_value ( names - > pcidev , " acpi_index " ) ;
2012-11-23 18:01:43 +04:00
/* SMBIOS type 41 -- Onboard Devices Extended Information */
if ( ! index )
2012-12-06 23:42:49 +04:00
index = udev_device_get_sysattr_value ( names - > pcidev , " index " ) ;
2012-11-26 17:07:16 +04:00
if ( ! index )
return - ENOENT ;
idx = strtoul ( index , NULL , 0 ) ;
if ( idx < = 0 )
return - EINVAL ;
2012-12-06 23:42:49 +04:00
snprintf ( names - > pci_onboard , sizeof ( names - > pci_onboard ) , " o%d " , idx ) ;
2012-11-20 21:10:45 +04:00
2012-12-06 23:42:49 +04:00
names - > pci_onboard_label = udev_device_get_sysattr_value ( names - > pcidev , " label " ) ;
2012-11-26 17:07:16 +04:00
return 0 ;
}
2012-11-20 21:10:45 +04:00
2012-12-03 18:47:18 +04:00
/* read the 256 bytes PCI configuration space to check the multi-function bit */
2012-11-30 22:27:42 +04:00
static bool is_pci_singlefunction ( struct udev_device * dev ) {
char filename [ 256 ] ;
FILE * f ;
2012-12-06 23:42:49 +04:00
char config [ 64 ] ;
2012-11-30 22:27:42 +04:00
bool single = false ;
snprintf ( filename , sizeof ( filename ) , " %s/config " , udev_device_get_syspath ( dev ) ) ;
f = fopen ( filename , " re " ) ;
if ( ! f )
goto out ;
if ( fread ( & config , sizeof ( config ) , 1 , f ) ! = 1 )
goto out ;
/* bit 0-6 header type, bit 7 multi/single function device */
if ( ( config [ PCI_HEADER_TYPE ] & 0x80 ) = = 0 )
single = true ;
out :
fclose ( f ) ;
return single ;
}
2012-12-06 23:42:49 +04:00
static int dev_pci_slot ( struct udev_device * dev , struct netnames * names ) {
struct udev * udev = udev_device_get_udev ( names - > pcidev ) ;
2012-11-26 17:07:16 +04:00
unsigned int bus ;
unsigned int slot ;
unsigned int func ;
struct udev_device * pci = NULL ;
char slots [ 256 ] ;
DIR * dir ;
struct dirent * dent ;
char str [ 256 ] ;
int hotplug_slot = 0 ;
int err = 0 ;
/* compose a name based on the raw kernel's PCI bus, slot numbers */
2012-12-06 23:42:49 +04:00
if ( sscanf ( udev_device_get_sysname ( names - > pcidev ) , " 0000:%x:%x.%d " , & bus , & slot , & func ) ! = 3 )
2012-11-26 17:07:16 +04:00
return - ENOENT ;
2012-12-06 23:42:49 +04:00
if ( func = = 0 & & is_pci_singlefunction ( names - > pcidev ) )
snprintf ( names - > pci_path , sizeof ( names - > pci_path ) , " p%ds%d " , bus , slot ) ;
2012-11-30 22:27:42 +04:00
else
2012-12-06 23:42:49 +04:00
snprintf ( names - > pci_path , sizeof ( names - > pci_path ) , " p%ds%df%d " , bus , slot , func ) ;
2012-11-20 21:10:45 +04:00
2012-11-26 17:07:16 +04:00
/* ACPI _SUN -- slot user number */
pci = udev_device_new_from_subsystem_sysname ( udev , " subsystem " , " pci " ) ;
if ( ! pci ) {
err = - ENOENT ;
goto out ;
}
snprintf ( slots , sizeof ( slots ) , " %s/slots " , udev_device_get_syspath ( pci ) ) ;
dir = opendir ( slots ) ;
if ( ! dir ) {
err = - errno ;
goto out ;
2012-11-20 21:10:45 +04:00
}
2012-11-26 17:07:16 +04:00
for ( dent = readdir ( dir ) ; dent ! = NULL ; dent = readdir ( dir ) ) {
int i ;
char * rest ;
char * address ;
if ( dent - > d_name [ 0 ] = = ' . ' )
continue ;
i = strtol ( dent - > d_name , & rest , 10 ) ;
if ( rest [ 0 ] ! = ' \0 ' )
continue ;
if ( i < 1 )
continue ;
snprintf ( str , sizeof ( str ) , " %s/%s/address " , slots , dent - > d_name ) ;
if ( read_one_line_file ( str , & address ) > = 0 ) {
/* match slot address with device by stripping the function */
2012-12-06 23:42:49 +04:00
if ( strncmp ( address , udev_device_get_sysname ( names - > pcidev ) , strlen ( address ) ) = = 0 )
2012-11-26 17:07:16 +04:00
hotplug_slot = i ;
free ( address ) ;
}
if ( hotplug_slot > 0 )
break ;
}
closedir ( dir ) ;
2012-11-20 21:10:45 +04:00
2012-11-26 17:07:16 +04:00
if ( hotplug_slot > 0 ) {
2012-12-06 23:42:49 +04:00
if ( func = = 0 & & is_pci_singlefunction ( names - > pcidev ) )
snprintf ( names - > pci_slot , sizeof ( names - > pci_slot ) , " s%d " , hotplug_slot ) ;
2012-11-30 22:27:42 +04:00
else
2012-12-06 23:42:49 +04:00
snprintf ( names - > pci_slot , sizeof ( names - > pci_slot ) , " s%df%d " , hotplug_slot , func ) ;
2012-11-20 21:10:45 +04:00
}
2012-11-26 17:07:16 +04:00
out :
udev_device_unref ( pci ) ;
return err ;
}
2012-12-06 23:42:49 +04:00
static int names_pci ( struct udev_device * dev , struct netnames * names ) {
2012-11-26 18:03:06 +04:00
struct udev_device * parent ;
2012-11-26 17:07:16 +04:00
2012-11-26 18:03:06 +04:00
parent = udev_device_get_parent ( dev ) ;
2012-12-06 23:42:49 +04:00
if ( ! parent )
return - ENOENT ;
/* check if our direct parent is a PCI device with no other bus in-between */
if ( streq ( " pci " , udev_device_get_subsystem ( parent ) ) ) {
names - > type = NET_PCI ;
names - > pcidev = parent ;
} else {
names - > pcidev = udev_device_get_parent_with_subsystem_devtype ( dev , " pci " , NULL ) ;
if ( ! names - > pcidev )
return - ENOENT ;
}
dev_pci_onboard ( dev , names ) ;
dev_pci_slot ( dev , names ) ;
return 0 ;
}
static int names_usb ( struct udev_device * dev , struct netnames * names ) {
char name [ 256 ] ;
char * ports ;
char * config ;
char * interf ;
size_t l ;
char * s ;
names - > usbdev = udev_device_get_parent_with_subsystem_devtype ( dev , " usb " , " usb_interface " ) ;
if ( ! names - > usbdev )
2012-11-26 17:07:16 +04:00
return - ENOENT ;
2012-12-06 23:42:49 +04:00
/* get USB port number chain, configuration, interface */
util_strscpy ( name , sizeof ( name ) , udev_device_get_sysname ( names - > usbdev ) ) ;
s = strchr ( name , ' - ' ) ;
if ( ! s )
return - EINVAL ;
ports = s + 1 ;
s = strchr ( ports , ' : ' ) ;
if ( ! s )
return - EINVAL ;
s [ 0 ] = ' \0 ' ;
config = s + 1 ;
s = strchr ( config , ' . ' ) ;
if ( ! s )
return - EINVAL ;
s [ 0 ] = ' \0 ' ;
interf = s + 1 ;
/* prefix every port number in the chain with "u"*/
s = ports ;
while ( ( s = strchr ( s , ' . ' ) ) )
s [ 0 ] = ' u ' ;
s = names - > usb_ports ;
l = util_strpcpyl ( & s , sizeof ( names - > usb_ports ) , " u " , ports , NULL ) ;
/* append USB config number, suppress the common config == 1 */
if ( ! streq ( config , " 1 " ) )
l = util_strpcpyl ( & s , sizeof ( names - > usb_ports ) , " c " , config , NULL ) ;
/* append USB interface number, suppress the interface == 0 */
if ( ! streq ( interf , " 0 " ) )
l = util_strpcpyl ( & s , sizeof ( names - > usb_ports ) , " i " , interf , NULL ) ;
if ( l = = 0 )
return - ENAMETOOLONG ;
names - > type = NET_USB ;
2012-11-20 21:10:45 +04:00
return 0 ;
}
2012-12-06 23:42:49 +04:00
static int names_mac ( struct udev_device * dev , struct netnames * names ) {
2012-11-20 21:10:45 +04:00
const char * s ;
unsigned int i ;
unsigned int a1 , a2 , a3 , a4 , a5 , a6 ;
/* check for NET_ADDR_PERM, skip random MAC addresses */
s = udev_device_get_sysattr_value ( dev , " addr_assign_type " ) ;
if ( ! s )
return EXIT_FAILURE ;
i = strtoul ( s , NULL , 0 ) ;
if ( i ! = 0 )
return 0 ;
s = udev_device_get_sysattr_value ( dev , " address " ) ;
if ( ! s )
return - ENOENT ;
if ( sscanf ( s , " %x:%x:%x:%x:%x:%x " , & a1 , & a2 , & a3 , & a4 , & a5 , & a6 ) ! = 6 )
return - EINVAL ;
/* skip empty MAC addresses */
if ( a1 + a2 + a3 + a4 + a5 + a6 = = 0 )
2012-11-09 15:37:40 +04:00
return - EINVAL ;
2012-12-06 23:42:49 +04:00
names - > mac [ 0 ] = a1 ;
names - > mac [ 1 ] = a2 ;
names - > mac [ 2 ] = a3 ;
names - > mac [ 3 ] = a4 ;
names - > mac [ 4 ] = a5 ;
names - > mac [ 5 ] = a6 ;
names - > mac_valid = true ;
return 0 ;
}
2012-11-20 21:10:45 +04:00
2012-12-06 23:42:49 +04:00
/* IEEE Organizationally Unique Identifier vendor string */
static int ieee_oui ( struct udev_device * dev , struct netnames * names , bool test ) {
char str [ IFNAMSIZ ] ;
if ( names - > mac_valid )
return - ENOENT ;
/* skip commonly misused 00:00:00 (Xerox) prefix */
if ( memcmp ( names - > mac , " \0 \0 \0 " , 3 ) = = 0 )
return - EINVAL ;
snprintf ( str , sizeof ( str ) , " OUI:%02X%02X%02X%02X%02X%02X " ,
names - > mac [ 0 ] , names - > mac [ 1 ] , names - > mac [ 2 ] ,
names - > mac [ 3 ] , names - > mac [ 4 ] , names - > mac [ 5 ] ) ;
udev_builtin_hwdb_lookup ( dev , str , test ) ;
return 0 ;
2012-11-09 15:37:40 +04:00
}
static int builtin_net_id ( struct udev_device * dev , int argc , char * argv [ ] , bool test ) {
2012-11-20 21:10:45 +04:00
const char * s ;
unsigned int i ;
const char * devtype ;
const char * prefix = " en " ;
2012-12-06 23:42:49 +04:00
struct netnames names ;
int err ;
2012-11-20 21:10:45 +04:00
/* handle only ARPHRD_ETHER devices */
s = udev_device_get_sysattr_value ( dev , " type " ) ;
if ( ! s )
return EXIT_FAILURE ;
i = strtoul ( s , NULL , 0 ) ;
if ( i ! = 1 )
return 0 ;
devtype = udev_device_get_devtype ( dev ) ;
if ( devtype ) {
if ( streq ( " wlan " , devtype ) )
prefix = " wl " ;
else if ( streq ( " wwan " , devtype ) )
prefix = " ww " ;
}
2012-12-06 23:42:49 +04:00
zero ( names ) ;
err = names_mac ( dev , & names ) ;
if ( err > = 0 & & names . mac_valid ) {
char str [ IFNAMSIZ ] ;
snprintf ( str , sizeof ( str ) , " %sx%02x%02x%02x%02x%02x%02x " , prefix ,
names . mac [ 0 ] , names . mac [ 1 ] , names . mac [ 2 ] ,
names . mac [ 3 ] , names . mac [ 4 ] , names . mac [ 5 ] ) ;
udev_builtin_add_property ( dev , test , " ID_NET_NAME_MAC " , str ) ;
ieee_oui ( dev , & names , test ) ;
}
/* get PCI based path names, we compose only PCI based paths */
err = names_pci ( dev , & names ) ;
if ( err < 0 )
goto out ;
/* plain PCI device */
if ( names . type = = NET_PCI ) {
char str [ IFNAMSIZ ] ;
if ( names . pci_onboard [ 0 ] )
if ( snprintf ( str , sizeof ( str ) , " %s%s " , prefix , names . pci_onboard ) < ( int ) sizeof ( str ) )
udev_builtin_add_property ( dev , test , " ID_NET_NAME_ONBOARD " , str ) ;
if ( names . pci_onboard_label )
if ( snprintf ( str , sizeof ( str ) , " %s%s " , prefix , names . pci_onboard_label ) < ( int ) sizeof ( str ) )
udev_builtin_add_property ( dev , test , " ID_NET_LABEL_ONBOARD " , str ) ;
if ( names . pci_path [ 0 ] )
if ( snprintf ( str , sizeof ( str ) , " %s%s " , prefix , names . pci_path ) < ( int ) sizeof ( str ) )
udev_builtin_add_property ( dev , test , " ID_NET_NAME_PATH " , str ) ;
if ( names . pci_slot [ 0 ] )
if ( snprintf ( str , sizeof ( str ) , " %s%s " , prefix , names . pci_slot ) < ( int ) sizeof ( str ) )
udev_builtin_add_property ( dev , test , " ID_NET_NAME_SLOT " , str ) ;
goto out ;
}
/* USB device */
err = names_usb ( dev , & names ) ;
if ( err > = 0 & & names . type = = NET_USB ) {
char str [ IFNAMSIZ ] ;
if ( names . pci_path [ 0 ] )
if ( snprintf ( str , sizeof ( str ) , " %s%s%s " , prefix , names . pci_path , names . usb_ports ) < ( int ) sizeof ( str ) )
udev_builtin_add_property ( dev , test , " ID_NET_NAME_PATH " , str ) ;
if ( names . pci_slot [ 0 ] )
if ( snprintf ( str , sizeof ( str ) , " %s%s%s " , prefix , names . pci_slot , names . usb_ports ) < ( int ) sizeof ( str ) )
udev_builtin_add_property ( dev , test , " ID_NET_NAME_SLOT " , str ) ;
}
out :
2012-11-09 15:37:40 +04:00
return EXIT_SUCCESS ;
}
const struct udev_builtin udev_builtin_net_id = {
. name = " net_id " ,
. cmd = builtin_net_id ,
. help = " network device properties " ,
} ;