- work around x86-64 assembler weirdness
[opensuse:hwinfo.git] / src / hd / net.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <inttypes.h>
7 #include <errno.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <sys/ioctl.h>
11 #include <sys/socket.h>
12
13 #define u8 uint8_t
14 #define u16 uint16_t
15 #define u32 uint32_t
16 #define u64 uint64_t
17 #include <linux/if.h>
18 #include <linux/sockios.h>
19 #include <linux/ethtool.h>
20
21 #include "hd.h"
22 #include "hd_int.h"
23 #include "net.h"
24
25 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26  * gather network interface info
27  *
28  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
29  */
30
31 static void get_driverinfo(hd_data_t *hd_data, hd_t *hd);
32 static void get_linkstate(hd_data_t *hd_data, hd_t *hd);
33 static void add_xpnet(hd_data_t *hdata);
34 static void add_iseries(hd_data_t *hdata);
35 static void add_uml(hd_data_t *hdata);
36
37 /*
38  * This is independent of the other scans.
39  */
40
41 void hd_scan_net(hd_data_t *hd_data)
42 {
43   unsigned u;
44   hd_t *hd, *hd2, *hd_card;
45   char *s, *hw_addr;
46   hd_res_t *res, *res1;
47
48   struct sysfs_class *sf_class;
49   struct sysfs_class_device *sf_cdev;
50   struct sysfs_device *sf_dev;
51   struct sysfs_driver *sf_drv;
52   struct dlist *sf_cdev_list;
53
54   if(!hd_probe_feature(hd_data, pr_net)) return;
55
56   hd_data->module = mod_net;
57
58   /* some clean-up */
59   remove_hd_entries(hd_data);
60   hd_data->net = free_str_list(hd_data->net);
61
62   PROGRESS(1, 0, "get network data");
63
64   sf_class = sysfs_open_class("net");
65
66   if(!sf_class) {
67     ADD2LOG("sysfs: no such class: net\n");
68     return;
69   }
70
71   sf_cdev_list = sysfs_get_class_devices(sf_class);
72   if(sf_cdev_list) dlist_for_each_data(sf_cdev_list, sf_cdev, struct sysfs_class_device) {
73     ADD2LOG(
74       "  net interface: name = %s, classname = %s, path = %s\n",
75       sf_cdev->name,
76       sf_cdev->classname,
77       hd_sysfs_id(sf_cdev->path)
78     );
79
80     hw_addr = NULL;
81     if((s = hd_attr_str(sysfs_get_classdev_attr(sf_cdev, "address")))) {
82       hw_addr = canon_str(s, strlen(s));
83       ADD2LOG("    hw_addr = %s\n", hw_addr);
84     }
85
86     sf_dev = sysfs_get_classdev_device(sf_cdev);
87     if(sf_dev) {
88       ADD2LOG("    net device: path = %s\n", hd_sysfs_id(sf_dev->path));
89     }
90
91     sf_drv = sysfs_get_classdev_driver(sf_cdev);
92     if(sf_drv) {
93       ADD2LOG(
94         "    net driver: name = %s, path = %s\n",
95         sf_drv->name,
96         hd_sysfs_id(sf_drv->path)
97       );
98     }
99
100     hd = add_hd_entry(hd_data, __LINE__, 0);
101     hd->base_class.id = bc_network_interface;
102
103     res1 = NULL;
104     if(hw_addr && strspn(hw_addr, "0:") != strlen(hw_addr)) {
105       res1 = new_mem(sizeof *res1);
106       res1->hwaddr.type = res_hwaddr;
107       res1->hwaddr.addr = new_str(hw_addr);
108       add_res_entry(&hd->res, res1);
109     }
110
111     hw_addr = free_mem(hw_addr);
112
113     hd->unix_dev_name = new_str(sf_cdev->name);
114     hd->sysfs_id = new_str(hd_sysfs_id(sf_cdev->path));
115
116     if(sf_drv) {
117       add_str_list(&hd->drivers, sf_drv->name);
118     }
119     else if(hd->res) {
120       get_driverinfo(hd_data, hd);
121     }
122
123     if(sf_dev) {
124       hd->sysfs_device_link = new_str(hd_sysfs_id(sf_dev->path)); 
125
126       hd2 = hd_find_sysfs_id(hd_data, hd_sysfs_id(sf_dev->path));
127       if(hd2) {
128         hd->attached_to = hd2->idx;
129         /* add hw addr to network card */
130         if(res1) {
131           u = 0;
132           for(res = hd2->res; res; res = res->next) {
133             if(
134               res->any.type == res_hwaddr &&
135               !strcmp(res->hwaddr.addr, res1->hwaddr.addr)
136             ) {
137               u = 1;
138               break;
139             }
140           }
141           if(!u) {
142             res = new_mem(sizeof *res);
143             res->hwaddr.type = res_hwaddr;
144             res->hwaddr.addr = new_str(res1->hwaddr.addr);
145             add_res_entry(&hd2->res, res);
146           }
147         }
148       }
149     }
150
151     if(!strcmp(hd->unix_dev_name, "lo")) {
152       hd->sub_class.id = sc_nif_loopback;
153     }
154     else if(sscanf(hd->unix_dev_name, "eth%u", &u) == 1) {
155       hd->sub_class.id = sc_nif_ethernet;
156       hd->slot = u;
157     }
158     else if(sscanf(hd->unix_dev_name, "tr%u", &u) == 1) {
159       hd->sub_class.id = sc_nif_tokenring;
160       hd->slot = u;
161     }
162     else if(sscanf(hd->unix_dev_name, "fddi%u", &u) == 1) {
163       hd->sub_class.id = sc_nif_fddi;
164       hd->slot = u;
165     }
166     else if(sscanf(hd->unix_dev_name, "ctc%u", &u) == 1) {
167       hd->sub_class.id = sc_nif_ctc;
168       hd->slot = u;
169     }
170     else if(sscanf(hd->unix_dev_name, "iucv%u", &u) == 1) {
171       hd->sub_class.id = sc_nif_iucv;
172       hd->slot = u;
173     }
174     else if(sscanf(hd->unix_dev_name, "hsi%u", &u) == 1) {
175       hd->sub_class.id = sc_nif_hsi;
176       hd->slot = u;
177     }
178     else if(sscanf(hd->unix_dev_name, "qeth%u", &u) == 1) {
179       hd->sub_class.id = sc_nif_qeth;
180       hd->slot = u;
181     }
182     else if(sscanf(hd->unix_dev_name, "escon%u", &u) == 1) {
183       hd->sub_class.id = sc_nif_escon;
184       hd->slot = u;
185     }
186     else if(sscanf(hd->unix_dev_name, "myri%u", &u) == 1) {
187       hd->sub_class.id = sc_nif_myrinet;
188       hd->slot = u;
189     }
190     else if(sscanf(hd->unix_dev_name, "sit%u", &u) == 1) {
191       hd->sub_class.id = sc_nif_sit;    /* ipv6 over ipv4 tunnel */
192       hd->slot = u;
193     }
194     else if(sscanf(hd->unix_dev_name, "wlan%u", &u) == 1) {
195       hd->sub_class.id = sc_nif_wlan;
196       hd->slot = u;
197     }
198     else if(sscanf(hd->unix_dev_name, "xp%u", &u) == 1) {
199       hd->sub_class.id = sc_nif_xp;
200       hd->slot = u;
201     }
202     /* ##### add more interface names here */
203     else {
204       hd->sub_class.id = sc_nif_other;
205     }
206
207     hd->bus.id = bus_none;
208   }
209
210   sysfs_close_class(sf_class);
211
212   if(hd_is_sgi_altix(hd_data)) add_xpnet(hd_data);
213   if(hd_is_iseries(hd_data)) add_iseries(hd_data);
214   add_uml(hd_data);
215
216   /* add link status info */
217   for(hd = hd_data->hd ; hd; hd = hd->next) {
218     if(
219       hd->module == hd_data->module &&
220       hd->base_class.id == bc_network_interface
221     ) {
222       get_linkstate(hd_data, hd);
223
224       if(!(hd_card = hd_get_device_by_idx(hd_data, hd->attached_to))) continue;
225
226       for(res = hd->res; res; res = res->next) {
227         if(res->any.type == res_link) break;
228       }
229
230       if(res) {
231         for(res1 = hd_card->res; res1; res1 = res1->next) {
232           if(res1->any.type == res_link) break;
233         }
234         if(res && !res1) {
235           res1 = new_mem(sizeof *res1);
236           res1->link.type = res_link;
237           res1->link.state = res->link.state;
238           add_res_entry(&hd_card->res, res1);
239         }
240       }
241     }
242   }
243 }
244
245
246 /*
247  * Get it the classical way, for drivers that don't support sysfs (veth).
248  */
249 void get_driverinfo(hd_data_t *hd_data, hd_t *hd)
250 {
251   int fd;
252   struct ethtool_drvinfo drvinfo = { cmd:ETHTOOL_GDRVINFO };
253   struct ifreq ifr;
254
255   if(!hd->unix_dev_name) return;
256
257   if(strlen(hd->unix_dev_name) > sizeof ifr.ifr_name - 1) return;
258
259   if((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return;
260
261   /* get driver info */
262   memset(&ifr, 0, sizeof ifr);
263   strcpy(ifr.ifr_name, hd->unix_dev_name);
264   ifr.ifr_data = (caddr_t) &drvinfo;
265   if(ioctl(fd, SIOCETHTOOL, &ifr) == 0) {
266     ADD2LOG("    ethtool driver: %s\n", drvinfo.driver);
267     ADD2LOG("    ethtool    bus: %s\n", drvinfo.bus_info);
268
269     add_str_list(&hd->drivers, drvinfo.driver);
270   }
271   else {
272     ADD2LOG("    GDRVINFO ethtool error: %s\n", strerror(errno));
273   }
274
275   close(fd);
276 }
277
278
279 /*
280  * Check network link status.
281  */
282 void get_linkstate(hd_data_t *hd_data, hd_t *hd)
283 {
284   int fd;
285   struct ethtool_value linkstatus = { cmd:ETHTOOL_GLINK };
286   struct ifreq ifr;
287   hd_res_t *res;
288
289   if(!hd->unix_dev_name) return;
290
291   if(strlen(hd->unix_dev_name) > sizeof ifr.ifr_name - 1) return;
292
293   if((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return;
294
295   /* get driver info */
296   memset(&ifr, 0, sizeof ifr);
297   strcpy(ifr.ifr_name, hd->unix_dev_name);
298   ifr.ifr_data = (caddr_t) &linkstatus;
299   if(ioctl(fd, SIOCETHTOOL, &ifr) == 0) {
300     ADD2LOG("  %s: ethtool link state: %d\n", hd->unix_dev_name, linkstatus.data);
301     res = new_mem(sizeof *res);
302     res->link.type = res_link;
303     res->link.state = linkstatus.data ? 1 : 0;
304     add_res_entry(&hd->res, res);
305   }
306   else {
307     ADD2LOG("  %s: GLINK ethtool error: %s\n", hd->unix_dev_name, strerror(errno));
308   }
309
310   close(fd);
311 }
312
313
314 /*
315  * SGI Altix cross partition network.
316  */
317 void add_xpnet(hd_data_t *hd_data)
318 {
319   hd_t *hd, *hd_card;
320   hd_res_t *res, *res2;
321
322   hd_card = add_hd_entry(hd_data, __LINE__, 0);
323   hd_card->base_class.id = bc_network;
324   hd_card->sub_class.id = 0x83;
325
326   hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x4002);
327   hd_card->device.id = MAKE_ID(TAG_SPECIAL, 1);
328
329   if(hd_module_is_active(hd_data, "xpnet")) {
330     add_str_list(&hd_card->drivers, "xpnet");
331   }
332
333   for(hd = hd_data->hd ; hd; hd = hd->next) {
334     if(
335       hd->module == hd_data->module &&
336       hd->base_class.id == bc_network_interface &&
337       hd->sub_class.id == sc_nif_xp
338     ) {
339       hd->attached_to = hd_card->idx;
340
341       for(res = hd->res; res; res = res->next) {
342         if(res->any.type == res_hwaddr) break;
343       }
344
345       if(res) {
346         res2 = new_mem(sizeof *res2);
347         res2->hwaddr.type = res_hwaddr;
348         res2->hwaddr.addr = new_str(res->hwaddr.addr);
349         add_res_entry(&hd_card->res, res2);
350       }
351
352       break;
353     }
354   }
355 }
356
357
358 /*
359  * iSeries veth devices.
360  */
361 void add_iseries(hd_data_t *hd_data)
362 {
363   hd_t *hd, *hd_card;
364   hd_res_t *res, *res2;
365   unsigned i, cardmask = 0, card_cnt = 0;
366   str_list_t *sl0, *sl;
367
368   for(hd = hd_data->hd ; hd; hd = hd->next) {
369     if(
370       hd->module == hd_data->module &&
371       hd->base_class.id == bc_network_interface &&
372       search_str_list(hd->drivers, "veth")
373     ) {
374       hd_card = add_hd_entry(hd_data, __LINE__, 0);
375       hd_card->base_class.id = bc_network;
376       hd_card->sub_class.id = 0x00;
377       hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x6001);        // IBM
378       hd_card->device.id = MAKE_ID(TAG_SPECIAL, 0x1000);
379       add_str_list(&hd_card->drivers, "iseries_veth");
380       hd_card->slot = card_cnt++;
381       str_printf(&hd_card->device.name, 0, "Virtual Ethernet card");
382       hd->attached_to = hd_card->idx;
383
384       for(res = hd->res; res; res = res->next) {
385         if(res->any.type == res_hwaddr) break;
386       }
387
388       if(res) {
389         unsigned int slotno;
390
391         res2 = new_mem(sizeof *res2);
392         res2->hwaddr.type = res_hwaddr;
393         res2->hwaddr.addr = new_str(res->hwaddr.addr);
394         add_res_entry(&hd_card->res, res2);
395         if (sscanf(res->hwaddr.addr, "02:01:ff:%x:ff:", &slotno)) {
396           hd_card->slot = slotno;
397           str_printf(&hd_card->device.name, 0, "Virtual Ethernet card %d", hd_card->slot);
398         }
399       }
400     }
401   }
402
403   if(!card_cnt) {
404     sl0 = read_file("/proc/iSeries/config", 0, 0);
405     for(sl = sl0; sl; sl = sl->next) {
406       if(sscanf(sl->str, "AVAILABLE_VETH=%x", &cardmask) == 1)
407          break;
408     }
409     free_str_list(sl0);
410
411     for (i = 0; i < 16; i++) {
412       if ((0x8000 >> i) & cardmask) {
413         hd_card = add_hd_entry(hd_data, __LINE__, 0);
414         hd_card->base_class.id = bc_network;
415         hd_card->sub_class.id = 0x00;
416         hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x6001);      // IBM
417         hd_card->device.id = MAKE_ID(TAG_SPECIAL, 0x1000);
418         hd_card->slot = i;
419         str_printf(&hd_card->device.name, 0, "Virtual Ethernet card %d", i);
420       }
421     }
422   }
423 }
424
425
426 /*
427  * UML veth devices.
428  */
429 void add_uml(hd_data_t *hd_data)
430 {
431   hd_t *hd, *hd_card;
432   hd_res_t *res, *res2;
433   unsigned card_cnt = 0;
434
435   for(hd = hd_data->hd ; hd; hd = hd->next) {
436     if(
437       hd->module == hd_data->module &&
438       hd->base_class.id == bc_network_interface &&
439       search_str_list(hd->drivers, "uml virtual ethernet")
440     ) {
441       hd_card = add_hd_entry(hd_data, __LINE__, 0);
442       hd_card->base_class.id = bc_network;
443       hd_card->sub_class.id = 0x00;
444       hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x6010);        // UML
445       hd_card->device.id = MAKE_ID(TAG_SPECIAL, 0x0001);
446       hd_card->slot = card_cnt++;
447       str_printf(&hd_card->device.name, 0, "Virtual Ethernet card %d", hd_card->slot);
448 //      add_str_list(&hd_card->drivers, "veth");
449
450       hd->attached_to = hd_card->idx;
451
452       for(res = hd->res; res; res = res->next) {
453         if(res->any.type == res_hwaddr) break;
454       }
455
456       if(res) {
457         res2 = new_mem(sizeof *res2);
458         res2->hwaddr.type = res_hwaddr;
459         res2->hwaddr.addr = new_str(res->hwaddr.addr);
460         add_res_entry(&hd_card->res, res2);
461       }
462     }
463   }
464 }
465
466