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