]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/interfaces.c
interfaces: mangle source MAC address on bonds for other OS too
[thirdparty/lldpd.git] / src / daemon / interfaces.c
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
20 #include <stddef.h>
21 #include <unistd.h>
22 #include <errno.h>
23 #include <assert.h>
24 #include <fnmatch.h>
25 #include <arpa/inet.h>
26
27 /* Generic ethernet interface initialization */
28 /**
29 * Enable multicast on the given interface.
30 */
31 void
32 interfaces_setup_multicast(struct lldpd *cfg, const char *name,
33 int remove)
34 {
35 int i, rc;
36
37 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
38 if (!cfg->g_protocols[i].enabled) continue;
39 if ((rc = priv_iface_multicast(name,
40 cfg->g_protocols[i].mac, !remove)) != 0) {
41 errno = rc;
42 if (errno != ENOENT)
43 log_info("interfaces",
44 "unable to %s %s address to multicast filter for %s",
45 (remove)?"delete":"add",
46 cfg->g_protocols[i].name,
47 name);
48 }
49 }
50 }
51
52 /**
53 * Free an interface.
54 *
55 * @param iff interface to be freed
56 */
57 void
58 interfaces_free_device(struct interfaces_device *iff)
59 {
60 if (!iff) return;
61 free(iff->name);
62 free(iff->alias);
63 free(iff->address);
64 free(iff->driver);
65 free(iff);
66 }
67
68 /**
69 * Free a list of interfaces.
70 *
71 * @param ifs list of interfaces to be freed
72 */
73 void
74 interfaces_free_devices(struct interfaces_device_list *ifs)
75 {
76 struct interfaces_device *iff, *iff_next;
77 if (!ifs) return;
78 for (iff = TAILQ_FIRST(ifs);
79 iff != NULL;
80 iff = iff_next) {
81 iff_next = TAILQ_NEXT(iff, next);
82 interfaces_free_device(iff);
83 }
84 free(ifs);
85 }
86
87 /**
88 * Free one address
89 *
90 * @param ifaddr Address to be freed
91 */
92 void
93 interfaces_free_address(struct interfaces_address *ifaddr)
94 {
95 free(ifaddr);
96 }
97
98 /**
99 * Free a list of addresses.
100 *
101 * @param ifaddrs list of addresses
102 */
103 void
104 interfaces_free_addresses(struct interfaces_address_list *ifaddrs)
105 {
106 struct interfaces_address *ifa, *ifa_next;
107 if (!ifaddrs) return;
108 for (ifa = TAILQ_FIRST(ifaddrs);
109 ifa != NULL;
110 ifa = ifa_next) {
111 ifa_next = TAILQ_NEXT(ifa, next);
112 interfaces_free_address(ifa);
113 }
114 free(ifaddrs);
115 }
116
117 /**
118 * Find the appropriate interface from the name.
119 *
120 * @param interfaces List of available interfaces
121 * @param device Name of the device we search for
122 * @return The interface or NULL if not found
123 */
124 struct interfaces_device*
125 interfaces_nametointerface(struct interfaces_device_list *interfaces,
126 const char *device)
127 {
128 struct interfaces_device *iface;
129 TAILQ_FOREACH(iface, interfaces, next) {
130 if (!strncmp(iface->name, device, IFNAMSIZ))
131 return iface;
132 }
133 log_debug("interfaces", "cannot get interface for index %s",
134 device);
135 return NULL;
136 }
137
138 /**
139 * Find the appropriate interface from the index.
140 *
141 * @param interfaces List of available interfaces
142 * @param index Index of the device we search for
143 * @return The interface or NULL if not found
144 */
145 struct interfaces_device*
146 interfaces_indextointerface(struct interfaces_device_list *interfaces,
147 int index)
148 {
149 struct interfaces_device *iface;
150 TAILQ_FOREACH(iface, interfaces, next) {
151 if (iface->index == index)
152 return iface;
153 }
154 log_debug("interfaces", "cannot get interface for index %d",
155 index);
156 return NULL;
157 }
158
159 static int
160 pattern_match(char *iface, char *list, int found)
161 {
162 char *interfaces = NULL;
163 char *pattern;
164
165 if ((interfaces = strdup(list)) == NULL) {
166 log_warnx("interfaces", "unable to allocate memory");
167 return 0;
168 }
169
170 for (pattern = strtok(interfaces, ",");
171 pattern != NULL;
172 pattern = strtok(NULL, ",")) {
173 if ((pattern[0] == '!') &&
174 ((fnmatch(pattern + 1, iface, 0) == 0))) {
175 /* Blacklisted. No need to search further. */
176 found = 0;
177 break;
178 }
179 if (fnmatch(pattern, iface, 0) == 0)
180 found = 1;
181 }
182
183 free(interfaces);
184 return found;
185 }
186
187 void
188 interfaces_helper_whitelist(struct lldpd *cfg,
189 struct interfaces_device_list *interfaces)
190 {
191 struct interfaces_device *iface;
192
193 if (!cfg->g_config.c_iface_pattern)
194 return;
195
196 TAILQ_FOREACH(iface, interfaces, next) {
197 if (iface->flags == 0) continue; /* Already handled by someone else */
198 if (!pattern_match(iface->name, cfg->g_config.c_iface_pattern, 0)) {
199 /* This interface was not found. We flag it. */
200 log_debug("interfaces", "blacklist %s", iface->name);
201 iface->flags = 0;
202 }
203 }
204 }
205
206 #ifdef ENABLE_DOT1
207 static void
208 iface_append_vlan(struct lldpd *cfg,
209 struct interfaces_device *vlan,
210 struct interfaces_device *lower)
211 {
212 struct lldpd_hardware *hardware =
213 lldpd_get_hardware(cfg, lower->name, lower->index, NULL);
214 struct lldpd_port *port;
215 struct lldpd_vlan *v;
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
224 /* Check if the VLAN is already here. */
225 port = &hardware->h_lport;
226 TAILQ_FOREACH(v, &port->p_vlans, v_entries)
227 if (strncmp(vlan->name, v->v_name, IFNAMSIZ) == 0)
228 return;
229 if ((v = (struct lldpd_vlan *)
230 calloc(1, sizeof(struct lldpd_vlan))) == NULL)
231 return;
232 if ((v->v_name = strdup(vlan->name)) == NULL) {
233 free(v);
234 return;
235 }
236 v->v_vid = vlan->vlanid;
237 log_debug("interfaces", "append VLAN %s for %s",
238 v->v_name,
239 hardware->h_ifname);
240 TAILQ_INSERT_TAIL(&port->p_vlans, v, v_entries);
241 }
242
243 /**
244 * Append VLAN to the lowest possible interface.
245 *
246 * @param vlan The VLAN interface (used to get VLAN ID).
247 * @param upper The upper interface we are currently examining.
248 *
249 * Initially, upper == vlan. This function will be called recursively.
250 */
251 static void
252 iface_append_vlan_to_lower(struct lldpd *cfg,
253 struct interfaces_device_list *interfaces,
254 struct interfaces_device *vlan,
255 struct interfaces_device *upper)
256 {
257 struct interfaces_device *lower;
258 log_debug("interfaces",
259 "looking to apply VLAN %s to physical interface behind %s",
260 vlan->name, upper->name);
261
262 /* Easy: check if we have a lower interface. */
263 if (upper->lower) {
264 log_debug("interfaces", "VLAN %s on lower interface %s",
265 vlan->name, upper->name);
266 iface_append_vlan_to_lower(cfg,
267 interfaces, vlan,
268 upper->lower);
269 return;
270 }
271
272 /* Other easy case, we have a physical interface. */
273 if (upper->type & IFACE_PHYSICAL_T) {
274 log_debug("interfaces", "VLAN %s on physical interface %s",
275 vlan->name, upper->name);
276 iface_append_vlan(cfg, vlan, upper);
277 return;
278 }
279
280 /* We can now search for interfaces that have our interface as an upper
281 * interface. */
282 TAILQ_FOREACH(lower, interfaces, next) {
283 if (lower->upper != upper) continue;
284 log_debug("interfaces", "VLAN %s on lower interface %s",
285 vlan->name, upper->name);
286 iface_append_vlan_to_lower(cfg,
287 interfaces, vlan, lower);
288 }
289 }
290
291 void
292 interfaces_helper_vlan(struct lldpd *cfg,
293 struct interfaces_device_list *interfaces)
294 {
295 struct interfaces_device *iface;
296
297 TAILQ_FOREACH(iface, interfaces, next) {
298 if (!iface->flags)
299 continue;
300 if (!(iface->type & IFACE_VLAN_T))
301 continue;
302
303 /* We need to find the physical interfaces of this
304 vlan, through bonds and bridges. */
305 log_debug("interfaces", "search physical interface for VLAN interface %s",
306 iface->name);
307 iface_append_vlan_to_lower(cfg, interfaces,
308 iface, iface);
309 }
310 }
311 #endif
312
313 /* Fill out chassis ID if not already done. This handler is special
314 because we will only handle interfaces that are already handled. */
315 void
316 interfaces_helper_chassis(struct lldpd *cfg,
317 struct interfaces_device_list *interfaces)
318 {
319 struct interfaces_device *iface;
320 struct lldpd_hardware *hardware;
321 char *name = NULL;
322
323 TAILQ_FOREACH(iface, interfaces, next) {
324 if (iface->type & IFACE_BRIDGE_T)
325 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE;
326 if (iface->type & IFACE_WIRELESS_T)
327 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN;
328 }
329
330 if (LOCAL_CHASSIS(cfg)->c_id != NULL &&
331 LOCAL_CHASSIS(cfg)->c_id_subtype == LLDP_CHASSISID_SUBTYPE_LLADDR)
332 return; /* We already have one */
333
334 TAILQ_FOREACH(iface, interfaces, next) {
335 if (iface->flags) continue;
336 if (cfg->g_config.c_cid_pattern &&
337 !pattern_match(iface->name, cfg->g_config.c_cid_pattern, 0)) continue;
338
339 if ((hardware = lldpd_get_hardware(cfg,
340 iface->name,
341 iface->index,
342 NULL)) == NULL)
343 /* That's odd. Let's skip. */
344 continue;
345
346 name = malloc(sizeof(hardware->h_lladdr));
347 if (!name) {
348 log_warn("interfaces", "not enough memory for chassis ID");
349 return;
350 }
351 free(LOCAL_CHASSIS(cfg)->c_id);
352 memcpy(name, hardware->h_lladdr, sizeof(hardware->h_lladdr));
353 LOCAL_CHASSIS(cfg)->c_id = name;
354 LOCAL_CHASSIS(cfg)->c_id_len = sizeof(hardware->h_lladdr);
355 LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
356 return;
357 }
358 }
359
360 #ifndef IN_IS_ADDR_LOOPBACK
361 #define IN_IS_ADDR_LOOPBACK(a) ((a)->s_addr == htonl(INADDR_LOOPBACK))
362 #endif
363 #ifndef IN_IS_ADDR_ANY
364 #define IN_IS_ADDR_ANY(a) ((a)->s_addr == htonl(INADDR_ANY))
365 #endif
366 #ifndef IN_IS_ADDR_GLOBAL
367 #define IN_IS_ADDR_GLOBAL(a) (!IN_IS_ADDR_LOOPBACK(a) && !IN_IS_ADDR_ANY(a))
368 #endif
369 #ifndef IN6_IS_ADDR_GLOBAL
370 #define IN6_IS_ADDR_GLOBAL(a) \
371 (!IN6_IS_ADDR_LOOPBACK(a) && !IN6_IS_ADDR_LINKLOCAL(a))
372 #endif
373
374 /* Find a management address in all available interfaces, even those that were
375 already handled. This is a special interface handler because it does not
376 really handle interface related information (management address is attached
377 to the local chassis). */
378 void
379 interfaces_helper_mgmt(struct lldpd *cfg,
380 struct interfaces_address_list *addrs)
381 {
382 struct interfaces_address *addr;
383 char addrstrbuf[INET6_ADDRSTRLEN];
384 struct lldpd_mgmt *mgmt;
385 void *sin_addr_ptr;
386 size_t sin_addr_size;
387 int af;
388 int allnegative = 0;
389
390 lldpd_chassis_mgmt_cleanup(LOCAL_CHASSIS(cfg));
391
392 /* Is the pattern provided all negative? */
393 if (cfg->g_config.c_mgmt_pattern == NULL) allnegative = 1;
394 else if (cfg->g_config.c_mgmt_pattern[0] == '!') {
395 /* If each comma is followed by '!', its an all
396 negative pattern */
397 char *sep = cfg->g_config.c_mgmt_pattern;
398 while ((sep = strchr(sep, ',')) &&
399 (*(++sep) == '!'));
400 if (sep == NULL) allnegative = 1;
401 }
402
403 /* Find management addresses */
404 for (af = LLDPD_AF_UNSPEC + 1; af != LLDPD_AF_LAST; af++) {
405 /* We only take one of each address family, unless a
406 pattern is provided and is not all negative. For
407 example !*:*,!10.* will only blacklist
408 addresses. We will pick the first IPv4 address not
409 matching 10.*. */
410 TAILQ_FOREACH(addr, addrs, next) {
411 if (addr->address.ss_family != lldpd_af(af))
412 continue;
413
414 switch (af) {
415 case LLDPD_AF_IPV4:
416 sin_addr_ptr = &((struct sockaddr_in *)&addr->address)->sin_addr;
417 sin_addr_size = sizeof(struct in_addr);
418 if (!IN_IS_ADDR_GLOBAL((struct in_addr *)sin_addr_ptr))
419 continue;
420 break;
421 case LLDPD_AF_IPV6:
422 sin_addr_ptr = &((struct sockaddr_in6 *)&addr->address)->sin6_addr;
423 sin_addr_size = sizeof(struct in6_addr);
424 if (!IN6_IS_ADDR_GLOBAL((struct in6_addr *)sin_addr_ptr))
425 continue;
426 break;
427 default:
428 assert(0);
429 continue;
430 }
431 if (inet_ntop(lldpd_af(af), sin_addr_ptr,
432 addrstrbuf, sizeof(addrstrbuf)) == NULL) {
433 log_warn("interfaces", "unable to convert IP address to a string");
434 continue;
435 }
436 if (cfg->g_config.c_mgmt_pattern == NULL ||
437 pattern_match(addrstrbuf, cfg->g_config.c_mgmt_pattern, allnegative)) {
438 mgmt = lldpd_alloc_mgmt(af, sin_addr_ptr, sin_addr_size,
439 addr->index);
440 if (mgmt == NULL) {
441 assert(errno == ENOMEM); /* anything else is a bug */
442 log_warn("interfaces", "out of memory error");
443 return;
444 }
445 log_debug("interfaces", "add management address %s", addrstrbuf);
446 TAILQ_INSERT_TAIL(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries);
447
448 /* Don't take additional address if the pattern is all negative. */
449 if (allnegative) break;
450 }
451 }
452 }
453 }
454
455 /* Fill up port name and description */
456 void
457 interfaces_helper_port_name_desc(struct lldpd_hardware *hardware,
458 struct interfaces_device *iface)
459 {
460 struct lldpd_port *port = &hardware->h_lport;
461
462 /* There are two cases:
463
464 1. We have a kernel recent enough to support ifAlias
465 _and_ a non empty ifAlias, then we will use it for
466 description and use ifname for port ID.
467
468 2. Otherwise, we will use the MAC address as ID and the
469 port name in description.
470 */
471
472 if (iface->alias == NULL || strlen(iface->alias) == 0) {
473 /* Case 2: MAC address and port name */
474 log_debug("interfaces", "use ifname and MAC address for %s",
475 hardware->h_ifname);
476 port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
477 if ((port->p_id =
478 calloc(1, sizeof(hardware->h_lladdr))) == NULL)
479 fatal("interfaces", NULL);
480 memcpy(port->p_id, hardware->h_lladdr,
481 sizeof(hardware->h_lladdr));
482 port->p_id_len = sizeof(hardware->h_lladdr);
483 port->p_descr = strdup(hardware->h_ifname);
484 return;
485 }
486 /* Case 1: port name and port description */
487 log_debug("interfaces", "use ifname and ifalias for %s",
488 hardware->h_ifname);
489 port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
490 port->p_id_len = strlen(hardware->h_ifname);
491 if ((port->p_id =
492 calloc(1, port->p_id_len)) == NULL)
493 fatal("interfaces", NULL);
494 memcpy(port->p_id, hardware->h_ifname, port->p_id_len);
495 port->p_descr = strdup(iface->alias);
496 }
497
498 void
499 interfaces_helper_physical(struct lldpd *cfg,
500 struct interfaces_device_list *interfaces,
501 struct lldpd_ops *ops,
502 int(*init)(struct lldpd *, struct lldpd_hardware *))
503 {
504 struct interfaces_device *iface;
505 struct lldpd_hardware *hardware;
506
507 TAILQ_FOREACH(iface, interfaces, next) {
508 if (!(iface->type & IFACE_PHYSICAL_T)) continue;
509 if (!iface->flags) continue;
510
511 log_debug("interfaces", "%s is an acceptable ethernet device",
512 iface->name);
513 if ((hardware = lldpd_get_hardware(cfg,
514 iface->name,
515 iface->index,
516 ops)) == NULL) {
517 if ((hardware = lldpd_alloc_hardware(cfg,
518 iface->name,
519 iface->index)) == NULL) {
520 log_warnx("interfaces", "Unable to allocate space for %s",
521 iface->name);
522 continue;
523 }
524 if (init(cfg, hardware) != 0) {
525 log_warnx("interfaces",
526 "unable to initialize %s",
527 hardware->h_ifname);
528 lldpd_hardware_cleanup(cfg, hardware);
529 continue;
530 }
531 hardware->h_ops = ops;
532 hardware->h_mangle = (iface->upper &&
533 iface->upper->type & IFACE_BOND_T);
534 TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
535 } else {
536 if (hardware->h_flags) continue; /* Already seen this time */
537 lldpd_port_cleanup(&hardware->h_lport, 0);
538 }
539
540 hardware->h_flags = iface->flags; /* Should be non-zero */
541 iface->flags = 0; /* Future handlers
542 don't have to
543 care about this
544 interface. */
545
546 /* Get local address */
547 memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
548
549 /* Fill information about port */
550 interfaces_helper_port_name_desc(hardware, iface);
551
552 /* Fill additional info */
553 hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
554
555 #ifdef ENABLE_DOT3
556 if (iface->upper && iface->upper->type & IFACE_BOND_T)
557 hardware->h_lport.p_aggregid = iface->upper->index;
558 #endif
559 }
560 }
561
562 /**
563 * Send the packet using the hardware function. Optionnaly mangle the MAC address.
564 *
565 * With bonds, we have duplicate MAC address on different physical
566 * interfaces. We need to alter the source MAC address when we send on an
567 * inactive slave. The `h_mangle` flah is used to know if we need to do
568 * something like that.
569 */
570 int
571 interfaces_send_helper(struct lldpd *cfg,
572 struct lldpd_hardware *hardware,
573 char *buffer, size_t size)
574 {
575 if (size < 2 * ETHER_ADDR_LEN) {
576 log_warnx("interfaces",
577 "packet to send on %s is too small!",
578 hardware->h_ifname);
579 return 0;
580 }
581 if (hardware->h_mangle) {
582 #define MAC_UL_ADMINISTERED_BIT_MASK 0x02
583 char *src_mac = buffer + ETHER_ADDR_LEN;
584 char arbitrary[] = { 0x00, 0x60, 0x08, 0x69, 0x97, 0xef};
585
586 switch (cfg->g_config.c_bond_slave_src_mac_type) {
587 case LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED:
588 if (*src_mac & MAC_UL_ADMINISTERED_BIT_MASK) {
589 /* If locally administered bit already set,
590 * use zero mac
591 */
592 memset(src_mac, 0, ETHER_ADDR_LEN);
593 break;
594 }
595 case LLDP_BOND_SLAVE_SRC_MAC_TYPE_FIXED:
596 memcpy(src_mac, arbitrary, ETHER_ADDR_LEN);
597 break;
598 case LLDP_BOND_SLAVE_SRC_MAC_TYPE_ZERO:
599 memset(src_mac, 0, ETHER_ADDR_LEN);
600 break;
601 }
602 }
603 return hardware->h_ops->send(cfg, hardware, buffer, size);
604 }