]>
Commit | Line | Data |
---|---|---|
4b292b55 | 1 | /* -*- mode: c; c-file-style: "openbsd" -*- */ |
6e75df87 VB |
2 | /* |
3 | * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> | |
4 | * | |
51434125 | 5 | * Permission to use, copy, modify, and/or distribute this software for any |
6e75df87 VB |
6 | * purpose with or without fee is hereby granted, provided that the above |
7 | * copyright notice and this permission notice appear in all copies. | |
8 | * | |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | */ | |
17 | ||
18 | #define INCLUDE_LINUX_IF_H | |
19 | #include "lldpd.h" | |
20 | ||
21 | #include <stdio.h> | |
06db3608 | 22 | #include <stddef.h> |
6e75df87 VB |
23 | #include <unistd.h> |
24 | #include <errno.h> | |
e6b36c87 | 25 | #include <assert.h> |
6e75df87 VB |
26 | #include <sys/ioctl.h> |
27 | #include <sys/types.h> | |
28 | #include <sys/stat.h> | |
29 | #include <fcntl.h> | |
30 | #include <fnmatch.h> | |
31 | #include <arpa/inet.h> | |
6e75df87 VB |
32 | #include <net/if_arp.h> |
33 | #include <linux/if_vlan.h> | |
34 | #include <linux/if_bonding.h> | |
35 | #include <linux/if_bridge.h> | |
36 | #include <linux/wireless.h> | |
37 | #include <linux/sockios.h> | |
38 | #include <linux/filter.h> | |
6e75df87 VB |
39 | #include <linux/if_packet.h> |
40 | ||
41 | #define SYSFS_PATH_MAX 256 | |
42 | #define MAX_PORTS 1024 | |
43 | #define MAX_BRIDGES 1024 | |
44 | ||
45 | /* BPF filter to get revelant information from interfaces */ | |
46 | /* LLDP: "ether proto 0x88cc and ether dst 01:80:c2:00:00:0e" */ | |
47 | /* FDP: "ether dst 01:e0:52:cc:cc:cc" */ | |
48 | /* CDP: "ether dst 01:00:0c:cc:cc:cc" */ | |
49 | /* SONMP: "ether dst 01:00:81:00:01:00" */ | |
50 | /* EDP: "ether dst 00:e0:2b:00:00:00" */ | |
a2606cac VB |
51 | /* For optimization purpose, we first check if the first bit of the |
52 | first byte is 1. if not, this can only be an EDP packet: | |
53 | ||
54 | tcpdump -dd "(ether[0] & 1 = 1 and | |
55 | ((ether proto 0x88cc and ether dst 01:80:c2:00:00:0e) or | |
56 | (ether dst 01:e0:52:cc:cc:cc) or | |
57 | (ether dst 01:00:0c:cc:cc:cc) or | |
58 | (ether dst 01:00:81:00:01:00))) or | |
59 | (ether dst 00:e0:2b:00:00:00)" | |
60 | */ | |
61 | ||
62 | #define LLDPD_FILTER_F \ | |
63 | { 0x30, 0, 0, 0x00000000 }, \ | |
64 | { 0x54, 0, 0, 0x00000001 }, \ | |
65 | { 0x15, 0, 14, 0x00000001 }, \ | |
66 | { 0x28, 0, 0, 0x0000000c }, \ | |
67 | { 0x15, 0, 4, 0x000088cc }, \ | |
68 | { 0x20, 0, 0, 0x00000002 }, \ | |
69 | { 0x15, 0, 2, 0xc200000e }, \ | |
70 | { 0x28, 0, 0, 0x00000000 }, \ | |
71 | { 0x15, 12, 13, 0x00000180 }, \ | |
72 | { 0x20, 0, 0, 0x00000002 }, \ | |
73 | { 0x15, 0, 2, 0x52cccccc }, \ | |
74 | { 0x28, 0, 0, 0x00000000 }, \ | |
75 | { 0x15, 8, 9, 0x000001e0 }, \ | |
76 | { 0x15, 1, 0, 0x0ccccccc }, \ | |
77 | { 0x15, 0, 2, 0x81000100 }, \ | |
78 | { 0x28, 0, 0, 0x00000000 }, \ | |
79 | { 0x15, 4, 5, 0x00000100 }, \ | |
80 | { 0x20, 0, 0, 0x00000002 }, \ | |
81 | { 0x15, 0, 3, 0x2b000000 }, \ | |
82 | { 0x28, 0, 0, 0x00000000 }, \ | |
83 | { 0x15, 0, 1, 0x000000e0 }, \ | |
84 | { 0x6, 0, 0, 0x0000ffff }, \ | |
6e75df87 | 85 | { 0x6, 0, 0, 0x00000000 }, |
a2606cac | 86 | |
6e75df87 VB |
87 | static struct sock_filter lldpd_filter_f[] = { LLDPD_FILTER_F }; |
88 | ||
89 | /* net/if.h */ | |
90 | extern unsigned int if_nametoindex (__const char *__ifname) __THROW; | |
91 | extern char *if_indextoname (unsigned int __ifindex, char *__ifname) __THROW; | |
92 | ||
31ee070d VB |
93 | struct lldpd_ops eth_ops; |
94 | struct lldpd_ops bond_ops; | |
6e75df87 | 95 | |
5339e725 | 96 | static int |
d4e4c804 | 97 | pattern_match(char *iface, char *list, int found) |
5339e725 VB |
98 | { |
99 | char *interfaces = NULL; | |
100 | char *pattern; | |
5339e725 VB |
101 | |
102 | if ((interfaces = strdup(list)) == NULL) { | |
103 | LLOG_WARNX("unable to allocate memory"); | |
104 | return 0; | |
105 | } | |
106 | ||
107 | for (pattern = strtok(interfaces, ","); | |
108 | pattern != NULL; | |
109 | pattern = strtok(NULL, ",")) { | |
110 | if ((pattern[0] == '!') && | |
111 | ((fnmatch(pattern + 1, iface, 0) == 0))) { | |
112 | /* Blacklisted. No need to search further. */ | |
113 | found = 0; | |
114 | break; | |
115 | } | |
116 | if (fnmatch(pattern, iface, 0) == 0) | |
117 | found = 1; | |
118 | } | |
119 | ||
120 | free(interfaces); | |
121 | return found; | |
122 | } | |
123 | ||
6e75df87 VB |
124 | static int |
125 | old_iface_is_bridge(struct lldpd *cfg, const char *name) | |
126 | { | |
127 | int ifindices[MAX_BRIDGES]; | |
128 | char ifname[IFNAMSIZ]; | |
129 | int num, i; | |
130 | unsigned long args[3] = { BRCTL_GET_BRIDGES, | |
131 | (unsigned long)ifindices, MAX_BRIDGES }; | |
b9f4c120 VB |
132 | if ((num = ioctl(cfg->g_sock, SIOCGIFBR, args)) < 0) |
133 | /* This can happen with a 64bit kernel and 32bit | |
134 | userland, don't output anything about this to avoid | |
135 | to fill logs. */ | |
6e75df87 | 136 | return 0; |
6e75df87 VB |
137 | for (i = 0; i < num; i++) { |
138 | if (if_indextoname(ifindices[i], ifname) == NULL) | |
139 | LLOG_INFO("unable to get name of interface %d", | |
140 | ifindices[i]); | |
141 | else if (strncmp(name, ifname, IFNAMSIZ) == 0) | |
142 | return 1; | |
143 | } | |
144 | return 0; | |
145 | } | |
146 | ||
147 | static int | |
148 | iface_is_bridge(struct lldpd *cfg, const char *name) | |
149 | { | |
f4ed5af1 | 150 | #ifdef SYSFS_BRIDGE_FDB |
6e75df87 VB |
151 | char path[SYSFS_PATH_MAX]; |
152 | int f; | |
153 | ||
154 | if ((snprintf(path, SYSFS_PATH_MAX, | |
155 | SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB, name)) >= SYSFS_PATH_MAX) | |
156 | LLOG_WARNX("path truncated"); | |
157 | if ((f = priv_open(path)) < 0) { | |
158 | return old_iface_is_bridge(cfg, name); | |
159 | } | |
160 | close(f); | |
161 | return 1; | |
f4ed5af1 VB |
162 | #else |
163 | return old_iface_is_bridge(cfg, name); | |
164 | #endif | |
6e75df87 VB |
165 | } |
166 | ||
7a008075 | 167 | #ifdef ENABLE_DOT1 |
6e75df87 VB |
168 | static int |
169 | old_iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master) | |
170 | { | |
171 | int j, index = if_nametoindex(slave); | |
172 | int ifptindices[MAX_PORTS]; | |
173 | unsigned long args2[4] = { BRCTL_GET_PORT_LIST, | |
174 | (unsigned long)ifptindices, MAX_PORTS, 0 }; | |
175 | struct ifreq ifr; | |
176 | ||
177 | strncpy(ifr.ifr_name, master, IFNAMSIZ); | |
178 | memset(ifptindices, 0, sizeof(ifptindices)); | |
179 | ifr.ifr_data = (char *)&args2; | |
180 | ||
b9f4c120 VB |
181 | if (ioctl(cfg->g_sock, SIOCDEVPRIVATE, &ifr) < 0) |
182 | /* This can happen with a 64bit kernel and 32bit | |
183 | userland, don't output anything about this to avoid | |
184 | to fill logs. */ | |
6e75df87 | 185 | return 0; |
6e75df87 VB |
186 | |
187 | for (j = 0; j < MAX_PORTS; j++) { | |
188 | if (ifptindices[j] == index) | |
189 | return 1; | |
190 | } | |
191 | ||
192 | return 0; | |
193 | } | |
194 | ||
195 | static int | |
196 | iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master) | |
197 | { | |
f4ed5af1 | 198 | #ifdef SYSFS_BRIDGE_PORT_SUBDIR |
6e75df87 VB |
199 | char path[SYSFS_PATH_MAX]; |
200 | int f; | |
201 | ||
202 | /* Master should be a bridge, first */ | |
203 | if (!iface_is_bridge(cfg, master)) return 0; | |
204 | ||
205 | if (snprintf(path, SYSFS_PATH_MAX, | |
206 | SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_PORT_SUBDIR "/%s/port_no", | |
207 | master, slave) >= SYSFS_PATH_MAX) | |
208 | LLOG_WARNX("path truncated"); | |
209 | if ((f = priv_open(path)) < 0) { | |
210 | return old_iface_is_bridged_to(cfg, slave, master); | |
211 | } | |
212 | close(f); | |
213 | return 1; | |
f4ed5af1 VB |
214 | #else |
215 | return old_iface_is_bridged_to(cfg, slave, master); | |
216 | #endif | |
6e75df87 | 217 | } |
7a008075 | 218 | #endif |
6e75df87 VB |
219 | |
220 | static int | |
221 | iface_is_vlan(struct lldpd *cfg, const char *name) | |
222 | { | |
223 | struct vlan_ioctl_args ifv; | |
224 | memset(&ifv, 0, sizeof(ifv)); | |
225 | ifv.cmd = GET_VLAN_REALDEV_NAME_CMD; | |
226 | if ((strlcpy(ifv.device1, name, sizeof(ifv.device1))) >= | |
227 | sizeof(ifv.device1)) | |
228 | LLOG_WARNX("device name truncated"); | |
229 | if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) | |
230 | return 1; | |
231 | return 0; | |
232 | } | |
233 | ||
234 | static int | |
235 | iface_is_wireless(struct lldpd *cfg, const char *name) | |
236 | { | |
237 | struct iwreq iwr; | |
238 | strlcpy(iwr.ifr_name, name, IFNAMSIZ); | |
239 | if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0) | |
240 | return 1; | |
241 | return 0; | |
242 | } | |
243 | ||
244 | static int | |
245 | iface_is_bond(struct lldpd *cfg, const char *name) | |
246 | { | |
247 | struct ifreq ifr; | |
248 | struct ifbond ifb; | |
249 | memset(&ifr, 0, sizeof(ifr)); | |
250 | memset(&ifb, 0, sizeof(ifb)); | |
251 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
8f88ff70 | 252 | ifr.ifr_data = (char *)&ifb; |
6e75df87 VB |
253 | if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) |
254 | return 1; | |
255 | return 0; | |
256 | } | |
257 | ||
258 | static int | |
259 | iface_is_bond_slave(struct lldpd *cfg, const char *slave, const char *master, | |
260 | int *active) | |
261 | { | |
262 | struct ifreq ifr; | |
263 | struct ifbond ifb; | |
264 | struct ifslave ifs; | |
265 | memset(&ifr, 0, sizeof(ifr)); | |
266 | memset(&ifb, 0, sizeof(ifb)); | |
267 | strlcpy(ifr.ifr_name, master, sizeof(ifr.ifr_name)); | |
8f88ff70 | 268 | ifr.ifr_data = (char *)&ifb; |
6e75df87 VB |
269 | if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) { |
270 | while (ifb.num_slaves--) { | |
271 | memset(&ifr, 0, sizeof(ifr)); | |
272 | memset(&ifs, 0, sizeof(ifs)); | |
273 | strlcpy(ifr.ifr_name, master, sizeof(ifr.ifr_name)); | |
8f88ff70 | 274 | ifr.ifr_data = (char *)&ifs; |
6e75df87 VB |
275 | ifs.slave_id = ifb.num_slaves; |
276 | if ((ioctl(cfg->g_sock, SIOCBONDSLAVEINFOQUERY, &ifr) >= 0) && | |
277 | (strncmp(ifs.slave_name, slave, sizeof(ifs.slave_name)) == 0)) { | |
278 | if (active) | |
279 | *active = ifs.state; | |
280 | return 1; | |
281 | } | |
282 | } | |
283 | } | |
284 | return 0; | |
285 | } | |
286 | ||
287 | static int | |
288 | iface_is_enslaved(struct lldpd *cfg, const char *name) | |
289 | { | |
290 | struct ifaddrs *ifap, *ifa; | |
291 | int master; | |
292 | ||
293 | if (getifaddrs(&ifap) != 0) { | |
294 | LLOG_WARN("unable to get interface list"); | |
295 | return -1; | |
296 | } | |
297 | for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { | |
298 | if (iface_is_bond_slave(cfg, name, ifa->ifa_name, NULL)) { | |
299 | master = if_nametoindex(ifa->ifa_name); | |
300 | freeifaddrs(ifap); | |
301 | return master; | |
302 | } | |
303 | } | |
304 | freeifaddrs(ifap); | |
305 | return -1; | |
306 | } | |
307 | ||
6e75df87 VB |
308 | static void |
309 | iface_get_permanent_mac(struct lldpd *cfg, struct lldpd_hardware *hardware) | |
310 | { | |
311 | int master, f, state = 0; | |
312 | FILE *netbond; | |
313 | const char *slaveif = "Slave Interface: "; | |
314 | const char *hwaddr = "Permanent HW addr: "; | |
315 | u_int8_t mac[ETHER_ADDR_LEN]; | |
316 | char bond[IFNAMSIZ]; | |
317 | char path[SYSFS_PATH_MAX]; | |
318 | char line[100]; | |
319 | if ((master = iface_is_enslaved(cfg, hardware->h_ifname)) == -1) | |
320 | return; | |
321 | /* We have a bond, we need to query it to get real MAC addresses */ | |
322 | if ((if_indextoname(master, bond)) == NULL) { | |
323 | LLOG_WARNX("unable to get bond name"); | |
324 | return; | |
325 | } | |
326 | ||
327 | if (snprintf(path, SYSFS_PATH_MAX, "/proc/net/bonding/%s", | |
328 | bond) >= SYSFS_PATH_MAX) { | |
329 | LLOG_WARNX("path truncated"); | |
330 | return; | |
331 | } | |
332 | if ((f = priv_open(path)) < 0) { | |
333 | if (snprintf(path, SYSFS_PATH_MAX, "/proc/self/net/bonding/%s", | |
334 | bond) >= SYSFS_PATH_MAX) { | |
335 | LLOG_WARNX("path truncated"); | |
336 | return; | |
337 | } | |
338 | f = priv_open(path); | |
339 | } | |
340 | if (f < 0) { | |
341 | LLOG_WARNX("unable to find %s in /proc/net/bonding or /proc/self/net/bonding", | |
342 | bond); | |
343 | return; | |
344 | } | |
345 | if ((netbond = fdopen(f, "r")) == NULL) { | |
346 | LLOG_WARN("unable to read stream from %s", path); | |
347 | close(f); | |
348 | return; | |
349 | } | |
350 | /* State 0: | |
351 | We parse the file to search "Slave Interface: ". If found, go to | |
352 | state 1. | |
353 | State 1: | |
354 | We parse the file to search "Permanent HW addr: ". If found, we get | |
355 | the mac. | |
356 | */ | |
357 | while (fgets(line, sizeof(line), netbond)) { | |
358 | switch (state) { | |
359 | case 0: | |
360 | if (strncmp(line, slaveif, strlen(slaveif)) == 0) { | |
361 | if (line[strlen(line)-1] == '\n') | |
362 | line[strlen(line)-1] = '\0'; | |
363 | if (strncmp(hardware->h_ifname, | |
364 | line + strlen(slaveif), | |
365 | sizeof(hardware->h_ifname)) == 0) | |
366 | state++; | |
367 | } | |
368 | break; | |
369 | case 1: | |
370 | if (strncmp(line, hwaddr, strlen(hwaddr)) == 0) { | |
371 | if (line[strlen(line)-1] == '\n') | |
372 | line[strlen(line)-1] = '\0'; | |
373 | if (sscanf(line + strlen(hwaddr), | |
374 | "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", | |
375 | &mac[0], &mac[1], &mac[2], | |
376 | &mac[3], &mac[4], &mac[5]) != | |
377 | ETHER_ADDR_LEN) { | |
378 | LLOG_WARN("unable to parse %s", | |
379 | line + strlen(hwaddr)); | |
380 | fclose(netbond); | |
381 | return; | |
382 | } | |
383 | memcpy(hardware->h_lladdr, mac, | |
384 | ETHER_ADDR_LEN); | |
385 | fclose(netbond); | |
386 | return; | |
387 | } | |
388 | break; | |
389 | } | |
390 | } | |
391 | LLOG_WARNX("unable to find real mac address for %s", | |
392 | bond); | |
393 | fclose(netbond); | |
394 | } | |
395 | ||
849954d7 VB |
396 | /* Generic minimal checks to handle a given interface. */ |
397 | static int | |
398 | iface_minimal_checks(struct lldpd *cfg, struct ifaddrs *ifa) | |
399 | { | |
06db3608 | 400 | struct sockaddr_ll sdl; |
58fe6128 | 401 | struct ifreq ifr; |
10935633 VB |
402 | struct ethtool_drvinfo ethc; |
403 | const char * const *rif; | |
404 | ||
405 | /* White-list some drivers */ | |
406 | const char * const regular_interfaces[] = { | |
407 | "dsa", | |
408 | "veth", | |
409 | NULL | |
410 | }; | |
411 | ||
a0aa43e9 | 412 | int is_bridge = iface_is_bridge(cfg, ifa->ifa_name); |
849954d7 VB |
413 | |
414 | if (!(LOCAL_CHASSIS(cfg)->c_cap_enabled & LLDP_CAP_BRIDGE) && | |
a0aa43e9 | 415 | is_bridge) { |
849954d7 VB |
416 | LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE; |
417 | return 0; | |
418 | } | |
419 | ||
420 | if (!(LOCAL_CHASSIS(cfg)->c_cap_enabled & LLDP_CAP_WLAN) && | |
421 | iface_is_wireless(cfg, ifa->ifa_name)) | |
422 | LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN; | |
423 | ||
424 | /* First, check if this interface has already been handled */ | |
425 | if (!ifa->ifa_flags) | |
426 | return 0; | |
427 | ||
428 | if (ifa->ifa_addr == NULL || | |
429 | ifa->ifa_addr->sa_family != PF_PACKET) | |
430 | return 0; | |
431 | ||
06db3608 VB |
432 | memcpy(&sdl, ifa->ifa_addr, sizeof(struct sockaddr_ll)); |
433 | if (sdl.sll_hatype != ARPHRD_ETHER || !sdl.sll_halen) | |
849954d7 VB |
434 | return 0; |
435 | ||
436 | /* We request that the interface is able to do either multicast | |
437 | * or broadcast to be able to send discovery frames. */ | |
438 | if (!(ifa->ifa_flags & (IFF_MULTICAST|IFF_BROADCAST))) | |
439 | return 0; | |
440 | ||
10935633 VB |
441 | /* Check if the driver is whitelisted */ |
442 | memset(&ifr, 0, sizeof(ifr)); | |
443 | strcpy(ifr.ifr_name, ifa->ifa_name); | |
3f0d19bb | 444 | memset(ðc, 0, sizeof(ethc)); |
10935633 VB |
445 | ifr.ifr_data = (caddr_t) ðc; |
446 | ethc.cmd = ETHTOOL_GDRVINFO; | |
447 | if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) { | |
448 | for (rif = regular_interfaces; *rif; rif++) { | |
449 | if (strcmp(ethc.driver, *rif) == 0) { | |
450 | /* White listed! */ | |
10935633 VB |
451 | return 1; |
452 | } | |
453 | } | |
454 | } | |
455 | ||
58fe6128 VB |
456 | /* Check queue len. If no queue, this usually means that this |
457 | is not a "real" interface. */ | |
458 | memset(&ifr, 0, sizeof(ifr)); | |
459 | strcpy(ifr.ifr_name, ifa->ifa_name); | |
460 | if ((ioctl(cfg->g_sock, SIOCGIFTXQLEN, &ifr) < 0) || !ifr.ifr_qlen) | |
461 | return 0; | |
462 | ||
a0aa43e9 | 463 | /* Don't handle bond and VLAN, nor bridge */ |
849954d7 | 464 | if ((iface_is_vlan(cfg, ifa->ifa_name)) || |
a0aa43e9 V |
465 | (iface_is_bond(cfg, ifa->ifa_name)) || |
466 | is_bridge) | |
849954d7 VB |
467 | return 0; |
468 | ||
469 | return 1; | |
470 | } | |
471 | ||
472 | static int | |
6a2fa591 | 473 | iface_set_filter(const char *name, int fd) |
849954d7 | 474 | { |
3f0d19bb VB |
475 | struct sock_fprog prog; |
476 | memset(&prog, 0, sizeof(struct sock_fprog)); | |
477 | prog.filter = lldpd_filter_f; | |
478 | prog.len = sizeof(lldpd_filter_f) / sizeof(struct sock_filter); | |
479 | ||
849954d7 VB |
480 | if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, |
481 | &prog, sizeof(prog)) < 0) { | |
6a2fa591 | 482 | LLOG_WARN("unable to change filter for %s", name); |
849954d7 VB |
483 | return ENETDOWN; |
484 | } | |
485 | return 0; | |
486 | } | |
487 | ||
f8cb50fa | 488 | /* Fill up port name and description */ |
849954d7 | 489 | static void |
36bd79e3 | 490 | iface_port_name_desc(struct lldpd_hardware *hardware) |
849954d7 VB |
491 | { |
492 | struct lldpd_port *port = &hardware->h_lport; | |
36bd79e3 VB |
493 | char buffer[256]; /* 256 = IFALIASZ */ |
494 | char path[SYSFS_PATH_MAX]; | |
495 | int f; | |
496 | ||
497 | /* There are two cases: | |
498 | ||
499 | 1. We have a kernel recent enough to support ifAlias | |
500 | _and_ a non empty ifAlias, then we will use it for | |
501 | description and use ifname for port ID. | |
502 | ||
503 | 2. Otherwise, we will use the MAC address as ID and the | |
504 | port name in description. | |
505 | */ | |
506 | ||
507 | if ((snprintf(path, SYSFS_PATH_MAX, | |
508 | SYSFS_CLASS_NET "%s/ifalias", hardware->h_ifname)) >= SYSFS_PATH_MAX) | |
509 | LLOG_WARNX("path truncated"); | |
510 | memset(buffer, 0, sizeof(buffer)); | |
511 | if (((f = priv_open(path)) < 0) || (read(f, buffer, sizeof(buffer)-1) < 1)) { | |
512 | /* Case 2: MAC address and port name */ | |
513 | close(f); | |
514 | port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR; | |
515 | if ((port->p_id = | |
516 | calloc(1, sizeof(hardware->h_lladdr))) == NULL) | |
517 | fatal(NULL); | |
518 | memcpy(port->p_id, hardware->h_lladdr, | |
519 | sizeof(hardware->h_lladdr)); | |
520 | port->p_id_len = sizeof(hardware->h_lladdr); | |
521 | port->p_descr = strdup(hardware->h_ifname); | |
522 | return; | |
523 | } | |
524 | /* Case 1: port name and port description */ | |
525 | close(f); | |
526 | port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME; | |
527 | port->p_id_len = strlen(hardware->h_ifname); | |
528 | if ((port->p_id = | |
529 | calloc(1, port->p_id_len)) == NULL) | |
849954d7 | 530 | fatal(NULL); |
36bd79e3 VB |
531 | memcpy(port->p_id, hardware->h_ifname, port->p_id_len); |
532 | if (buffer[strlen(buffer) - 1] == '\n') | |
533 | buffer[strlen(buffer) - 1] = '\0'; | |
534 | port->p_descr = strdup(buffer); | |
849954d7 VB |
535 | } |
536 | ||
6e75df87 VB |
537 | /* Fill up MAC/PHY for a given hardware port */ |
538 | static void | |
539 | iface_macphy(struct lldpd_hardware *hardware) | |
540 | { | |
541 | #ifdef ENABLE_DOT3 | |
542 | struct ethtool_cmd ethc; | |
543 | struct lldpd_port *port = &hardware->h_lport; | |
544 | int j; | |
545 | int advertised_ethtool_to_rfc3636[][2] = { | |
546 | {ADVERTISED_10baseT_Half, LLDP_DOT3_LINK_AUTONEG_10BASE_T}, | |
547 | {ADVERTISED_10baseT_Full, LLDP_DOT3_LINK_AUTONEG_10BASET_FD}, | |
548 | {ADVERTISED_100baseT_Half, LLDP_DOT3_LINK_AUTONEG_100BASE_TX}, | |
549 | {ADVERTISED_100baseT_Full, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD}, | |
550 | {ADVERTISED_1000baseT_Half, LLDP_DOT3_LINK_AUTONEG_1000BASE_T}, | |
551 | {ADVERTISED_1000baseT_Full, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD}, | |
552 | {ADVERTISED_10000baseT_Full, LLDP_DOT3_LINK_AUTONEG_OTHER}, | |
553 | {ADVERTISED_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE}, | |
554 | {ADVERTISED_Asym_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE}, | |
555 | {ADVERTISED_2500baseX_Full, LLDP_DOT3_LINK_AUTONEG_OTHER}, | |
556 | {0,0}}; | |
557 | ||
558 | if (priv_ethtool(hardware->h_ifname, ðc) == 0) { | |
3fd015c0 VB |
559 | port->p_macphy.autoneg_support = (ethc.supported & SUPPORTED_Autoneg) ? 1 : 0; |
560 | port->p_macphy.autoneg_enabled = (ethc.autoneg == AUTONEG_DISABLE) ? 0 : 1; | |
6e75df87 VB |
561 | for (j=0; advertised_ethtool_to_rfc3636[j][0]; j++) { |
562 | if (ethc.advertising & advertised_ethtool_to_rfc3636[j][0]) | |
3fd015c0 | 563 | port->p_macphy.autoneg_advertised |= |
6e75df87 VB |
564 | advertised_ethtool_to_rfc3636[j][1]; |
565 | } | |
566 | switch (ethc.speed) { | |
567 | case SPEED_10: | |
3fd015c0 | 568 | port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \ |
6e75df87 | 569 | LLDP_DOT3_MAU_10BASETFD : LLDP_DOT3_MAU_10BASETHD; |
3fd015c0 | 570 | if (ethc.port == PORT_BNC) port->p_macphy.mau_type = LLDP_DOT3_MAU_10BASE2; |
6e75df87 | 571 | if (ethc.port == PORT_FIBRE) |
3fd015c0 | 572 | port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \ |
6e75df87 VB |
573 | LLDP_DOT3_MAU_10BASEFLDF : LLDP_DOT3_MAU_10BASEFLHD; |
574 | break; | |
575 | case SPEED_100: | |
3fd015c0 | 576 | port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \ |
6e75df87 VB |
577 | LLDP_DOT3_MAU_100BASETXFD : LLDP_DOT3_MAU_100BASETXHD; |
578 | if (ethc.port == PORT_BNC) | |
3fd015c0 | 579 | port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \ |
6e75df87 VB |
580 | LLDP_DOT3_MAU_100BASET2DF : LLDP_DOT3_MAU_100BASET2HD; |
581 | if (ethc.port == PORT_FIBRE) | |
3fd015c0 | 582 | port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \ |
6e75df87 VB |
583 | LLDP_DOT3_MAU_100BASEFXFD : LLDP_DOT3_MAU_100BASEFXHD; |
584 | break; | |
585 | case SPEED_1000: | |
3fd015c0 | 586 | port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \ |
6e75df87 VB |
587 | LLDP_DOT3_MAU_1000BASETFD : LLDP_DOT3_MAU_1000BASETHD; |
588 | if (ethc.port == PORT_FIBRE) | |
3fd015c0 | 589 | port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \ |
6e75df87 VB |
590 | LLDP_DOT3_MAU_1000BASEXFD : LLDP_DOT3_MAU_1000BASEXHD; |
591 | break; | |
592 | case SPEED_10000: | |
3fd015c0 | 593 | port->p_macphy.mau_type = (ethc.port == PORT_FIBRE) ? \ |
6e75df87 VB |
594 | LLDP_DOT3_MAU_10GIGBASEX : LLDP_DOT3_MAU_10GIGBASER; |
595 | break; | |
596 | } | |
3fd015c0 | 597 | if (ethc.port == PORT_AUI) port->p_macphy.mau_type = LLDP_DOT3_MAU_AUI; |
6e75df87 VB |
598 | } |
599 | #endif | |
600 | } | |
601 | ||
602 | static void | |
603 | iface_mtu(struct lldpd *cfg, struct lldpd_hardware *hardware) | |
604 | { | |
605 | struct ifreq ifr; | |
606 | ||
607 | /* get MTU */ | |
608 | memset(&ifr, 0, sizeof(ifr)); | |
609 | strlcpy(ifr.ifr_name, hardware->h_ifname, sizeof(ifr.ifr_name)); | |
610 | if (ioctl(cfg->g_sock, SIOCGIFMTU, (char*)&ifr) == -1) { | |
611 | LLOG_WARN("unable to get MTU of %s, using 1500", hardware->h_ifname); | |
612 | hardware->h_mtu = 1500; | |
613 | } else | |
77c4d9d0 | 614 | hardware->h_mtu = ifr.ifr_mtu; |
6e75df87 VB |
615 | } |
616 | ||
617 | static void | |
618 | iface_multicast(struct lldpd *cfg, const char *name, int remove) | |
619 | { | |
620 | int i, rc; | |
621 | ||
622 | for (i=0; cfg->g_protocols[i].mode != 0; i++) { | |
623 | if (!cfg->g_protocols[i].enabled) continue; | |
624 | if ((rc = priv_iface_multicast(name, | |
625 | cfg->g_protocols[i].mac, !remove)) != 0) { | |
626 | errno = rc; | |
627 | if (errno != ENOENT) | |
628 | LLOG_INFO("unable to %s %s address to multicast filter for %s", | |
629 | (remove)?"delete":"add", | |
630 | cfg->g_protocols[i].name, | |
631 | name); | |
632 | } | |
633 | } | |
634 | } | |
635 | ||
636 | static int | |
637 | iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware) | |
638 | { | |
849954d7 | 639 | int fd, status; |
6e75df87 | 640 | |
849954d7 VB |
641 | if ((fd = priv_iface_init(hardware->h_ifname)) == -1) |
642 | return -1; | |
d6e889b6 | 643 | hardware->h_sendfd = fd; /* Send */ |
6e75df87 VB |
644 | |
645 | /* Set filter */ | |
6a2fa591 | 646 | if ((status = iface_set_filter(hardware->h_ifname, fd)) != 0) { |
849954d7 VB |
647 | close(fd); |
648 | return status; | |
6e75df87 VB |
649 | } |
650 | ||
651 | iface_multicast(cfg, hardware->h_ifname, 0); | |
652 | ||
d6e889b6 | 653 | levent_hardware_add_fd(hardware, fd); /* Receive */ |
6e75df87 | 654 | LLOG_DEBUG("interface %s initialized (fd=%d)", hardware->h_ifname, |
849954d7 | 655 | fd); |
6e75df87 VB |
656 | return 0; |
657 | } | |
658 | ||
659 | static int | |
660 | iface_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware, | |
661 | char *buffer, size_t size) | |
662 | { | |
663 | return write(hardware->h_sendfd, | |
664 | buffer, size); | |
665 | } | |
666 | ||
667 | static int | |
668 | iface_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, | |
669 | int fd, char *buffer, size_t size) | |
670 | { | |
671 | int n; | |
672 | struct sockaddr_ll from; | |
673 | socklen_t fromlen; | |
674 | ||
675 | fromlen = sizeof(from); | |
676 | if ((n = recvfrom(fd, | |
677 | buffer, | |
678 | size, 0, | |
679 | (struct sockaddr *)&from, | |
680 | &fromlen)) == -1) { | |
681 | LLOG_WARN("error while receiving frame on %s", | |
682 | hardware->h_ifname); | |
683 | hardware->h_rx_discarded_cnt++; | |
684 | return -1; | |
685 | } | |
686 | if (from.sll_pkttype == PACKET_OUTGOING) | |
687 | return -1; | |
688 | return n; | |
689 | } | |
690 | ||
691 | static int | |
692 | iface_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware) | |
693 | { | |
6e75df87 VB |
694 | iface_multicast(cfg, hardware->h_ifname, 1); |
695 | return 0; | |
696 | } | |
697 | ||
698 | void | |
699 | lldpd_ifh_eth(struct lldpd *cfg, struct ifaddrs *ifap) | |
700 | { | |
701 | struct ifaddrs *ifa; | |
6e75df87 | 702 | struct lldpd_hardware *hardware; |
6e75df87 VB |
703 | |
704 | for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { | |
849954d7 | 705 | if (!iface_minimal_checks(cfg, ifa)) |
6e75df87 VB |
706 | continue; |
707 | ||
44002d66 VB |
708 | if ((hardware = lldpd_get_hardware(cfg, |
709 | ifa->ifa_name, | |
710 | if_nametoindex(ifa->ifa_name), | |
711 | ð_ops)) == NULL) { | |
6e75df87 VB |
712 | if ((hardware = lldpd_alloc_hardware(cfg, |
713 | ifa->ifa_name)) == NULL) { | |
714 | LLOG_WARNX("Unable to allocate space for %s", | |
715 | ifa->ifa_name); | |
716 | continue; | |
717 | } | |
718 | if (iface_eth_init(cfg, hardware) != 0) { | |
719 | LLOG_WARN("unable to initialize %s", hardware->h_ifname); | |
720 | lldpd_hardware_cleanup(cfg, hardware); | |
721 | continue; | |
722 | } | |
723 | hardware->h_ops = ð_ops; | |
44002d66 | 724 | hardware->h_ifindex = if_nametoindex(ifa->ifa_name); |
6e75df87 | 725 | TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries); |
cfe00f7f VB |
726 | } else { |
727 | if (hardware->h_flags) continue; /* Already seen this time */ | |
4b292b55 | 728 | lldpd_port_cleanup(&hardware->h_lport, 0); |
cfe00f7f | 729 | } |
6e75df87 | 730 | |
6e75df87 VB |
731 | hardware->h_flags = ifa->ifa_flags; /* Should be non-zero */ |
732 | ifa->ifa_flags = 0; /* Future handlers | |
733 | don't have to | |
734 | care about this | |
735 | interface. */ | |
736 | ||
737 | /* Get local address */ | |
06db3608 VB |
738 | memcpy(&hardware->h_lladdr, |
739 | (u_int8_t*)((u_int8_t*)ifa->ifa_addr + | |
740 | offsetof(struct sockaddr_ll, sll_addr)), | |
741 | sizeof(hardware->h_lladdr)); | |
6e75df87 | 742 | |
36bd79e3 VB |
743 | /* Fill information about port */ |
744 | iface_port_name_desc(hardware); | |
6e75df87 VB |
745 | |
746 | /* Fill additional info */ | |
747 | iface_macphy(hardware); | |
748 | iface_mtu(cfg, hardware); | |
749 | } | |
750 | } | |
751 | ||
ba85f9f4 VB |
752 | void |
753 | lldpd_ifh_whitelist(struct lldpd *cfg, struct ifaddrs *ifap) | |
754 | { | |
755 | struct ifaddrs *ifa; | |
ba85f9f4 | 756 | |
8ec333bd | 757 | if (!cfg->g_config.c_iface_pattern) |
ba85f9f4 | 758 | return; |
ba85f9f4 VB |
759 | |
760 | for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { | |
761 | if (ifa->ifa_flags == 0) continue; /* Already handled by someone else */ | |
8ec333bd | 762 | if (!pattern_match(ifa->ifa_name, cfg->g_config.c_iface_pattern, 0)) { |
ba85f9f4 VB |
763 | /* This interface was not found. We flag it. */ |
764 | LLOG_DEBUG("blacklist %s", ifa->ifa_name); | |
765 | ifa->ifa_flags = 0; | |
766 | } | |
767 | } | |
ba85f9f4 VB |
768 | } |
769 | ||
849954d7 VB |
770 | static int |
771 | iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware) | |
772 | { | |
773 | char *mastername = (char *)hardware->h_data; /* Master name */ | |
774 | int fd, status; | |
775 | int un = 1; | |
776 | ||
777 | if (!mastername) return -1; | |
778 | ||
779 | /* First, we get a socket to the raw physical interface */ | |
780 | if ((fd = priv_iface_init(hardware->h_ifname)) == -1) | |
781 | return -1; | |
782 | hardware->h_sendfd = fd; | |
d6e889b6 | 783 | levent_hardware_add_fd(hardware, fd); |
6a2fa591 | 784 | if ((status = iface_set_filter(hardware->h_ifname, fd)) != 0) { |
849954d7 VB |
785 | close(fd); |
786 | return status; | |
787 | } | |
788 | iface_multicast(cfg, hardware->h_ifname, 0); | |
789 | ||
790 | /* Then, we open a raw interface for the master */ | |
791 | if ((fd = priv_iface_init(mastername)) == -1) { | |
792 | close(hardware->h_sendfd); | |
793 | return -1; | |
794 | } | |
6a2fa591 | 795 | if ((status = iface_set_filter(mastername, fd)) != 0) { |
849954d7 VB |
796 | close(hardware->h_sendfd); |
797 | close(fd); | |
798 | return status; | |
799 | } | |
800 | /* With bonding and older kernels (< 2.6.27) we need to listen | |
801 | * to bond device. We use setsockopt() PACKET_ORIGDEV to get | |
802 | * physical device instead of bond device (works with >= | |
803 | * 2.6.24). */ | |
804 | if (setsockopt(fd, SOL_PACKET, | |
805 | PACKET_ORIGDEV, &un, sizeof(un)) == -1) { | |
806 | LLOG_DEBUG("[priv]: unable to setsockopt for master bonding device of %s. " | |
807 | "You will get inaccurate results", | |
808 | hardware->h_ifname); | |
809 | } | |
810 | iface_multicast(cfg, mastername, 0); | |
811 | ||
d6e889b6 | 812 | levent_hardware_add_fd(hardware, fd); |
849954d7 VB |
813 | LLOG_DEBUG("interface %s initialized (fd=%d,master=%s[%d])", |
814 | hardware->h_ifname, | |
815 | hardware->h_sendfd, | |
816 | mastername, fd); | |
817 | return 0; | |
818 | } | |
819 | ||
820 | static int | |
821 | iface_bond_send(struct lldpd *cfg, struct lldpd_hardware *hardware, | |
822 | char *buffer, size_t size) | |
823 | { | |
824 | /* With bonds, we have duplicate MAC address on different physical | |
825 | * interfaces. We need to alter the source MAC address when we send on | |
826 | * an inactive slave. */ | |
827 | char *master = (char*)hardware->h_data; | |
828 | int active; | |
829 | if (!iface_is_bond_slave(cfg, hardware->h_ifname, master, &active)) { | |
830 | LLOG_WARNX("%s seems to not be enslaved anymore?", | |
831 | hardware->h_ifname); | |
832 | return 0; | |
833 | } | |
834 | if (active) { | |
835 | /* We need to modify the source MAC address */ | |
836 | if (size < 2 * ETH_ALEN) { | |
837 | LLOG_WARNX("packet to send on %s is too small!", | |
838 | hardware->h_ifname); | |
839 | return 0; | |
840 | } | |
841 | memset(buffer + ETH_ALEN, 0, ETH_ALEN); | |
842 | } | |
843 | return write(hardware->h_sendfd, | |
844 | buffer, size); | |
845 | } | |
846 | ||
847 | static int | |
848 | iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, | |
849 | int fd, char *buffer, size_t size) | |
850 | { | |
851 | int n; | |
852 | struct sockaddr_ll from; | |
853 | socklen_t fromlen; | |
854 | ||
855 | fromlen = sizeof(from); | |
856 | if ((n = recvfrom(fd, buffer, size, 0, | |
857 | (struct sockaddr *)&from, | |
858 | &fromlen)) == -1) { | |
859 | LLOG_WARN("error while receiving frame on %s", | |
860 | hardware->h_ifname); | |
861 | hardware->h_rx_discarded_cnt++; | |
862 | return -1; | |
863 | } | |
864 | if (from.sll_pkttype == PACKET_OUTGOING) | |
865 | return -1; | |
866 | if (fd == hardware->h_sendfd) | |
867 | /* We received this on the physical interface. */ | |
868 | return n; | |
869 | /* We received this on the bonding interface. Is it really for us? */ | |
44002d66 | 870 | if (from.sll_ifindex == hardware->h_ifindex) |
849954d7 VB |
871 | /* This is for us */ |
872 | return n; | |
873 | if (from.sll_ifindex == if_nametoindex((char*)hardware->h_data)) | |
874 | /* We don't know from which physical interface it comes (kernel | |
875 | * < 2.6.24). In doubt, this is for us. */ | |
876 | return n; | |
877 | return -1; /* Not for us */ | |
878 | } | |
879 | ||
880 | static int | |
881 | iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware) | |
882 | { | |
849954d7 VB |
883 | iface_multicast(cfg, hardware->h_ifname, 1); |
884 | iface_multicast(cfg, (char*)hardware->h_data, 1); | |
885 | free(hardware->h_data); | |
886 | return 0; | |
887 | } | |
888 | ||
889 | void | |
890 | lldpd_ifh_bond(struct lldpd *cfg, struct ifaddrs *ifap) | |
891 | { | |
892 | struct ifaddrs *ifa; | |
893 | struct lldpd_hardware *hardware; | |
849954d7 VB |
894 | int master; |
895 | for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { | |
896 | if (!iface_minimal_checks(cfg, ifa)) | |
897 | continue; | |
898 | if ((master = iface_is_enslaved(cfg, ifa->ifa_name)) == -1) | |
899 | continue; | |
900 | ||
44002d66 VB |
901 | if ((hardware = lldpd_get_hardware(cfg, |
902 | ifa->ifa_name, | |
903 | if_nametoindex(ifa->ifa_name), | |
904 | &bond_ops)) == NULL) { | |
849954d7 VB |
905 | if ((hardware = lldpd_alloc_hardware(cfg, |
906 | ifa->ifa_name)) == NULL) { | |
907 | LLOG_WARNX("Unable to allocate space for %s", | |
908 | ifa->ifa_name); | |
909 | continue; | |
910 | } | |
911 | hardware->h_data = (char *)calloc(1, IFNAMSIZ); | |
912 | if_indextoname(master, hardware->h_data); | |
913 | if (iface_bond_init(cfg, hardware) != 0) { | |
914 | LLOG_WARN("unable to initialize %s", | |
915 | hardware->h_ifname); | |
916 | lldpd_hardware_cleanup(cfg, hardware); | |
917 | continue; | |
918 | } | |
919 | hardware->h_ops = &bond_ops; | |
44002d66 | 920 | hardware->h_ifindex = if_nametoindex(ifa->ifa_name); |
849954d7 VB |
921 | TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries); |
922 | } else { | |
cfe00f7f | 923 | if (hardware->h_flags) continue; /* Already seen this time */ |
849954d7 VB |
924 | memset(hardware->h_data, 0, IFNAMSIZ); |
925 | if_indextoname(master, hardware->h_data); | |
4b292b55 | 926 | lldpd_port_cleanup(&hardware->h_lport, 0); |
849954d7 VB |
927 | } |
928 | ||
849954d7 VB |
929 | hardware->h_flags = ifa->ifa_flags; |
930 | ifa->ifa_flags = 0; | |
931 | ||
932 | /* Get local address */ | |
933 | iface_get_permanent_mac(cfg, hardware); | |
934 | ||
36bd79e3 VB |
935 | /* Fill information about port */ |
936 | iface_port_name_desc(hardware); | |
849954d7 VB |
937 | |
938 | /* Fill additional info */ | |
7a008075 | 939 | #ifdef ENABLE_DOT3 |
e49b274a | 940 | hardware->h_lport.p_aggregid = master; |
7a008075 | 941 | #endif |
849954d7 VB |
942 | iface_macphy(hardware); |
943 | iface_mtu(cfg, hardware); | |
944 | } | |
945 | } | |
946 | ||
5994b27d VB |
947 | #ifdef ENABLE_DOT1 |
948 | static void | |
949 | iface_append_vlan(struct lldpd *cfg, | |
950 | struct lldpd_hardware *hardware, struct ifaddrs *ifa) | |
951 | { | |
952 | struct lldpd_port *port = &hardware->h_lport; | |
953 | struct lldpd_vlan *vlan; | |
954 | struct vlan_ioctl_args ifv; | |
955 | ||
cfe00f7f VB |
956 | /* Check if the VLAN is already here. */ |
957 | TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) | |
958 | if (strncmp(ifa->ifa_name, vlan->v_name, IFNAMSIZ) == 0) | |
959 | return; | |
5994b27d VB |
960 | if ((vlan = (struct lldpd_vlan *) |
961 | calloc(1, sizeof(struct lldpd_vlan))) == NULL) | |
962 | return; | |
963 | if ((vlan->v_name = strdup(ifa->ifa_name)) == NULL) { | |
964 | free(vlan); | |
965 | return; | |
966 | } | |
967 | memset(&ifv, 0, sizeof(ifv)); | |
968 | ifv.cmd = GET_VLAN_VID_CMD; | |
969 | strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1)); | |
970 | if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) { | |
971 | /* Dunno what happened */ | |
972 | free(vlan->v_name); | |
973 | free(vlan); | |
6a2fa591 | 974 | return; |
5994b27d | 975 | } |
6a2fa591 VB |
976 | vlan->v_vid = ifv.u.VID; |
977 | TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries); | |
5994b27d VB |
978 | } |
979 | ||
6e75df87 VB |
980 | void |
981 | lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap) | |
982 | { | |
6e75df87 | 983 | struct ifaddrs *ifa; |
6e75df87 VB |
984 | struct vlan_ioctl_args ifv; |
985 | struct lldpd_hardware *hardware; | |
6a2fa591 | 986 | |
6e75df87 VB |
987 | for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { |
988 | if (!ifa->ifa_flags) | |
989 | continue; | |
990 | if (!iface_is_vlan(cfg, ifa->ifa_name)) | |
991 | continue; | |
992 | ||
993 | /* We need to find the physical interfaces of this | |
994 | vlan, through bonds and bridges. */ | |
995 | memset(&ifv, 0, sizeof(ifv)); | |
996 | ifv.cmd = GET_VLAN_REALDEV_NAME_CMD; | |
997 | strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1)); | |
998 | if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) { | |
999 | /* Three cases: | |
1000 | 1. we get a real device | |
1001 | 2. we get a bond | |
1002 | 3. we get a bridge | |
1003 | */ | |
1004 | if ((hardware = lldpd_get_hardware(cfg, | |
44002d66 VB |
1005 | ifv.u.device2, |
1006 | if_nametoindex(ifv.u.device2), | |
1007 | NULL)) == NULL) { | |
6e75df87 VB |
1008 | if (iface_is_bond(cfg, ifv.u.device2)) { |
1009 | TAILQ_FOREACH(hardware, &cfg->g_hardware, | |
1010 | h_entries) | |
1011 | if (iface_is_bond_slave(cfg, | |
1012 | hardware->h_ifname, | |
1013 | ifv.u.device2, NULL)) | |
5994b27d VB |
1014 | iface_append_vlan(cfg, |
1015 | hardware, ifa); | |
6e75df87 VB |
1016 | } else if (iface_is_bridge(cfg, ifv.u.device2)) { |
1017 | TAILQ_FOREACH(hardware, &cfg->g_hardware, | |
1018 | h_entries) | |
1019 | if (iface_is_bridged_to(cfg, | |
1020 | hardware->h_ifname, | |
1021 | ifv.u.device2)) | |
5994b27d VB |
1022 | iface_append_vlan(cfg, |
1023 | hardware, ifa); | |
6e75df87 | 1024 | } |
5994b27d VB |
1025 | } else iface_append_vlan(cfg, |
1026 | hardware, ifa); | |
6e75df87 VB |
1027 | } |
1028 | } | |
6e75df87 | 1029 | } |
5994b27d | 1030 | #endif |
6e75df87 | 1031 | |
e6b36c87 JV |
1032 | #ifndef IN_IS_ADDR_LOOPBACK |
1033 | #define IN_IS_ADDR_LOOPBACK(a) ((a)->s_addr == htonl(INADDR_LOOPBACK)) | |
1034 | #endif | |
1035 | #ifndef IN_IS_ADDR_GLOBAL | |
1036 | #define IN_IS_ADDR_GLOBAL(a) (!IN_IS_ADDR_LOOPBACK(a)) | |
1037 | #endif | |
1038 | #ifndef IN6_IS_ADDR_GLOBAL | |
1039 | #define IN6_IS_ADDR_GLOBAL(a) (!IN6_IS_ADDR_LOOPBACK(a) && \ | |
1040 | !IN6_IS_ADDR_LINKLOCAL(a)) | |
1041 | #endif | |
1042 | ||
6e75df87 VB |
1043 | /* Find a management address in all available interfaces, even those that were |
1044 | already handled. This is a special interface handler because it does not | |
1045 | really handle interface related information (management address is attached | |
1046 | to the local chassis). */ | |
1047 | void | |
1048 | lldpd_ifh_mgmt(struct lldpd *cfg, struct ifaddrs *ifap) | |
1049 | { | |
1050 | struct ifaddrs *ifa; | |
e6b36c87 | 1051 | char addrstrbuf[INET6_ADDRSTRLEN]; |
5fb27919 | 1052 | struct lldpd_mgmt *mgmt; |
e6b36c87 JV |
1053 | void *sin_addr_ptr; |
1054 | size_t sin_addr_size; | |
e6b36c87 | 1055 | int af; |
d4e4c804 | 1056 | int allnegative = 0; |
e6b36c87 | 1057 | |
5fb27919 | 1058 | lldpd_chassis_mgmt_cleanup(LOCAL_CHASSIS(cfg)); |
6e75df87 | 1059 | |
d4e4c804 | 1060 | /* Is the pattern provided all negative? */ |
8ec333bd VB |
1061 | if (cfg->g_config.c_mgmt_pattern == NULL) allnegative = 1; |
1062 | else if (cfg->g_config.c_mgmt_pattern[0] == '!') { | |
d4e4c804 VB |
1063 | /* If each comma is followed by '!', its an all |
1064 | negative pattern */ | |
8ec333bd | 1065 | char *sep = cfg->g_config.c_mgmt_pattern; |
d4e4c804 VB |
1066 | while ((sep = strchr(sep, ',')) && |
1067 | (*(++sep) == '!')); | |
1068 | if (sep == NULL) allnegative = 1; | |
1069 | } | |
1070 | ||
e6b36c87 JV |
1071 | /* Find management addresses */ |
1072 | for (af = LLDPD_AF_UNSPEC + 1; af != LLDPD_AF_LAST; af++) { | |
d4e4c804 VB |
1073 | /* We only take one of each address family, unless a |
1074 | pattern is provided and is not all negative. For | |
1075 | example !*:*,!10.* will only blacklist | |
1076 | addresses. We will pick the first IPv4 address not | |
1077 | matching 10.*. */ | |
1078 | for (ifa = ifap; | |
1079 | ifa != NULL; | |
1080 | ifa = ifa->ifa_next) { | |
e6b36c87 JV |
1081 | if (ifa->ifa_addr == NULL) |
1082 | continue; | |
1083 | if (ifa->ifa_addr->sa_family != lldpd_af(af)) | |
1084 | continue; | |
d4e4c804 | 1085 | |
e6b36c87 JV |
1086 | switch (af) { |
1087 | case LLDPD_AF_IPV4: | |
1088 | sin_addr_ptr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; | |
1089 | sin_addr_size = sizeof(struct in_addr); | |
1090 | if (!IN_IS_ADDR_GLOBAL((struct in_addr *)sin_addr_ptr)) | |
1091 | continue; | |
e6b36c87 JV |
1092 | break; |
1093 | case LLDPD_AF_IPV6: | |
1094 | sin_addr_ptr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; | |
1095 | sin_addr_size = sizeof(struct in6_addr); | |
1096 | if (!IN6_IS_ADDR_GLOBAL((struct in6_addr *)sin_addr_ptr)) | |
1097 | continue; | |
e6b36c87 JV |
1098 | break; |
1099 | default: | |
1100 | assert(0); | |
d4e4c804 | 1101 | continue; |
e6b36c87 | 1102 | } |
d4e4c804 VB |
1103 | if (inet_ntop(lldpd_af(af), sin_addr_ptr, |
1104 | addrstrbuf, sizeof(addrstrbuf)) == NULL) { | |
1105 | LLOG_WARN("unable to convert IP address to a string"); | |
1106 | continue; | |
1107 | } | |
8ec333bd VB |
1108 | if (cfg->g_config.c_mgmt_pattern == NULL || |
1109 | pattern_match(addrstrbuf, cfg->g_config.c_mgmt_pattern, allnegative)) { | |
e6b36c87 JV |
1110 | mgmt = lldpd_alloc_mgmt(af, sin_addr_ptr, sin_addr_size, |
1111 | if_nametoindex(ifa->ifa_name)); | |
1112 | if (mgmt == NULL) { | |
1113 | assert(errno == ENOMEM); /* anything else is a bug */ | |
1114 | LLOG_WARN("out of memory error"); | |
1115 | return; | |
6e75df87 | 1116 | } |
e6b36c87 | 1117 | TAILQ_INSERT_TAIL(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries); |
d4e4c804 VB |
1118 | |
1119 | /* Don't take additional address if the pattern is all negative. */ | |
1120 | if (allnegative) break; | |
6e75df87 VB |
1121 | } |
1122 | } | |
1123 | } | |
1124 | } | |
31ee070d | 1125 | |
e6b36c87 | 1126 | |
b4ac8083 VB |
1127 | /* Fill out chassis ID if not already done. This handler is special |
1128 | because we will only handle interfaces that are already handled. */ | |
1129 | void | |
1130 | lldpd_ifh_chassis(struct lldpd *cfg, struct ifaddrs *ifap) | |
1131 | { | |
1132 | struct ifaddrs *ifa; | |
1133 | struct lldpd_hardware *hardware; | |
5fd6695c | 1134 | char *name = NULL; |
b4ac8083 VB |
1135 | |
1136 | if (LOCAL_CHASSIS(cfg)->c_id != NULL && | |
1137 | LOCAL_CHASSIS(cfg)->c_id_subtype == LLDP_CHASSISID_SUBTYPE_LLADDR) | |
1138 | return; /* We already have one */ | |
1139 | ||
1140 | for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { | |
5339e725 | 1141 | if (ifa->ifa_flags) continue; |
8ec333bd VB |
1142 | if (cfg->g_config.c_cid_pattern && |
1143 | !pattern_match(ifa->ifa_name, cfg->g_config.c_cid_pattern, 0)) continue; | |
b4ac8083 VB |
1144 | |
1145 | if ((hardware = lldpd_get_hardware(cfg, | |
1146 | ifa->ifa_name, | |
1147 | if_nametoindex(ifa->ifa_name), | |
1148 | NULL)) == NULL) | |
1149 | /* That's odd. Let's skip. */ | |
1150 | continue; | |
1151 | ||
5fd6695c | 1152 | name = malloc(sizeof(hardware->h_lladdr)); |
b4ac8083 VB |
1153 | if (!name) { |
1154 | LLOG_WARN("Not enough memory for chassis ID"); | |
1155 | return; | |
1156 | } | |
1157 | free(LOCAL_CHASSIS(cfg)->c_id); | |
1158 | memcpy(name, hardware->h_lladdr, sizeof(hardware->h_lladdr)); | |
1159 | LOCAL_CHASSIS(cfg)->c_id = name; | |
1160 | LOCAL_CHASSIS(cfg)->c_id_len = sizeof(hardware->h_lladdr); | |
1161 | LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR; | |
1162 | return; | |
1163 | } | |
1164 | } | |
1165 | ||
31ee070d VB |
1166 | struct lldpd_ops eth_ops = { |
1167 | .send = iface_eth_send, | |
1168 | .recv = iface_eth_recv, | |
1169 | .cleanup = iface_eth_close, | |
1170 | }; | |
1171 | struct lldpd_ops bond_ops = { | |
1172 | .send = iface_bond_send, | |
1173 | .recv = iface_bond_recv, | |
1174 | .cleanup = iface_bond_close, | |
1175 | }; |