1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
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 #include "lldpd.h"
19 #include "trace.h"
20
21 #include <stddef.h>
22 #include <unistd.h>
23 #include <errno.h>
24 #include <assert.h>
25 #include <arpa/inet.h>
26
27 static int
lldpd_af(int af)28 lldpd_af(int af)
29 {
30 switch (af) {
31 case LLDPD_AF_IPV4: return AF_INET;
32 case LLDPD_AF_IPV6: return AF_INET6;
33 case LLDPD_AF_LAST: return AF_MAX;
34 default: return AF_UNSPEC;
35 }
36 }
37
38 /* Generic ethernet interface initialization */
39 /**
40 * Enable multicast on the given interface.
41 */
42 void
interfaces_setup_multicast(struct lldpd * cfg,const char * name,int remove)43 interfaces_setup_multicast(struct lldpd *cfg, const char *name,
44 int remove)
45 {
46 int rc;
47 size_t i, j;
48 const u_int8_t *mac;
49 const u_int8_t zero[ETHER_ADDR_LEN] = {};
50
51 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
52 if (!cfg->g_protocols[i].enabled) continue;
53 for (mac = cfg->g_protocols[i].mac1, j = 0;
54 j < 3;
55 mac += ETHER_ADDR_LEN,
56 j++) {
57 if (memcmp(mac, zero, ETHER_ADDR_LEN) == 0) break;
58 if ((rc = priv_iface_multicast(name,
59 mac, !remove)) != 0) {
60 errno = rc;
61 if (errno != ENOENT)
62 log_debug("interfaces",
63 "unable to %s %s address to multicast filter for %s (%s)",
64 (remove)?"delete":"add",
65 cfg->g_protocols[i].name,
66 name, strerror(rc));
67 }
68 }
69 }
70 }
71
72 /**
73 * Free an interface.
74 *
75 * @param iff interface to be freed
76 */
77 void
interfaces_free_device(struct interfaces_device * iff)78 interfaces_free_device(struct interfaces_device *iff)
79 {
80 if (!iff) return;
81 free(iff->name);
82 free(iff->alias);
83 free(iff->address);
84 free(iff->driver);
85 free(iff);
86 }
87
88 /**
89 * Free a list of interfaces.
90 *
91 * @param ifs list of interfaces to be freed
92 */
93 void
interfaces_free_devices(struct interfaces_device_list * ifs)94 interfaces_free_devices(struct interfaces_device_list *ifs)
95 {
96 struct interfaces_device *iff, *iff_next;
97 if (!ifs) return;
98 for (iff = TAILQ_FIRST(ifs);
99 iff != NULL;
100 iff = iff_next) {
101 iff_next = TAILQ_NEXT(iff, next);
102 interfaces_free_device(iff);
103 }
104 free(ifs);
105 }
106
107 /**
108 * Free one address
109 *
110 * @param ifaddr Address to be freed
111 */
112 void
interfaces_free_address(struct interfaces_address * ifaddr)113 interfaces_free_address(struct interfaces_address *ifaddr)
114 {
115 free(ifaddr);
116 }
117
118 /**
119 * Free a list of addresses.
120 *
121 * @param ifaddrs list of addresses
122 */
123 void
interfaces_free_addresses(struct interfaces_address_list * ifaddrs)124 interfaces_free_addresses(struct interfaces_address_list *ifaddrs)
125 {
126 struct interfaces_address *ifa, *ifa_next;
127 if (!ifaddrs) return;
128 for (ifa = TAILQ_FIRST(ifaddrs);
129 ifa != NULL;
130 ifa = ifa_next) {
131 ifa_next = TAILQ_NEXT(ifa, next);
132 interfaces_free_address(ifa);
133 }
134 free(ifaddrs);
135 }
136
137 /**
138 * Find the appropriate interface from the name.
139 *
140 * @param interfaces List of available interfaces
141 * @param device Name of the device we search for
142 * @return The interface or NULL if not found
143 */
144 struct interfaces_device*
interfaces_nametointerface(struct interfaces_device_list * interfaces,const char * device)145 interfaces_nametointerface(struct interfaces_device_list *interfaces,
146 const char *device)
147 {
148 struct interfaces_device *iface;
149 TAILQ_FOREACH(iface, interfaces, next) {
150 if (!strncmp(iface->name, device, IFNAMSIZ))
151 return iface;
152 }
153 log_debug("interfaces", "cannot get interface for index %s",
154 device);
155 return NULL;
156 }
157
158 /**
159 * Find the appropriate interface from the index.
160 *
161 * @param interfaces List of available interfaces
162 * @param index Index of the device we search for
163 * @return The interface or NULL if not found
164 */
165 struct interfaces_device*
interfaces_indextointerface(struct interfaces_device_list * interfaces,int index)166 interfaces_indextointerface(struct interfaces_device_list *interfaces,
167 int index)
168 {
169 struct interfaces_device *iface;
170 TAILQ_FOREACH(iface, interfaces, next) {
171 if (iface->index == index)
172 return iface;
173 }
174 log_debug("interfaces", "cannot get interface for index %d",
175 index);
176 return NULL;
177 }
178
179 void
interfaces_helper_allowlist(struct lldpd * cfg,struct interfaces_device_list * interfaces)180 interfaces_helper_allowlist(struct lldpd *cfg,
181 struct interfaces_device_list *interfaces)
182 {
183 struct interfaces_device *iface;
184
185 if (!cfg->g_config.c_iface_pattern)
186 return;
187
188 TAILQ_FOREACH(iface, interfaces, next) {
189 int m = pattern_match(iface->name, cfg->g_config.c_iface_pattern, 0);
190 switch (m) {
191 case 0:
192 log_debug("interfaces", "deny %s", iface->name);
193 iface->ignore = 1;
194 continue;
195 case 2:
196 log_debug("interfaces", "allow %s (consider it as a physical interface)",
197 iface->name);
198 iface->type |= IFACE_PHYSICAL_T;
199 continue;
200 }
201 }
202 }
203
204 #ifdef ENABLE_DOT1
205 static void
iface_append_vlan(struct lldpd * cfg,struct interfaces_device * vlan,struct interfaces_device * lower)206 iface_append_vlan(struct lldpd *cfg,
207 struct interfaces_device *vlan,
208 struct interfaces_device *lower)
209 {
210 struct lldpd_hardware *hardware =
211 lldpd_get_hardware(cfg, lower->name, lower->index);
212 struct lldpd_port *port;
213 struct lldpd_vlan *v;
214 char *name = NULL;
215 uint16_t vlan_id;
216
217 if (hardware == NULL) {
218 log_debug("interfaces",
219 "cannot find real interface %s for VLAN %s",
220 lower->name, vlan->name);
221 return;
222 }
223 port = &hardware->h_lport;
224
225 for (int i = 0; (i < VLAN_BITMAP_LEN); i++) {
226 if (vlan->vlan_bmap[i] == 0)
227 continue;
228 for (unsigned bit = 0; bit < 32; bit++) {
229 uint32_t mask = 1L << bit;
230 if (!(vlan->vlan_bmap[i] & mask))
231 continue;
232 vlan_id = (i * 32) + bit;
233 if (asprintf(&name, "vlan%d", vlan_id) == -1)
234 return;
235
236 /* Check if the VLAN is already here. */
237 TAILQ_FOREACH(v, &port->p_vlans, v_entries)
238 if (strncmp(name, v->v_name, IFNAMSIZ) == 0) {
239 free(name);
240 return;
241 }
242
243 if ((v = (struct lldpd_vlan *)
244 calloc(1, sizeof(struct lldpd_vlan))) == NULL) {
245 free(name);
246 return;
247 }
248 v->v_name = name;
249 v->v_vid = vlan_id;
250 if (vlan->pvid)
251 port->p_pvid = vlan->pvid;
252 log_debug("interfaces", "append VLAN %s for %s",
253 v->v_name,
254 hardware->h_ifname);
255 TAILQ_INSERT_TAIL(&port->p_vlans, v, v_entries);
256 }
257 }
258 }
259
260 /**
261 * Append VLAN to the lowest possible interface.
262 *
263 * @param vlan The VLAN interface (used to get VLAN ID).
264 * @param upper The upper interface we are currently examining.
265 * @param depth Depth of the stack (avoid infinite recursion)
266 *
267 * Initially, upper == vlan. This function will be called recursively.
268 */
269 static void
iface_append_vlan_to_lower(struct lldpd * cfg,struct interfaces_device_list * interfaces,struct interfaces_device * vlan,struct interfaces_device * upper,int depth)270 iface_append_vlan_to_lower(struct lldpd *cfg,
271 struct interfaces_device_list *interfaces,
272 struct interfaces_device *vlan,
273 struct interfaces_device *upper,
274 int depth)
275 {
276 if (depth > 5) {
277 log_warnx("interfaces",
278 "BUG: maximum depth reached when applying VLAN %s (loop?)",
279 vlan->name);
280 return;
281 }
282 depth++;
283 struct interfaces_device *lower;
284 log_debug("interfaces",
285 "looking to apply VLAN %s to physical interface behind %s",
286 vlan->name, upper->name);
287
288 /* Some bridges managed VLAN internally, skip them. */
289 if (upper->type & IFACE_BRIDGE_VLAN_T) {
290 log_debug("interfaces", "VLAN %s ignored for VLAN-aware bridge interface %s",
291 vlan->name, upper->name);
292 return;
293 }
294
295 /* Easy: check if we have a lower interface. */
296 if (upper->lower) {
297 log_debug("interfaces", "VLAN %s on lower interface %s",
298 vlan->name, upper->name);
299 iface_append_vlan_to_lower(cfg,
300 interfaces, vlan,
301 upper->lower,
302 depth);
303 return;
304 }
305
306 /* Other easy case, we have a physical interface. */
307 if (upper->type & IFACE_PHYSICAL_T) {
308 log_debug("interfaces", "VLAN %s on physical interface %s",
309 vlan->name, upper->name);
310 iface_append_vlan(cfg, vlan, upper);
311 return;
312 }
313
314 /* We can now search for interfaces that have our interface as an upper
315 * interface. */
316 TAILQ_FOREACH(lower, interfaces, next) {
317 if (lower->upper != upper) continue;
318 log_debug("interfaces", "VLAN %s on lower interface %s",
319 vlan->name, upper->name);
320 iface_append_vlan_to_lower(cfg,
321 interfaces, vlan, lower, depth);
322 }
323 }
324
325 void
interfaces_helper_vlan(struct lldpd * cfg,struct interfaces_device_list * interfaces)326 interfaces_helper_vlan(struct lldpd *cfg,
327 struct interfaces_device_list *interfaces)
328 {
329 struct interfaces_device *iface;
330
331 TAILQ_FOREACH(iface, interfaces, next) {
332 if (!(iface->type & IFACE_VLAN_T) && bitmap_isempty(iface->vlan_bmap))
333 continue;
334
335 /* We need to find the physical interfaces of this
336 vlan, through bonds and bridges. */
337 log_debug("interfaces", "search physical interface for VLAN interface %s",
338 iface->name);
339 iface_append_vlan_to_lower(cfg, interfaces,
340 iface, iface, 0);
341 }
342 }
343 #endif
344
345 /* Fill out chassis ID if not already done. Only physical interfaces are
346 * considered. */
347 void
interfaces_helper_chassis(struct lldpd * cfg,struct interfaces_device_list * interfaces)348 interfaces_helper_chassis(struct lldpd *cfg,
349 struct interfaces_device_list *interfaces)
350 {
351 struct interfaces_device *iface;
352 struct lldpd_hardware *hardware;
353 char *name = NULL;
354
355 LOCAL_CHASSIS(cfg)->c_cap_enabled &=
356 ~(LLDP_CAP_BRIDGE | LLDP_CAP_WLAN | LLDP_CAP_STATION);
357 TAILQ_FOREACH(iface, interfaces, next) {
358 if (iface->type & IFACE_BRIDGE_T)
359 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE;
360 if (iface->type & IFACE_WIRELESS_T)
361 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN;
362 }
363 if ((LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_STATION) &&
364 (LOCAL_CHASSIS(cfg)->c_cap_enabled == 0))
365 LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_STATION;
366
367 /* Do not modify the chassis if it's already set to a MAC address or if
368 * it's set to a local address equal to the user-provided
369 * configuration. */
370 if ((LOCAL_CHASSIS(cfg)->c_id != NULL &&
371 LOCAL_CHASSIS(cfg)->c_id_subtype == LLDP_CHASSISID_SUBTYPE_LLADDR) ||
372 cfg->g_config.c_cid_string != NULL)
373 return; /* We already have one */
374
375 TAILQ_FOREACH(iface, interfaces, next) {
376 if (!(iface->type & IFACE_PHYSICAL_T)) continue;
377 if (cfg->g_config.c_cid_pattern &&
378 !pattern_match(iface->name, cfg->g_config.c_cid_pattern, 0)) continue;
379
380 if ((hardware = lldpd_get_hardware(cfg,
381 iface->name,
382 iface->index)) == NULL)
383 /* That's odd. Let's skip. */
384 continue;
385
386 name = malloc(ETHER_ADDR_LEN);
387 if (!name) {
388 log_warn("interfaces", "not enough memory for chassis ID");
389 return;
390 }
391 free(LOCAL_CHASSIS(cfg)->c_id);
392 memcpy(name, hardware->h_lladdr, ETHER_ADDR_LEN);
393 LOCAL_CHASSIS(cfg)->c_id = name;
394 LOCAL_CHASSIS(cfg)->c_id_len = ETHER_ADDR_LEN;
395 LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
396 return;
397 }
398 }
399
400 #undef IN_IS_ADDR_LOOPBACK
401 #define IN_IS_ADDR_LOOPBACK(a) ((a)->s_addr == htonl(INADDR_LOOPBACK))
402 #undef IN_IS_ADDR_ANY
403 #define IN_IS_ADDR_ANY(a) ((a)->s_addr == htonl(INADDR_ANY))
404 #undef IN_IS_ADDR_LINKLOCAL
405 #define IN_IS_ADDR_LINKLOCAL(a) (((a)->s_addr & htonl(0xffff0000)) == htonl(0xa9fe0000))
406 #undef IN_IS_ADDR_GLOBAL
407 #define IN_IS_ADDR_GLOBAL(a) (!IN_IS_ADDR_LOOPBACK(a) && !IN_IS_ADDR_ANY(a) && !IN_IS_ADDR_LINKLOCAL(a))
408 #undef IN6_IS_ADDR_GLOBAL
409 #define IN6_IS_ADDR_GLOBAL(a) \
410 (!IN6_IS_ADDR_LOOPBACK(a) && !IN6_IS_ADDR_LINKLOCAL(a))
411
412 /* Add management addresses for the given family. We only take one of each
413 address family, unless a pattern is provided and is not all negative. For
414 example !*:*,!10.* will only deny addresses. We will pick the first IPv4
415 address not matching 10.*.
416 */
417 static int
interfaces_helper_mgmt_for_af(struct lldpd * cfg,int af,struct interfaces_address_list * addrs,struct interfaces_device_list * interfaces,int global,int allnegative)418 interfaces_helper_mgmt_for_af(struct lldpd *cfg,
419 int af,
420 struct interfaces_address_list *addrs,
421 struct interfaces_device_list *interfaces,
422 int global, int allnegative)
423 {
424 struct interfaces_address *addr;
425 struct interfaces_device *device;
426 struct lldpd_mgmt *mgmt;
427 char addrstrbuf[INET6_ADDRSTRLEN];
428 int found = 0;
429 union lldpd_address in_addr;
430 size_t in_addr_size;
431
432 TAILQ_FOREACH(addr, addrs, next) {
433 if (addr->address.ss_family != lldpd_af(af))
434 continue;
435
436 switch (af) {
437 case LLDPD_AF_IPV4:
438 in_addr_size = sizeof(struct in_addr);
439 memcpy(&in_addr, &((struct sockaddr_in *)&addr->address)->sin_addr,
440 in_addr_size);
441 if (global) {
442 if (!IN_IS_ADDR_GLOBAL(&in_addr.inet))
443 continue;
444 } else {
445 if (!IN_IS_ADDR_LINKLOCAL(&in_addr.inet))
446 continue;
447 }
448 break;
449 case LLDPD_AF_IPV6:
450 in_addr_size = sizeof(struct in6_addr);
451 memcpy(&in_addr, &((struct sockaddr_in6 *)&addr->address)->sin6_addr,
452 in_addr_size);
453 if (global) {
454 if (!IN6_IS_ADDR_GLOBAL(&in_addr.inet6))
455 continue;
456 } else {
457 if (!IN6_IS_ADDR_LINKLOCAL(&in_addr.inet6))
458 continue;
459 }
460 break;
461 default:
462 assert(0);
463 continue;
464 }
465 if (inet_ntop(lldpd_af(af), &in_addr,
466 addrstrbuf, sizeof(addrstrbuf)) == NULL) {
467 log_warn("interfaces", "unable to convert IP address to a string");
468 continue;
469 }
470 if (cfg->g_config.c_mgmt_pattern == NULL ||
471 /* Match on IP address */
472 pattern_match(addrstrbuf, cfg->g_config.c_mgmt_pattern, allnegative) ||
473 /* Match on interface name */
474 ((device = interfaces_indextointerface(interfaces, addr->index)) &&
475 pattern_match(device->name, cfg->g_config.c_mgmt_pattern, allnegative))) {
476 mgmt = lldpd_alloc_mgmt(af, &in_addr, in_addr_size,
477 addr->index);
478 if (mgmt == NULL) {
479 assert(errno == ENOMEM); /* anything else is a bug */
480 log_warn("interfaces", "out of memory error");
481 return found;
482 }
483 log_debug("interfaces", "add management address %s", addrstrbuf);
484 TAILQ_INSERT_TAIL(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries);
485 found = 1;
486
487 /* Don't take additional address if the pattern is all negative. */
488 if (allnegative) break;
489 }
490 }
491 return found;
492 }
493
494 /* Find a management address in all available interfaces, even those that were
495 already handled. This is a special interface handler because it does not
496 really handle interface related information (management address is attached
497 to the local chassis). */
498 void
interfaces_helper_mgmt(struct lldpd * cfg,struct interfaces_address_list * addrs,struct interfaces_device_list * interfaces)499 interfaces_helper_mgmt(struct lldpd *cfg,
500 struct interfaces_address_list *addrs,
501 struct interfaces_device_list *interfaces)
502 {
503 int allnegative = 0;
504 int af;
505 const char *pattern = cfg->g_config.c_mgmt_pattern;
506
507 lldpd_chassis_mgmt_cleanup(LOCAL_CHASSIS(cfg));
508 if (!cfg->g_config.c_mgmt_advertise)
509 return;
510
511 /* Is the pattern provided an actual IP address? */
512 if (pattern && strpbrk(pattern, "!,*?") == NULL) {
513 unsigned char addr[sizeof(struct in6_addr)];
514 size_t addr_size;
515 struct lldpd_mgmt *mgmt;
516 struct interfaces_address *ifaddr;
517
518 for (af = LLDPD_AF_UNSPEC + 1;
519 af != LLDPD_AF_LAST; af++) {
520 switch (af) {
521 case LLDPD_AF_IPV4: addr_size = sizeof(struct in_addr); break;
522 case LLDPD_AF_IPV6: addr_size = sizeof(struct in6_addr); break;
523 default: assert(0);
524 }
525 if (inet_pton(lldpd_af(af), pattern, addr) == 1)
526 break;
527 }
528 if (af != LLDPD_AF_LAST) {
529 /* Try to get the index if possible. */
530 TAILQ_FOREACH(ifaddr, addrs, next) {
531 if (ifaddr->address.ss_family != lldpd_af(af))
532 continue;
533 if (LLDPD_AF_IPV4 == af) {
534 struct sockaddr_in *sa_sin;
535 sa_sin = (struct sockaddr_in *)&ifaddr->address;
536 if (0 == memcmp(addr,
537 &(sa_sin->sin_addr),
538 addr_size))
539 break;
540 }
541 else if (LLDPD_AF_IPV6 == af) {
542 if (0 == memcmp(addr,
543 &((struct sockaddr_in6 *)&ifaddr->address)->sin6_addr,
544 addr_size))
545 break;
546 }
547 }
548
549 mgmt = lldpd_alloc_mgmt(af, addr, addr_size, ifaddr ? ifaddr->index : 0);
550 if (mgmt == NULL) {
551 log_warn("interfaces", "out of memory error");
552 return;
553 }
554 log_debug("interfaces", "add exact management address %s",
555 pattern);
556 TAILQ_INSERT_TAIL(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries);
557 return;
558 }
559 /* else: could be an interface name */
560 }
561
562 /* Is the pattern provided all negative? */
563 if (pattern == NULL) allnegative = 1;
564 else if (pattern[0] == '!') {
565 /* If each comma is followed by '!', its an all
566 negative pattern */
567 const char *sep = pattern;
568 while ((sep = strchr(sep, ',')) &&
569 (*(++sep) == '!'));
570 if (sep == NULL) allnegative = 1;
571 }
572
573 /* Find management addresses */
574 for (af = LLDPD_AF_UNSPEC + 1; af != LLDPD_AF_LAST; af++) {
575 (void)(interfaces_helper_mgmt_for_af(cfg, af, addrs, interfaces, 1, allnegative) ||
576 interfaces_helper_mgmt_for_af(cfg, af, addrs, interfaces, 0, allnegative));
577 }
578 }
579
580 /* Fill up port name and description */
581 void
interfaces_helper_port_name_desc(struct lldpd * cfg,struct lldpd_hardware * hardware,struct interfaces_device * iface)582 interfaces_helper_port_name_desc(struct lldpd *cfg,
583 struct lldpd_hardware *hardware,
584 struct interfaces_device *iface)
585 {
586 struct lldpd_port *port = &hardware->h_lport;
587
588 /* We need to set the portid to what the client configured.
589 This can be done from the CLI.
590 */
591 int has_alias = (iface->alias != NULL && strlen(iface->alias) != 0);
592 int portid_type = cfg->g_config.c_lldp_portid_type;
593 if (portid_type == LLDP_PORTID_SUBTYPE_IFNAME ||
594 (portid_type == LLDP_PORTID_SUBTYPE_UNKNOWN && has_alias) ||
595 (port->p_id_subtype == LLDP_PORTID_SUBTYPE_LOCAL && has_alias)) {
596 if (port->p_id_subtype != LLDP_PORTID_SUBTYPE_LOCAL) {
597 log_debug("interfaces", "use ifname for %s",
598 hardware->h_ifname);
599 port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
600 port->p_id_len = strlen(hardware->h_ifname);
601 free(port->p_id);
602 if ((port->p_id = calloc(1, port->p_id_len)) == NULL)
603 fatal("interfaces", NULL);
604 memcpy(port->p_id, hardware->h_ifname, port->p_id_len);
605 }
606
607 if (port->p_descr_force == 0) {
608 /* use the actual alias in the port description */
609 log_debug("interfaces", "using alias in description for %s",
610 hardware->h_ifname);
611 free(port->p_descr);
612 if (has_alias) {
613 port->p_descr = strdup(iface->alias);
614 } else {
615 /* We don't have anything else to put here and for CDP
616 * with need something non-NULL */
617 port->p_descr = strdup(hardware->h_ifname);
618 }
619 }
620 } else {
621 if (port->p_id_subtype != LLDP_PORTID_SUBTYPE_LOCAL) {
622 log_debug("interfaces", "use MAC address for %s",
623 hardware->h_ifname);
624 port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
625 free(port->p_id);
626 if ((port->p_id = calloc(1, ETHER_ADDR_LEN)) == NULL)
627 fatal("interfaces", NULL);
628 memcpy(port->p_id, hardware->h_lladdr, ETHER_ADDR_LEN);
629 port->p_id_len = ETHER_ADDR_LEN;
630 }
631
632 if (port->p_descr_force == 0) {
633 /* use the ifname in the port description until alias is set */
634 log_debug("interfaces", "using ifname in description for %s",
635 hardware->h_ifname);
636 free(port->p_descr);
637 port->p_descr = strdup(hardware->h_ifname);
638 }
639 }
640 }
641
642 void
interfaces_helper_add_hardware(struct lldpd * cfg,struct lldpd_hardware * hardware)643 interfaces_helper_add_hardware(struct lldpd *cfg,
644 struct lldpd_hardware *hardware)
645 {
646 TRACE(LLDPD_INTERFACES_NEW(hardware->h_ifname));
647 TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
648 }
649
650 void
interfaces_helper_physical(struct lldpd * cfg,struct interfaces_device_list * interfaces,struct lldpd_ops * ops,int (* init)(struct lldpd *,struct lldpd_hardware *))651 interfaces_helper_physical(struct lldpd *cfg,
652 struct interfaces_device_list *interfaces,
653 struct lldpd_ops *ops,
654 int(*init)(struct lldpd *, struct lldpd_hardware *))
655 {
656 struct interfaces_device *iface;
657 struct lldpd_hardware *hardware;
658 int created;
659
660 TAILQ_FOREACH(iface, interfaces, next) {
661 if (!(iface->type & IFACE_PHYSICAL_T)) continue;
662 if (iface->ignore) continue;
663
664 log_debug("interfaces", "%s is an acceptable ethernet device",
665 iface->name);
666 created = 0;
667 if ((hardware = lldpd_get_hardware(cfg,
668 iface->name,
669 iface->index)) == NULL) {
670 if ((hardware = lldpd_alloc_hardware(cfg,
671 iface->name,
672 iface->index)) == NULL) {
673 log_warnx("interfaces", "Unable to allocate space for %s",
674 iface->name);
675 continue;
676 }
677 created = 1;
678 }
679 if (hardware->h_flags)
680 continue;
681 if (hardware->h_ops != ops) {
682 if (!created) {
683 log_debug("interfaces",
684 "interface %s is converted from another type of interface",
685 hardware->h_ifname);
686 if (hardware->h_ops && hardware->h_ops->cleanup) {
687 hardware->h_ops->cleanup(cfg, hardware);
688 levent_hardware_release(hardware);
689 levent_hardware_init(hardware);
690 }
691 }
692 if (init(cfg, hardware) != 0) {
693 log_warnx("interfaces",
694 "unable to initialize %s",
695 hardware->h_ifname);
696 lldpd_hardware_cleanup(cfg, hardware);
697 continue;
698 }
699 hardware->h_ops = ops;
700 hardware->h_mangle = (iface->upper &&
701 iface->upper->type & IFACE_BOND_T);
702 }
703 if (created)
704 interfaces_helper_add_hardware(cfg, hardware);
705 else
706 lldpd_port_cleanup(&hardware->h_lport, 0);
707
708 hardware->h_flags = iface->flags; /* Should be non-zero */
709 iface->ignore = 1; /* Future handlers
710 don't have to
711 care about this
712 interface. */
713
714 /* Get local address */
715 memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
716
717 /* Fill information about port */
718 interfaces_helper_port_name_desc(cfg, hardware, iface);
719
720 /* Fill additional info */
721 hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
722
723 #ifdef ENABLE_DOT3
724 if (iface->upper && iface->upper->type & IFACE_BOND_T)
725 hardware->h_lport.p_aggregid = iface->upper->index;
726 else
727 hardware->h_lport.p_aggregid = 0;
728 #endif
729 }
730 }
731
732 void
interfaces_helper_promisc(struct lldpd * cfg,struct lldpd_hardware * hardware)733 interfaces_helper_promisc(struct lldpd *cfg,
734 struct lldpd_hardware *hardware)
735 {
736 if (!cfg->g_config.c_promisc) return;
737 if (priv_iface_promisc(hardware->h_ifname) != 0) {
738 log_warnx("interfaces",
739 "unable to enable promiscuous mode for %s",
740 hardware->h_ifname);
741 }
742 }
743
744 /**
745 * Send the packet using the hardware function. Optionnaly mangle the MAC address.
746 *
747 * With bonds, we have duplicate MAC address on different physical
748 * interfaces. We need to alter the source MAC address when we send on an
749 * inactive slave. The `h_mangle` flah is used to know if we need to do
750 * something like that.
751 */
752 int
interfaces_send_helper(struct lldpd * cfg,struct lldpd_hardware * hardware,char * buffer,size_t size)753 interfaces_send_helper(struct lldpd *cfg,
754 struct lldpd_hardware *hardware,
755 char *buffer, size_t size)
756 {
757 if (size < 2 * ETHER_ADDR_LEN) {
758 log_warnx("interfaces",
759 "packet to send on %s is too small!",
760 hardware->h_ifname);
761 return 0;
762 }
763 if (hardware->h_mangle) {
764 #define MAC_UL_ADMINISTERED_BIT_MASK 0x02
765 char *src_mac = buffer + ETHER_ADDR_LEN;
766 char arbitrary[] = { 0x00, 0x60, 0x08, 0x69, 0x97, 0xef};
767
768 switch (cfg->g_config.c_bond_slave_src_mac_type) {
769 case LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED:
770 if (!(*src_mac & MAC_UL_ADMINISTERED_BIT_MASK)) {
771 *src_mac |= MAC_UL_ADMINISTERED_BIT_MASK;
772 break;
773 }
774 /* Fallback to fixed value */
775 memcpy(src_mac, arbitrary, ETHER_ADDR_LEN);
776 break;
777 case LLDP_BOND_SLAVE_SRC_MAC_TYPE_FIXED:
778 memcpy(src_mac, arbitrary, ETHER_ADDR_LEN);
779 break;
780 case LLDP_BOND_SLAVE_SRC_MAC_TYPE_ZERO:
781 memset(src_mac, 0, ETHER_ADDR_LEN);
782 break;
783 }
784 }
785 return hardware->h_ops->send(cfg, hardware, buffer, size);
786 }
787