diff options
author | Bob Gilligan <gilligan@vyatta.com> | 2010-02-05 09:54:11 -0800 |
---|---|---|
committer | Bob Gilligan <gilligan@vyatta.com> | 2010-02-05 09:54:11 -0800 |
commit | e1971e4774a6ebb5ed33a09bdd60afa2c0534b6f (patch) | |
tree | f4fc5aaccb4d9dac43ae7a825df9006532c1a059 /src/eths.c | |
download | vyatta-biosdevname-e1971e4774a6ebb5ed33a09bdd60afa2c0534b6f.tar.gz vyatta-biosdevname-e1971e4774a6ebb5ed33a09bdd60afa2c0534b6f.zip |
Initial commit.debian/0.1
Diffstat (limited to 'src/eths.c')
-rw-r--r-- | src/eths.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/src/eths.c b/src/eths.c new file mode 100644 index 0000000..6ad5878 --- /dev/null +++ b/src/eths.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ +#define _GNU_SOURCE + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <getopt.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <linux/sockios.h> +#include <unistd.h> +#include <net/if.h> +#include <net/if_arp.h> +#include "ethtool-util.h" +#include "pci.h" +#include "eths.h" +#include "state.h" + +/* Display an Ethernet address in readable format. */ +char *pr_ether(char *buf, const int size, const unsigned char *s) +{ + snprintf(buf, size, "%02X:%02X:%02X:%02X:%02X:%02X", + (s[0] & 0377), (s[1] & 0377), (s[2] & 0377), + (s[3] & 0377), (s[4] & 0377), (s[5] & 0377) + ); + return (buf); +} + +static int eths_get_hwaddr(const char *devname, unsigned char *buf, int size, int *type) +{ + int fd, err; + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name)); + + /* Open control socket. */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("Cannot get control socket"); + return 1; + } + + err = ioctl(fd, SIOCGIFHWADDR, &ifr); + if (!err) { + memcpy(buf, ifr.ifr_hwaddr.sa_data, min(size, sizeof(ifr.ifr_hwaddr.sa_data))); + *type = ifr.ifr_hwaddr.sa_family; + } + else { + perror("SIOCGIFHWADDR failed"); + } + close(fd); + return err; +} + +static int eths_get_info(const char *devname, struct ethtool_drvinfo *drvinfo) +{ + int fd, err; + struct ifreq ifr; + + /* Setup our control structures. */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name)); + + /* Open control socket. */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("Cannot get control socket"); + return 1; + } + + drvinfo->cmd = ETHTOOL_GDRVINFO; + ifr.ifr_data = (caddr_t)drvinfo; + err = ioctl(fd, SIOCETHTOOL, &ifr); + close(fd); + return err; +} + +static int eths_get_permaddr(const char *devname, unsigned char *buf, int size) +{ + int fd, err; + struct ifreq ifr; + struct ethtool_perm_addr *permaddr; + int s = sizeof(*permaddr) + MAX_ADDR_LEN; + + permaddr = malloc(s); + if (!permaddr) + return 1; + memset(permaddr, 0, s); + + /* Setup our control structures. */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name)); + + + + /* Open control socket. */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("Cannot get control socket"); + return 1; + } + + permaddr->cmd = ETHTOOL_GPERMADDR; + permaddr->size = MAX_ADDR_LEN; + ifr.ifr_data = (caddr_t)permaddr; + err = ioctl(fd, SIOCETHTOOL, &ifr); + if (err < 0) { + close(fd); + return err; + } + memcpy(buf, permaddr->data, min(permaddr->size, size)); + free(permaddr); + close(fd); + return err; +} + +static void fill_eth_dev(struct network_device *dev) +{ + int rc; + eths_get_hwaddr(dev->kernel_name, dev->dev_addr, sizeof(dev->dev_addr), &dev->arphrd_type); + eths_get_permaddr(dev->kernel_name, dev->perm_addr, sizeof(dev->perm_addr)); + rc = eths_get_info(dev->kernel_name, &dev->drvinfo); + if (rc == 0) + dev->drvinfo_valid = 1; +} + +void free_eths(struct libbiosdevname_state *state) +{ + struct network_device *pos, *next; + list_for_each_entry_safe(pos, next, &state->network_devices, node) { + list_del(&pos->node); + free(pos); + } +} + +/* read_proc.c */ +extern int get_interfaces(struct libbiosdevname_state *state); + +void get_eths(struct libbiosdevname_state *state) +{ + struct network_device *pos; + get_interfaces(state); + list_for_each_entry(pos, &state->network_devices, node) { + fill_eth_dev(pos); + } +} + +int zero_mac(const void *addr) +{ + char zero_mac[MAX_ADDR_LEN]; + memset(zero_mac, 0, sizeof(zero_mac)); + return !memcmp(zero_mac, addr, sizeof(zero_mac)); +} + + +int unparse_network_device(char *buf, const int size, struct network_device *dev) +{ + char buffer[40]; + char *s = buf; + s += snprintf(s, size-(s-buf), "Kernel name: %s\n", dev->kernel_name); + if (!zero_mac(dev->perm_addr)) + s += snprintf(s, size-(s-buf), "Permanant MAC: %s\n", pr_ether(buffer, sizeof(buffer), dev->perm_addr)); + s += snprintf(s, size-(s-buf), "Assigned MAC : %s\n", pr_ether(buffer, sizeof(buffer), dev->dev_addr)); + if (drvinfo_valid(dev)) { + s += snprintf(s, size-(s-buf), "Driver: %s\n", dev->drvinfo.driver); + s += snprintf(s, size-(s-buf), "Driver version: %s\n", dev->drvinfo.version); + s += snprintf(s, size-(s-buf), "Firmware version: %s\n", dev->drvinfo.fw_version); + s += snprintf(s, size-(s-buf), "Bus Info: %s\n", dev->drvinfo.bus_info); + } + return (s-buf); +}; + +struct network_device * find_net_device_by_bus_info(struct libbiosdevname_state *state, + const char *bus_info) +{ + struct network_device *n; + list_for_each_entry(n, &state->network_devices, node) { + if (!strncmp(n->drvinfo.bus_info, bus_info, sizeof(n->drvinfo.bus_info))) + return n; + } + return NULL; +} + +int is_ethernet(struct network_device *dev) +{ + int i; + int rc = 0; + + /* FIXME: /sys/class/net/$kernel_name/device will be a symlink if there's underlying hardware, + * or not exist if it's virtual. Try using that if this isn't good enough already. + */ + + /* No bus means not visible to BIOS */ + if (strncmp("N/A", dev->drvinfo.bus_info, sizeof(dev->drvinfo.bus_info)) == 0) + goto out; + + const char *nonethernet_drivers[] = { + "bridge", + "tun", + }; + for (i=0; i<sizeof(nonethernet_drivers)/sizeof(nonethernet_drivers[0]); i++) { + if (strncmp(dev->drvinfo.driver, nonethernet_drivers[i], sizeof(dev->drvinfo.driver)) == 0) + goto out; + } + + rc = dev->arphrd_type == ARPHRD_ETHER; +out: + return rc; +} |