- check type, not name
[opensuse:hwinfo.git] / src / hd / net.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <inttypes.h>
8 #include <errno.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <sys/ioctl.h>
12 #include <sys/socket.h>
13
14 #define u8 uint8_t
15 #define u16 uint16_t
16 #define u32 uint32_t
17 #define u64 uint64_t
18 #include <linux/if.h>
19 #include <linux/sockios.h>
20 #include <linux/ethtool.h>
21 #include <linux/if_arp.h>
22
23 #include "hd.h"
24 #include "hd_int.h"
25 #include "net.h"
26
27 /**
28  * @defgroup NETint Network devices
29  * @ingroup libhdDEVint
30  * @brief Network device scan functions
31  *
32  * Gather network interface info
33  *
34  * @{
35  */
36
37 static void get_driverinfo(hd_data_t *hd_data, hd_t *hd);
38 static void get_linkstate(hd_data_t *hd_data, hd_t *hd);
39 static void add_xpnet(hd_data_t *hdata);
40 static void add_uml(hd_data_t *hdata);
41 static void add_kma(hd_data_t *hdata);
42 static void add_if_name(hd_t *hd_card, hd_t *hd);
43
44 /*
45  * This is independent of the other scans.
46  */
47
48 void hd_scan_net(hd_data_t *hd_data)
49 {
50   unsigned u;
51   int if_type, if_carrier;
52   hd_t *hd, *hd_card;
53   char *s, *t, *hw_addr;
54   hd_res_t *res, *res1, *res2;
55   uint64_t ul0;
56   str_list_t *sf_class, *sf_class_e;
57   char *sf_cdev = NULL, *sf_dev = NULL;
58   char *sf_drv_name, *sf_drv;
59
60   if(!hd_probe_feature(hd_data, pr_net)) return;
61
62   hd_data->module = mod_net;
63
64   /* some clean-up */
65   remove_hd_entries(hd_data);
66   hd_data->net = free_str_list(hd_data->net);
67
68   PROGRESS(1, 0, "get network data");
69
70   sf_class = read_dir("/sys/class/net", 'l');
71   if(!sf_class) sf_class = read_dir("/sys/class/net", 'd');
72
73   if(!sf_class) {
74     ADD2LOG("sysfs: no such class: net\n");
75     return;
76   }
77
78   for(sf_class_e = sf_class; sf_class_e; sf_class_e = sf_class_e->next) {
79     str_printf(&sf_cdev, 0, "/sys/class/net/%s", sf_class_e->str);
80
81     hd_card = NULL;
82
83     ADD2LOG(
84       "  net interface: name = %s, path = %s\n",
85       sf_class_e->str,
86       hd_sysfs_id(sf_cdev)
87     );
88
89     if_type = -1;
90     if(hd_attr_uint(get_sysfs_attr_by_path(sf_cdev, "type"), &ul0, 0)) {
91       if_type = ul0;
92       ADD2LOG("    type = %d\n", if_type);
93     }
94
95     if_carrier = -1;
96     if(hd_attr_uint(get_sysfs_attr_by_path(sf_cdev, "carrier"), &ul0, 0)) {
97       if_carrier = ul0;
98       ADD2LOG("    carrier = %d\n", if_carrier);
99     }
100
101     hw_addr = NULL;
102     if((s = get_sysfs_attr_by_path(sf_cdev, "address"))) {
103       hw_addr = canon_str(s, strlen(s));
104       ADD2LOG("    hw_addr = %s\n", hw_addr);
105     }
106
107     sf_dev = new_str(hd_read_sysfs_link(sf_cdev, "device"));
108     if(sf_dev) {
109       ADD2LOG("    net device: path = %s\n", hd_sysfs_id(sf_dev));
110     }
111
112     sf_drv_name = NULL;
113     sf_drv = hd_read_sysfs_link(sf_dev, "driver");
114     if(sf_drv) {
115       sf_drv_name = strrchr(sf_drv, '/');
116       if(sf_drv_name) sf_drv_name++;
117       ADD2LOG(
118         "    net driver: name = %s, path = %s\n",
119         sf_drv_name,
120         hd_sysfs_id(sf_drv)
121       );
122     }
123
124     hd = add_hd_entry(hd_data, __LINE__, 0);
125     hd->base_class.id = bc_network_interface;
126     hd->sub_class.id = sc_nif_other;
127
128     res1 = NULL;
129     if(hw_addr && strspn(hw_addr, "0:") != strlen(hw_addr)) {
130       res1 = new_mem(sizeof *res1);
131       res1->hwaddr.type = res_hwaddr;
132       res1->hwaddr.addr = new_str(hw_addr);
133       add_res_entry(&hd->res, res1);
134     }
135
136     res2 = NULL;
137     if(if_carrier >= 0) {
138       res = new_mem(sizeof *res);
139       res->link.type = res_link;
140       res->link.state = if_carrier ? 1 : 0;
141       add_res_entry(&hd->res, res);
142     }
143
144     hd->unix_dev_name = new_str(sf_class_e->str);
145     hd->sysfs_id = new_str(hd_sysfs_id(sf_cdev));
146
147     if(sf_drv_name) {
148       add_str_list(&hd->drivers, sf_drv_name);
149     }
150     else if(hd->res) {
151       get_driverinfo(hd_data, hd);
152     }
153
154     switch(if_type) {
155       case ARPHRD_ETHER:        /* eth */
156         hd->sub_class.id = sc_nif_ethernet;
157         break;
158       case ARPHRD_LOOPBACK:     /* lo */
159         hd->sub_class.id = sc_nif_loopback;
160         break;
161       case ARPHRD_SIT:          /* sit */
162         hd->sub_class.id = sc_nif_sit;
163         break;
164       case ARPHRD_FDDI:         /* fddi */
165         hd->sub_class.id = sc_nif_fddi;
166         break;
167       case ARPHRD_IEEE802_TR:   /* tr */
168         hd->sub_class.id = sc_nif_tokenring;
169         break;
170 #if 0
171       case ARPHRD_IEEE802:      /* fc */
172         hd->sub_class.id = sc_nif_fc;
173         break;
174 #endif
175       default:
176         hd->sub_class.id = sc_nif_other;
177     }
178
179     if(!strcmp(hd->unix_dev_name, "lo")) {
180       hd->sub_class.id = sc_nif_loopback;
181     }
182     else if(sscanf(hd->unix_dev_name, "eth%u", &u) == 1) {
183       hd->sub_class.id = sc_nif_ethernet;
184       hd->slot = u;
185     }
186     else if(sscanf(hd->unix_dev_name, "tr%u", &u) == 1) {
187       hd->sub_class.id = sc_nif_tokenring;
188       hd->slot = u;
189     }
190     else if(sscanf(hd->unix_dev_name, "fddi%u", &u) == 1) {
191       hd->sub_class.id = sc_nif_fddi;
192       hd->slot = u;
193     }
194     else if(sscanf(hd->unix_dev_name, "ctc%u", &u) == 1) {
195       hd->sub_class.id = sc_nif_ctc;
196       hd->slot = u;
197     }
198     else if(sscanf(hd->unix_dev_name, "iucv%u", &u) == 1) {
199       hd->sub_class.id = sc_nif_iucv;
200       hd->slot = u;
201     }
202     else if(sscanf(hd->unix_dev_name, "hsi%u", &u) == 1) {
203       hd->sub_class.id = sc_nif_hsi;
204       hd->slot = u;
205     }
206     else if(sscanf(hd->unix_dev_name, "qeth%u", &u) == 1) {
207       hd->sub_class.id = sc_nif_qeth;
208       hd->slot = u;
209     }
210     else if(sscanf(hd->unix_dev_name, "escon%u", &u) == 1) {
211       hd->sub_class.id = sc_nif_escon;
212       hd->slot = u;
213     }
214     else if(sscanf(hd->unix_dev_name, "myri%u", &u) == 1) {
215       hd->sub_class.id = sc_nif_myrinet;
216       hd->slot = u;
217     }
218     else if(sscanf(hd->unix_dev_name, "sit%u", &u) == 1) {
219       hd->sub_class.id = sc_nif_sit;    /* ipv6 over ipv4 tunnel */
220       hd->slot = u;
221     }
222     else if(sscanf(hd->unix_dev_name, "wlan%u", &u) == 1) {
223       hd->sub_class.id = sc_nif_wlan;
224       hd->slot = u;
225     }
226     else if(sscanf(hd->unix_dev_name, "xp%u", &u) == 1) {
227       hd->sub_class.id = sc_nif_xp;
228       hd->slot = u;
229     }
230     else if(sscanf(hd->unix_dev_name, "usb%u", &u) == 1) {
231       hd->sub_class.id = sc_nif_usb;
232       hd->slot = u;
233     }
234     /* ##### add more interface names here */
235     else {
236       for(s = hd->unix_dev_name; *s; s++) if(isdigit(*s)) break;
237       if(*s && (u = strtoul(s, &s, 10), !*s)) {
238         hd->slot = u;
239       }
240     }
241
242     hd->bus.id = bus_none;
243
244     hd_card = NULL;
245
246     if(sf_dev) {
247       s = new_str(hd_sysfs_id(sf_dev));
248
249       hd->sysfs_device_link = new_str(s);
250
251       hd_card = hd_find_sysfs_id(hd_data, s);
252
253       // try one above, if not found
254       if(!hd_card) {
255         t = strrchr(s, '/');
256         if(t) {
257           *t = 0;
258           hd_card = hd_find_sysfs_id(hd_data, s);
259         }
260       }
261
262       /* if one card has several interfaces (as with PS3), check interface names, too */
263       if(
264         hd_card &&
265         hd_card->unix_dev_name &&
266         hd->unix_dev_name &&
267         strcmp(hd->unix_dev_name, hd_card->unix_dev_name)
268       ) {
269         hd_card = hd_find_sysfs_id_devname(hd_data, s, hd->unix_dev_name);
270       }
271
272       s = free_mem(s);
273
274       if(hd_card) {
275         hd->attached_to = hd_card->idx;
276
277         /* for cards with strange pci classes */
278         hd_set_hw_class(hd_card, hw_network_ctrl);
279
280         /* add hw addr to network card */
281         if(res1) {
282           u = 0;
283           for(res = hd_card->res; res; res = res->next) {
284             if(
285               res->any.type == res_hwaddr &&
286               !strcmp(res->hwaddr.addr, res1->hwaddr.addr)
287             ) {
288               u = 1;
289               break;
290             }
291           }
292           if(!u) {
293             res = new_mem(sizeof *res);
294             res->hwaddr.type = res_hwaddr;
295             res->hwaddr.addr = new_str(res1->hwaddr.addr);
296             add_res_entry(&hd_card->res, res);
297           }
298         }
299         /*
300          * add interface names...
301          * but not wmasterX (bnc #441778)
302          */
303         if(if_type != 801) add_if_name(hd_card, hd);
304       }
305     }
306
307     if(!hd_card && hw_addr) {
308       /* try to find card based on hwaddr (for prom-based cards) */
309
310       for(hd_card = hd_data->hd; hd_card; hd_card = hd_card->next) {
311         if(
312           hd_card->base_class.id != bc_network ||
313           hd_card->sub_class.id != 0
314         ) continue;
315         for(res = hd_card->res; res; res = res->next) {
316           if(
317             res->any.type == res_hwaddr &&
318             !strcmp(hw_addr, res->hwaddr.addr)
319           ) break;
320         }
321         if(res) {
322           hd->attached_to = hd_card->idx;
323           break;
324         }
325       }
326     }
327
328     hw_addr = free_mem(hw_addr);
329
330     /* fix card type */
331     if(hd_card) {
332       if(
333         (hd_card->base_class.id == 0 && hd_card->sub_class.id == 0) ||
334         (hd_card->base_class.id == bc_network && hd_card->sub_class.id == 0x80)
335       ) {
336         switch(hd->sub_class.id) {
337           case sc_nif_ethernet:
338             hd_card->base_class.id = bc_network;
339             hd_card->sub_class.id = 0;
340             break;
341
342           case sc_nif_usb:
343             hd_card->base_class.id = bc_network;
344             hd_card->sub_class.id = 0x91;
345             break;
346         }
347       }
348     }
349
350     sf_dev = free_mem(sf_dev);
351   }
352
353   sf_cdev = free_mem(sf_cdev);
354   sf_class = free_str_list(sf_class);
355
356   if(hd_is_sgi_altix(hd_data)) add_xpnet(hd_data);
357   add_uml(hd_data);
358   add_kma(hd_data);
359
360   /* add link status info & dump eeprom */
361   for(hd = hd_data->hd ; hd; hd = hd->next) {
362     if(
363       hd->module == hd_data->module &&
364       hd->base_class.id == bc_network_interface
365     ) {
366       char *buf = NULL;
367       str_list_t *sl0, *sl;
368
369       if(hd_probe_feature(hd_data, pr_net_eeprom) && hd->unix_dev_name) {
370         PROGRESS(2, 0, "eeprom dump");
371
372         str_printf(&buf, 0, "|/usr/sbin/ethtool -e %s 2>/dev/null", hd->unix_dev_name);
373         if((sl0 = read_file(buf, 0, 0))) {
374           ADD2LOG("----- %s %s -----\n", hd->unix_dev_name, "EEPROM dump");
375           for(sl = sl0; sl; sl = sl->next) {
376             ADD2LOG("%s", sl->str);
377           }
378           ADD2LOG("----- %s end -----\n", "EEPROM dump");
379           free_str_list(sl0);
380         }
381         free(buf);
382       }
383
384       for(res = hd->res; res; res = res->next) {
385         if(res->any.type == res_link) break;
386       }
387
388       if(!res) get_linkstate(hd_data, hd);
389
390       if(!(hd_card = hd_get_device_by_idx(hd_data, hd->attached_to))) continue;
391
392       for(res = hd->res; res; res = res->next) {
393         if(res->any.type == res_link) break;
394       }
395
396       if(res) {
397         for(res1 = hd_card->res; res1; res1 = res1->next) {
398           if(res1->any.type == res_link) break;
399         }
400         if(res && !res1) {
401           res1 = new_mem(sizeof *res1);
402           res1->link.type = res_link;
403           res1->link.state = res->link.state;
404           add_res_entry(&hd_card->res, res1);
405         }
406       }
407     }
408   }
409 }
410
411
412 /*
413  * Get it the classical way, for drivers that don't support sysfs (veth).
414  */
415 void get_driverinfo(hd_data_t *hd_data, hd_t *hd)
416 {
417   int fd;
418   struct ethtool_drvinfo drvinfo = { cmd:ETHTOOL_GDRVINFO };
419   struct ifreq ifr;
420
421   if(!hd->unix_dev_name) return;
422
423   if(strlen(hd->unix_dev_name) > sizeof ifr.ifr_name - 1) return;
424
425   if((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return;
426
427   /* get driver info */
428   memset(&ifr, 0, sizeof ifr);
429   strcpy(ifr.ifr_name, hd->unix_dev_name);
430   ifr.ifr_data = (caddr_t) &drvinfo;
431   if(ioctl(fd, SIOCETHTOOL, &ifr) == 0) {
432     ADD2LOG("    ethtool driver: %s\n", drvinfo.driver);
433     ADD2LOG("    ethtool    bus: %s\n", drvinfo.bus_info);
434
435     add_str_list(&hd->drivers, drvinfo.driver);
436   }
437   else {
438     ADD2LOG("    GDRVINFO ethtool error: %s\n", strerror(errno));
439   }
440
441   close(fd);
442 }
443
444
445 /*
446  * Check network link status.
447  */
448 void get_linkstate(hd_data_t *hd_data, hd_t *hd)
449 {
450   int fd;
451   struct ethtool_value linkstatus = { cmd:ETHTOOL_GLINK };
452   struct ifreq ifr;
453   hd_res_t *res;
454
455   if(!hd->unix_dev_name) return;
456
457   if(strlen(hd->unix_dev_name) > sizeof ifr.ifr_name - 1) return;
458
459   if((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return;
460
461   /* get driver info */
462   memset(&ifr, 0, sizeof ifr);
463   strcpy(ifr.ifr_name, hd->unix_dev_name);
464   ifr.ifr_data = (caddr_t) &linkstatus;
465   if(ioctl(fd, SIOCETHTOOL, &ifr) == 0) {
466     ADD2LOG("  %s: ethtool link state: %d\n", hd->unix_dev_name, linkstatus.data);
467     res = new_mem(sizeof *res);
468     res->link.type = res_link;
469     res->link.state = linkstatus.data ? 1 : 0;
470     add_res_entry(&hd->res, res);
471   }
472   else {
473     ADD2LOG("  %s: GLINK ethtool error: %s\n", hd->unix_dev_name, strerror(errno));
474   }
475
476   close(fd);
477 }
478
479
480 /*
481  * SGI Altix cross partition network.
482  */
483 void add_xpnet(hd_data_t *hd_data)
484 {
485   hd_t *hd, *hd_card;
486   hd_res_t *res, *res2;
487
488   hd_card = add_hd_entry(hd_data, __LINE__, 0);
489   hd_card->base_class.id = bc_network;
490   hd_card->sub_class.id = 0x83;
491
492   hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x4002);
493   hd_card->device.id = MAKE_ID(TAG_SPECIAL, 1);
494
495   if(hd_module_is_active(hd_data, "xpnet")) {
496     add_str_list(&hd_card->drivers, "xpnet");
497   }
498
499   for(hd = hd_data->hd ; hd; hd = hd->next) {
500     if(
501       hd->module == hd_data->module &&
502       hd->base_class.id == bc_network_interface &&
503       hd->sub_class.id == sc_nif_xp
504     ) {
505       hd->attached_to = hd_card->idx;
506
507       for(res = hd->res; res; res = res->next) {
508         if(res->any.type == res_hwaddr) break;
509       }
510
511       if(res) {
512         res2 = new_mem(sizeof *res2);
513         res2->hwaddr.type = res_hwaddr;
514         res2->hwaddr.addr = new_str(res->hwaddr.addr);
515         add_res_entry(&hd_card->res, res2);
516       }
517
518       add_if_name(hd_card, hd);
519
520       break;
521     }
522   }
523 }
524
525
526
527
528
529
530 /*
531  * UML veth devices.
532  */
533 void add_uml(hd_data_t *hd_data)
534 {
535   hd_t *hd, *hd_card;
536   hd_res_t *res, *res2;
537   unsigned card_cnt = 0;
538
539   for(hd = hd_data->hd ; hd; hd = hd->next) {
540     if(
541       hd->module == hd_data->module &&
542       hd->base_class.id == bc_network_interface &&
543       search_str_list(hd->drivers, "uml virtual ethernet")
544     ) {
545       hd_card = add_hd_entry(hd_data, __LINE__, 0);
546       hd_card->base_class.id = bc_network;
547       hd_card->sub_class.id = 0x00;
548       hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x6010);        // UML
549       hd_card->device.id = MAKE_ID(TAG_SPECIAL, 0x0001);
550       hd_card->slot = card_cnt++;
551       str_printf(&hd_card->device.name, 0, "Virtual Ethernet card %d", hd_card->slot);
552
553       hd->attached_to = hd_card->idx;
554
555       for(res = hd->res; res; res = res->next) {
556         if(res->any.type == res_hwaddr) break;
557       }
558
559       if(res) {
560         res2 = new_mem(sizeof *res2);
561         res2->hwaddr.type = res_hwaddr;
562         res2->hwaddr.addr = new_str(res->hwaddr.addr);
563         add_res_entry(&hd_card->res, res2);
564       }
565
566       add_if_name(hd_card, hd);
567     }
568   }
569 }
570
571
572 /*
573  * KMA veth devices.
574  */
575 void add_kma(hd_data_t *hd_data)
576 {
577   hd_t *hd, *hd_card;
578   hd_res_t *res, *res2;
579   unsigned card_cnt = 0;
580
581   for(hd = hd_data->hd ; hd; hd = hd->next) {
582     if(
583       hd->module == hd_data->module &&
584       hd->base_class.id == bc_network_interface &&
585       search_str_list(hd->drivers, "kveth2")
586     ) {
587       hd_card = add_hd_entry(hd_data, __LINE__, 0);
588       hd_card->base_class.id = bc_network;
589       hd_card->sub_class.id = 0x00;
590       hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x6012);        // VirtualIron
591       hd_card->device.id = MAKE_ID(TAG_SPECIAL, 0x0001);
592       hd_card->slot = card_cnt++;
593       str_printf(&hd_card->device.name, 0, "Ethernet card %d", hd_card->slot);
594
595       hd->attached_to = hd_card->idx;
596
597       for(res = hd->res; res; res = res->next) {
598         if(res->any.type == res_hwaddr) break;
599       }
600
601       if(res) {
602         res2 = new_mem(sizeof *res2);
603         res2->hwaddr.type = res_hwaddr;
604         res2->hwaddr.addr = new_str(res->hwaddr.addr);
605         add_res_entry(&hd_card->res, res2);
606       }
607
608       add_if_name(hd_card, hd);
609     }
610   }
611 }
612
613
614 /*
615  * add interface name to card
616  */
617 void add_if_name(hd_t *hd_card, hd_t *hd)
618 {
619   str_list_t *sl0;
620
621   if(hd->unix_dev_name) {
622     if(!search_str_list(hd_card->unix_dev_names, hd->unix_dev_name)) {
623       if(hd->sub_class.id == sc_nif_other) {
624         /* add at end */
625         add_str_list(&hd_card->unix_dev_names, hd->unix_dev_name);
626       }
627       else {
628         /* add at top */
629         sl0 = new_mem(sizeof *sl0);
630         sl0->next = hd_card->unix_dev_names;
631         sl0->str = new_str(hd->unix_dev_name);
632         hd_card->unix_dev_names = sl0;
633       }
634       free_mem(hd_card->unix_dev_name);
635       hd_card->unix_dev_name = new_str(hd_card->unix_dev_names->str);
636     }
637   }
638 }
639
640 /** @} */
641