]> git.ipfire.org Git - people/teissler/ipfire-2.x.git/blobdiff - src/hwinfo/src/hd/net.c
Zwischencommit Installer...
[people/teissler/ipfire-2.x.git] / src / hwinfo / src / hd / net.c
diff --git a/src/hwinfo/src/hd/net.c b/src/hwinfo/src/hd/net.c
new file mode 100644 (file)
index 0000000..955740f
--- /dev/null
@@ -0,0 +1,546 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#define u8 uint8_t
+#define u16 uint16_t
+#define u32 uint32_t
+#define u64 uint64_t
+#include <linux/if.h>
+#include <linux/sockios.h>
+#include <linux/ethtool.h>
+#include <linux/if_arp.h>
+
+#include "hd.h"
+#include "hd_int.h"
+#include "net.h"
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * gather network interface info
+ *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+static void get_driverinfo(hd_data_t *hd_data, hd_t *hd);
+static void get_linkstate(hd_data_t *hd_data, hd_t *hd);
+static void add_xpnet(hd_data_t *hdata);
+static void add_iseries(hd_data_t *hdata);
+static void add_uml(hd_data_t *hdata);
+
+/*
+ * This is independent of the other scans.
+ */
+
+void hd_scan_net(hd_data_t *hd_data)
+{
+  unsigned u;
+  int if_type;
+  hd_t *hd, *hd_card;
+  char *s, *hw_addr;
+  hd_res_t *res, *res1;
+  uint64_t ul0;
+
+  struct sysfs_class *sf_class;
+  struct sysfs_class_device *sf_cdev;
+  struct sysfs_device *sf_dev;
+  struct sysfs_driver *sf_drv;
+  struct dlist *sf_cdev_list;
+
+  if(!hd_probe_feature(hd_data, pr_net)) return;
+
+  hd_data->module = mod_net;
+
+  /* some clean-up */
+  remove_hd_entries(hd_data);
+  hd_data->net = free_str_list(hd_data->net);
+
+  PROGRESS(1, 0, "get network data");
+
+  sf_class = sysfs_open_class("net");
+
+  if(!sf_class) {
+    ADD2LOG("sysfs: no such class: net\n");
+    return;
+  }
+
+  sf_cdev_list = sysfs_get_class_devices(sf_class);
+  if(sf_cdev_list) dlist_for_each_data(sf_cdev_list, sf_cdev, struct sysfs_class_device) {
+    hd_card = NULL;
+
+    ADD2LOG(
+      "  net interface: name = %s, classname = %s, path = %s\n",
+      sf_cdev->name,
+      sf_cdev->classname,
+      hd_sysfs_id(sf_cdev->path)
+    );
+
+    if_type = -1;
+    if(hd_attr_uint(sysfs_get_classdev_attr(sf_cdev, "type"), &ul0, 0)) {
+      if_type = ul0;
+      ADD2LOG("    type = %d\n", if_type);
+    }
+
+    hw_addr = NULL;
+    if((s = hd_attr_str(sysfs_get_classdev_attr(sf_cdev, "address")))) {
+      hw_addr = canon_str(s, strlen(s));
+      ADD2LOG("    hw_addr = %s\n", hw_addr);
+    }
+
+    sf_dev = sysfs_get_classdev_device(sf_cdev);
+    if(sf_dev) {
+      ADD2LOG("    net device: path = %s\n", hd_sysfs_id(sf_dev->path));
+    }
+
+    sf_drv = sysfs_get_classdev_driver(sf_cdev);
+    if(sf_drv) {
+      ADD2LOG(
+        "    net driver: name = %s, path = %s\n",
+        sf_drv->name,
+        hd_sysfs_id(sf_drv->path)
+      );
+    }
+
+    hd = add_hd_entry(hd_data, __LINE__, 0);
+    hd->base_class.id = bc_network_interface;
+    hd->sub_class.id = sc_nif_other;
+
+    res1 = NULL;
+    if(hw_addr && strspn(hw_addr, "0:") != strlen(hw_addr)) {
+      res1 = new_mem(sizeof *res1);
+      res1->hwaddr.type = res_hwaddr;
+      res1->hwaddr.addr = new_str(hw_addr);
+      add_res_entry(&hd->res, res1);
+    }
+
+    hw_addr = free_mem(hw_addr);
+
+    hd->unix_dev_name = new_str(sf_cdev->name);
+    hd->sysfs_id = new_str(hd_sysfs_id(sf_cdev->path));
+
+    if(sf_drv) {
+      add_str_list(&hd->drivers, sf_drv->name);
+    }
+    else if(hd->res) {
+      get_driverinfo(hd_data, hd);
+    }
+
+    if(sf_dev) {
+      hd->sysfs_device_link = new_str(hd_sysfs_id(sf_dev->path)); 
+
+      hd_card = hd_find_sysfs_id(hd_data, hd_sysfs_id(sf_dev->path));
+      if(hd_card) {
+        hd->attached_to = hd_card->idx;
+
+        /* for cards with strange pci classes */
+        hd_set_hw_class(hd_card, hw_network_ctrl);
+
+        /* add hw addr to network card */
+        if(res1) {
+          u = 0;
+          for(res = hd_card->res; res; res = res->next) {
+            if(
+              res->any.type == res_hwaddr &&
+              !strcmp(res->hwaddr.addr, res1->hwaddr.addr)
+            ) {
+              u = 1;
+              break;
+            }
+          }
+          if(!u) {
+            res = new_mem(sizeof *res);
+            res->hwaddr.type = res_hwaddr;
+            res->hwaddr.addr = new_str(res1->hwaddr.addr);
+            add_res_entry(&hd_card->res, res);
+          }
+        }
+        /* add interface names */
+        if(hd->unix_dev_name) {
+          if(!search_str_list(hd_card->unix_dev_names, hd->unix_dev_name)) {
+            add_str_list(&hd_card->unix_dev_names, hd->unix_dev_name);
+          }
+          if(!hd_card->unix_dev_name) {
+            hd_card->unix_dev_name = new_str(hd->unix_dev_name);
+          }
+        }
+      }
+    }
+
+#if 0
+    "ctc"      sc_nif_ctc
+    "iucv"     sc_nif_iucv
+    "hsi"      sc_nif_hsi
+    "qeth"     sc_nif_qeth
+    "escon"    sc_nif_escon
+    "myri"     sc_nif_myrinet
+    "wlan"     sc_nif_wlan
+    "xp"       sc_nif_xp
+    "usb"      sc_nif_usb
+#endif
+    switch(if_type) {
+      case ARPHRD_ETHER:       /* eth */
+        hd->sub_class.id = sc_nif_ethernet;
+        break;
+      case ARPHRD_LOOPBACK:    /* lo */
+        hd->sub_class.id = sc_nif_loopback;
+        break;
+      case ARPHRD_SIT:         /* sit */
+        hd->sub_class.id = sc_nif_sit;
+        break;
+      case ARPHRD_FDDI:                /* fddi */
+        hd->sub_class.id = sc_nif_fddi;
+        break;
+      case ARPHRD_IEEE802_TR:  /* tr */
+        hd->sub_class.id = sc_nif_tokenring;
+        break;
+#if 0
+      case ARPHRD_IEEE802:     /* fc */
+        hd->sub_class.id = sc_nif_fc;
+        break;
+#endif
+    }
+
+    if(!strcmp(hd->unix_dev_name, "lo")) {
+      hd->sub_class.id = sc_nif_loopback;
+    }
+    else if(sscanf(hd->unix_dev_name, "eth%u", &u) == 1) {
+      hd->sub_class.id = sc_nif_ethernet;
+      hd->slot = u;
+    }
+    else if(sscanf(hd->unix_dev_name, "tr%u", &u) == 1) {
+      hd->sub_class.id = sc_nif_tokenring;
+      hd->slot = u;
+    }
+    else if(sscanf(hd->unix_dev_name, "fddi%u", &u) == 1) {
+      hd->sub_class.id = sc_nif_fddi;
+      hd->slot = u;
+    }
+    else if(sscanf(hd->unix_dev_name, "ctc%u", &u) == 1) {
+      hd->sub_class.id = sc_nif_ctc;
+      hd->slot = u;
+    }
+    else if(sscanf(hd->unix_dev_name, "iucv%u", &u) == 1) {
+      hd->sub_class.id = sc_nif_iucv;
+      hd->slot = u;
+    }
+    else if(sscanf(hd->unix_dev_name, "hsi%u", &u) == 1) {
+      hd->sub_class.id = sc_nif_hsi;
+      hd->slot = u;
+    }
+    else if(sscanf(hd->unix_dev_name, "qeth%u", &u) == 1) {
+      hd->sub_class.id = sc_nif_qeth;
+      hd->slot = u;
+    }
+    else if(sscanf(hd->unix_dev_name, "escon%u", &u) == 1) {
+      hd->sub_class.id = sc_nif_escon;
+      hd->slot = u;
+    }
+    else if(sscanf(hd->unix_dev_name, "myri%u", &u) == 1) {
+      hd->sub_class.id = sc_nif_myrinet;
+      hd->slot = u;
+    }
+    else if(sscanf(hd->unix_dev_name, "sit%u", &u) == 1) {
+      hd->sub_class.id = sc_nif_sit;   /* ipv6 over ipv4 tunnel */
+      hd->slot = u;
+    }
+    else if(sscanf(hd->unix_dev_name, "wlan%u", &u) == 1) {
+      hd->sub_class.id = sc_nif_wlan;
+      hd->slot = u;
+    }
+    else if(sscanf(hd->unix_dev_name, "xp%u", &u) == 1) {
+      hd->sub_class.id = sc_nif_xp;
+      hd->slot = u;
+    }
+    else if(sscanf(hd->unix_dev_name, "usb%u", &u) == 1) {
+      hd->sub_class.id = sc_nif_usb;
+      hd->slot = u;
+    }
+    /* ##### add more interface names here */
+
+    hd->bus.id = bus_none;
+
+    /* fix card type */
+    if(hd_card) {
+      if(
+        (hd_card->base_class.id == 0 && hd_card->sub_class.id == 0) ||
+        (hd_card->base_class.id == bc_network && hd_card->sub_class.id == 0x80)
+      ) {
+        switch(hd->sub_class.id) {
+          case sc_nif_ethernet:
+            hd_card->base_class.id = bc_network;
+            hd_card->sub_class.id = 0;
+            break;
+
+          case sc_nif_usb:
+            hd_card->base_class.id = bc_network;
+            hd_card->sub_class.id = 0x91;
+            break;
+        }
+      }
+    }
+  }
+
+  sysfs_close_class(sf_class);
+
+  if(hd_is_sgi_altix(hd_data)) add_xpnet(hd_data);
+  if(hd_is_iseries(hd_data)) add_iseries(hd_data);
+  add_uml(hd_data);
+
+  /* add link status info */
+  for(hd = hd_data->hd ; hd; hd = hd->next) {
+    if(
+      hd->module == hd_data->module &&
+      hd->base_class.id == bc_network_interface
+    ) {
+      get_linkstate(hd_data, hd);
+
+      if(!(hd_card = hd_get_device_by_idx(hd_data, hd->attached_to))) continue;
+
+      for(res = hd->res; res; res = res->next) {
+        if(res->any.type == res_link) break;
+      }
+
+      if(res) {
+        for(res1 = hd_card->res; res1; res1 = res1->next) {
+          if(res1->any.type == res_link) break;
+        }
+        if(res && !res1) {
+          res1 = new_mem(sizeof *res1);
+          res1->link.type = res_link;
+          res1->link.state = res->link.state;
+          add_res_entry(&hd_card->res, res1);
+        }
+      }
+    }
+  }
+}
+
+
+/*
+ * Get it the classical way, for drivers that don't support sysfs (veth).
+ */
+void get_driverinfo(hd_data_t *hd_data, hd_t *hd)
+{
+  int fd;
+  struct ethtool_drvinfo drvinfo = { cmd:ETHTOOL_GDRVINFO };
+  struct ifreq ifr;
+
+  if(!hd->unix_dev_name) return;
+
+  if(strlen(hd->unix_dev_name) > sizeof ifr.ifr_name - 1) return;
+
+  if((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return;
+
+  /* get driver info */
+  memset(&ifr, 0, sizeof ifr);
+  strcpy(ifr.ifr_name, hd->unix_dev_name);
+  ifr.ifr_data = (caddr_t) &drvinfo;
+  if(ioctl(fd, SIOCETHTOOL, &ifr) == 0) {
+    ADD2LOG("    ethtool driver: %s\n", drvinfo.driver);
+    ADD2LOG("    ethtool    bus: %s\n", drvinfo.bus_info);
+
+    add_str_list(&hd->drivers, drvinfo.driver);
+  }
+  else {
+    ADD2LOG("    GDRVINFO ethtool error: %s\n", strerror(errno));
+  }
+
+  close(fd);
+}
+
+
+/*
+ * Check network link status.
+ */
+void get_linkstate(hd_data_t *hd_data, hd_t *hd)
+{
+  int fd;
+  struct ethtool_value linkstatus = { cmd:ETHTOOL_GLINK };
+  struct ifreq ifr;
+  hd_res_t *res;
+
+  if(!hd->unix_dev_name) return;
+
+  if(strlen(hd->unix_dev_name) > sizeof ifr.ifr_name - 1) return;
+
+  if((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return;
+
+  /* get driver info */
+  memset(&ifr, 0, sizeof ifr);
+  strcpy(ifr.ifr_name, hd->unix_dev_name);
+  ifr.ifr_data = (caddr_t) &linkstatus;
+  if(ioctl(fd, SIOCETHTOOL, &ifr) == 0) {
+    ADD2LOG("  %s: ethtool link state: %d\n", hd->unix_dev_name, linkstatus.data);
+    res = new_mem(sizeof *res);
+    res->link.type = res_link;
+    res->link.state = linkstatus.data ? 1 : 0;
+    add_res_entry(&hd->res, res);
+  }
+  else {
+    ADD2LOG("  %s: GLINK ethtool error: %s\n", hd->unix_dev_name, strerror(errno));
+  }
+
+  close(fd);
+}
+
+
+/*
+ * SGI Altix cross partition network.
+ */
+void add_xpnet(hd_data_t *hd_data)
+{
+  hd_t *hd, *hd_card;
+  hd_res_t *res, *res2;
+
+  hd_card = add_hd_entry(hd_data, __LINE__, 0);
+  hd_card->base_class.id = bc_network;
+  hd_card->sub_class.id = 0x83;
+
+  hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x4002);
+  hd_card->device.id = MAKE_ID(TAG_SPECIAL, 1);
+
+  if(hd_module_is_active(hd_data, "xpnet")) {
+    add_str_list(&hd_card->drivers, "xpnet");
+  }
+
+  for(hd = hd_data->hd ; hd; hd = hd->next) {
+    if(
+      hd->module == hd_data->module &&
+      hd->base_class.id == bc_network_interface &&
+      hd->sub_class.id == sc_nif_xp
+    ) {
+      hd->attached_to = hd_card->idx;
+
+      for(res = hd->res; res; res = res->next) {
+        if(res->any.type == res_hwaddr) break;
+      }
+
+      if(res) {
+        res2 = new_mem(sizeof *res2);
+        res2->hwaddr.type = res_hwaddr;
+        res2->hwaddr.addr = new_str(res->hwaddr.addr);
+        add_res_entry(&hd_card->res, res2);
+      }
+
+      break;
+    }
+  }
+}
+
+
+/*
+ * iSeries veth devices.
+ */
+void add_iseries(hd_data_t *hd_data)
+{
+  hd_t *hd, *hd_card;
+  hd_res_t *res, *res2;
+  unsigned i, cardmask = 0, card_cnt = 0;
+  str_list_t *sl0, *sl;
+
+  for(hd = hd_data->hd ; hd; hd = hd->next) {
+    if(
+      hd->module == hd_data->module &&
+      hd->base_class.id == bc_network_interface &&
+      search_str_list(hd->drivers, "veth")
+    ) {
+      hd_card = add_hd_entry(hd_data, __LINE__, 0);
+      hd_card->base_class.id = bc_network;
+      hd_card->sub_class.id = 0x00;
+      hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x6001);       // IBM
+      hd_card->device.id = MAKE_ID(TAG_SPECIAL, 0x1000);
+      add_str_list(&hd_card->drivers, "iseries_veth");
+      hd_card->slot = card_cnt++;
+      str_printf(&hd_card->device.name, 0, "Virtual Ethernet card");
+      hd->attached_to = hd_card->idx;
+
+      for(res = hd->res; res; res = res->next) {
+        if(res->any.type == res_hwaddr) break;
+      }
+
+      if(res) {
+        unsigned int slotno;
+
+        res2 = new_mem(sizeof *res2);
+        res2->hwaddr.type = res_hwaddr;
+        res2->hwaddr.addr = new_str(res->hwaddr.addr);
+        add_res_entry(&hd_card->res, res2);
+        if (sscanf(res->hwaddr.addr, "02:01:ff:%x:ff:", &slotno)) {
+          hd_card->slot = slotno;
+          str_printf(&hd_card->device.name, 0, "Virtual Ethernet card %d", hd_card->slot);
+       }
+      }
+    }
+  }
+
+  if(!card_cnt) {
+    sl0 = read_file("/proc/iSeries/config", 0, 0);
+    for(sl = sl0; sl; sl = sl->next) {
+      if(sscanf(sl->str, "AVAILABLE_VETH=%x", &cardmask) == 1)
+        break;
+    }
+    free_str_list(sl0);
+
+    for (i = 0; i < 16; i++) {
+      if ((0x8000 >> i) & cardmask) {
+       hd_card = add_hd_entry(hd_data, __LINE__, 0);
+       hd_card->base_class.id = bc_network;
+       hd_card->sub_class.id = 0x00;
+       hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x6001);      // IBM
+       hd_card->device.id = MAKE_ID(TAG_SPECIAL, 0x1000);
+       hd_card->slot = i;
+       str_printf(&hd_card->device.name, 0, "Virtual Ethernet card %d", i);
+      }
+    }
+  }
+}
+
+
+/*
+ * UML veth devices.
+ */
+void add_uml(hd_data_t *hd_data)
+{
+  hd_t *hd, *hd_card;
+  hd_res_t *res, *res2;
+  unsigned card_cnt = 0;
+
+  for(hd = hd_data->hd ; hd; hd = hd->next) {
+    if(
+      hd->module == hd_data->module &&
+      hd->base_class.id == bc_network_interface &&
+      search_str_list(hd->drivers, "uml virtual ethernet")
+    ) {
+      hd_card = add_hd_entry(hd_data, __LINE__, 0);
+      hd_card->base_class.id = bc_network;
+      hd_card->sub_class.id = 0x00;
+      hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x6010);       // UML
+      hd_card->device.id = MAKE_ID(TAG_SPECIAL, 0x0001);
+      hd_card->slot = card_cnt++;
+      str_printf(&hd_card->device.name, 0, "Virtual Ethernet card %d", hd_card->slot);
+//      add_str_list(&hd_card->drivers, "veth");
+
+      hd->attached_to = hd_card->idx;
+
+      for(res = hd->res; res; res = res->next) {
+        if(res->any.type == res_hwaddr) break;
+      }
+
+      if(res) {
+        res2 = new_mem(sizeof *res2);
+        res2->hwaddr.type = res_hwaddr;
+        res2->hwaddr.addr = new_str(res->hwaddr.addr);
+        add_res_entry(&hd_card->res, res2);
+      }
+    }
+  }
+}
+
+