]>
Commit | Line | Data |
---|---|---|
adbb6e54 VB |
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" | |
bdfe4193 | 19 | #include "trace.h" |
adbb6e54 VB |
20 | |
21 | #include <stddef.h> | |
22 | #include <unistd.h> | |
23 | #include <errno.h> | |
24 | #include <assert.h> | |
adbb6e54 VB |
25 | #include <arpa/inet.h> |
26 | ||
adbb6e54 VB |
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) | |
0469161d ST |
43 | log_debug("interfaces", |
44 | "unable to %s %s address to multicast filter for %s (%s)", | |
adbb6e54 VB |
45 | (remove)?"delete":"add", |
46 | cfg->g_protocols[i].name, | |
0469161d | 47 | name, strerror(rc)); |
adbb6e54 VB |
48 | } |
49 | } | |
50 | } | |
51 | ||
adbb6e54 VB |
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 | |
9cac8fed | 122 | * @return The interface or NULL if not found |
adbb6e54 VB |
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 | |
9cac8fed | 143 | * @return The interface or NULL if not found |
adbb6e54 VB |
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 | ||
adbb6e54 VB |
159 | void |
160 | interfaces_helper_whitelist(struct lldpd *cfg, | |
161 | struct interfaces_device_list *interfaces) | |
162 | { | |
163 | struct interfaces_device *iface; | |
164 | ||
165 | if (!cfg->g_config.c_iface_pattern) | |
166 | return; | |
167 | ||
168 | TAILQ_FOREACH(iface, interfaces, next) { | |
627e31c4 VB |
169 | int m = pattern_match(iface->name, cfg->g_config.c_iface_pattern, 0); |
170 | switch (m) { | |
171 | case 0: | |
adbb6e54 | 172 | log_debug("interfaces", "blacklist %s", iface->name); |
0fa2254b | 173 | iface->ignore = 1; |
627e31c4 VB |
174 | continue; |
175 | case 2: | |
c6aa43e5 VB |
176 | log_debug("interfaces", "whitelist %s (consider it as a physical interface)", |
177 | iface->name); | |
178 | iface->type |= IFACE_PHYSICAL_T; | |
627e31c4 | 179 | continue; |
adbb6e54 VB |
180 | } |
181 | } | |
182 | } | |
183 | ||
184 | #ifdef ENABLE_DOT1 | |
185 | static void | |
186 | iface_append_vlan(struct lldpd *cfg, | |
187 | struct interfaces_device *vlan, | |
188 | struct interfaces_device *lower) | |
189 | { | |
190 | struct lldpd_hardware *hardware = | |
191 | lldpd_get_hardware(cfg, lower->name, lower->index, NULL); | |
192 | struct lldpd_port *port; | |
193 | struct lldpd_vlan *v; | |
194 | ||
195 | if (hardware == NULL) { | |
196 | log_debug("interfaces", | |
197 | "cannot find real interface %s for VLAN %s", | |
198 | lower->name, vlan->name); | |
199 | return; | |
200 | } | |
201 | ||
202 | /* Check if the VLAN is already here. */ | |
203 | port = &hardware->h_lport; | |
204 | TAILQ_FOREACH(v, &port->p_vlans, v_entries) | |
205 | if (strncmp(vlan->name, v->v_name, IFNAMSIZ) == 0) | |
206 | return; | |
207 | if ((v = (struct lldpd_vlan *) | |
208 | calloc(1, sizeof(struct lldpd_vlan))) == NULL) | |
209 | return; | |
210 | if ((v->v_name = strdup(vlan->name)) == NULL) { | |
211 | free(v); | |
212 | return; | |
213 | } | |
214 | v->v_vid = vlan->vlanid; | |
215 | log_debug("interfaces", "append VLAN %s for %s", | |
216 | v->v_name, | |
217 | hardware->h_ifname); | |
218 | TAILQ_INSERT_TAIL(&port->p_vlans, v, v_entries); | |
219 | } | |
220 | ||
221 | /** | |
222 | * Append VLAN to the lowest possible interface. | |
223 | * | |
224 | * @param vlan The VLAN interface (used to get VLAN ID). | |
225 | * @param upper The upper interface we are currently examining. | |
e7e6676e | 226 | * @param depth Depth of the stack (avoid infinite recursion) |
adbb6e54 VB |
227 | * |
228 | * Initially, upper == vlan. This function will be called recursively. | |
229 | */ | |
230 | static void | |
231 | iface_append_vlan_to_lower(struct lldpd *cfg, | |
232 | struct interfaces_device_list *interfaces, | |
233 | struct interfaces_device *vlan, | |
e7e6676e VB |
234 | struct interfaces_device *upper, |
235 | int depth) | |
adbb6e54 | 236 | { |
e7e6676e VB |
237 | if (depth > 5) { |
238 | log_debug("interfaces", | |
239 | "maximum depth reached when applying VLAN %s (loop?)", | |
240 | vlan->name); | |
241 | return; | |
242 | } | |
243 | depth++; | |
adbb6e54 VB |
244 | struct interfaces_device *lower; |
245 | log_debug("interfaces", | |
246 | "looking to apply VLAN %s to physical interface behind %s", | |
247 | vlan->name, upper->name); | |
248 | ||
249 | /* Easy: check if we have a lower interface. */ | |
250 | if (upper->lower) { | |
251 | log_debug("interfaces", "VLAN %s on lower interface %s", | |
252 | vlan->name, upper->name); | |
253 | iface_append_vlan_to_lower(cfg, | |
254 | interfaces, vlan, | |
e7e6676e VB |
255 | upper->lower, |
256 | depth); | |
adbb6e54 VB |
257 | return; |
258 | } | |
259 | ||
260 | /* Other easy case, we have a physical interface. */ | |
261 | if (upper->type & IFACE_PHYSICAL_T) { | |
262 | log_debug("interfaces", "VLAN %s on physical interface %s", | |
263 | vlan->name, upper->name); | |
264 | iface_append_vlan(cfg, vlan, upper); | |
265 | return; | |
266 | } | |
267 | ||
268 | /* We can now search for interfaces that have our interface as an upper | |
269 | * interface. */ | |
270 | TAILQ_FOREACH(lower, interfaces, next) { | |
271 | if (lower->upper != upper) continue; | |
272 | log_debug("interfaces", "VLAN %s on lower interface %s", | |
273 | vlan->name, upper->name); | |
274 | iface_append_vlan_to_lower(cfg, | |
e7e6676e | 275 | interfaces, vlan, lower, depth); |
adbb6e54 VB |
276 | } |
277 | } | |
278 | ||
279 | void | |
280 | interfaces_helper_vlan(struct lldpd *cfg, | |
281 | struct interfaces_device_list *interfaces) | |
282 | { | |
283 | struct interfaces_device *iface; | |
284 | ||
285 | TAILQ_FOREACH(iface, interfaces, next) { | |
0fa2254b | 286 | if (iface->ignore) |
adbb6e54 VB |
287 | continue; |
288 | if (!(iface->type & IFACE_VLAN_T)) | |
289 | continue; | |
290 | ||
291 | /* We need to find the physical interfaces of this | |
292 | vlan, through bonds and bridges. */ | |
293 | log_debug("interfaces", "search physical interface for VLAN interface %s", | |
294 | iface->name); | |
295 | iface_append_vlan_to_lower(cfg, interfaces, | |
e7e6676e | 296 | iface, iface, 0); |
adbb6e54 VB |
297 | } |
298 | } | |
299 | #endif | |
300 | ||
790752d0 VB |
301 | /* Fill out chassis ID if not already done. Only physical interfaces are |
302 | * considered. */ | |
adbb6e54 VB |
303 | void |
304 | interfaces_helper_chassis(struct lldpd *cfg, | |
305 | struct interfaces_device_list *interfaces) | |
306 | { | |
307 | struct interfaces_device *iface; | |
308 | struct lldpd_hardware *hardware; | |
309 | char *name = NULL; | |
310 | ||
a2b113ef | 311 | LOCAL_CHASSIS(cfg)->c_cap_enabled &= |
312 | ~(LLDP_CAP_BRIDGE | LLDP_CAP_WLAN | LLDP_CAP_STATION); | |
adbb6e54 VB |
313 | TAILQ_FOREACH(iface, interfaces, next) { |
314 | if (iface->type & IFACE_BRIDGE_T) | |
315 | LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE; | |
316 | if (iface->type & IFACE_WIRELESS_T) | |
317 | LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN; | |
318 | } | |
a2b113ef | 319 | if ((LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_STATION) && |
320 | (LOCAL_CHASSIS(cfg)->c_cap_enabled == 0)) | |
321 | LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_STATION; | |
adbb6e54 VB |
322 | |
323 | if (LOCAL_CHASSIS(cfg)->c_id != NULL && | |
324 | LOCAL_CHASSIS(cfg)->c_id_subtype == LLDP_CHASSISID_SUBTYPE_LLADDR) | |
325 | return; /* We already have one */ | |
326 | ||
327 | TAILQ_FOREACH(iface, interfaces, next) { | |
790752d0 | 328 | if (!(iface->type & IFACE_PHYSICAL_T)) continue; |
adbb6e54 VB |
329 | if (cfg->g_config.c_cid_pattern && |
330 | !pattern_match(iface->name, cfg->g_config.c_cid_pattern, 0)) continue; | |
331 | ||
332 | if ((hardware = lldpd_get_hardware(cfg, | |
333 | iface->name, | |
334 | iface->index, | |
335 | NULL)) == NULL) | |
336 | /* That's odd. Let's skip. */ | |
337 | continue; | |
338 | ||
ed409ccd | 339 | name = malloc(ETHER_ADDR_LEN); |
adbb6e54 VB |
340 | if (!name) { |
341 | log_warn("interfaces", "not enough memory for chassis ID"); | |
342 | return; | |
343 | } | |
344 | free(LOCAL_CHASSIS(cfg)->c_id); | |
ed409ccd | 345 | memcpy(name, hardware->h_lladdr, ETHER_ADDR_LEN); |
adbb6e54 | 346 | LOCAL_CHASSIS(cfg)->c_id = name; |
ed409ccd | 347 | LOCAL_CHASSIS(cfg)->c_id_len = ETHER_ADDR_LEN; |
adbb6e54 VB |
348 | LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR; |
349 | return; | |
350 | } | |
351 | } | |
352 | ||
0c6a1966 | 353 | #undef IN_IS_ADDR_LOOPBACK |
adbb6e54 | 354 | #define IN_IS_ADDR_LOOPBACK(a) ((a)->s_addr == htonl(INADDR_LOOPBACK)) |
0c6a1966 | 355 | #undef IN_IS_ADDR_ANY |
c3e340b6 | 356 | #define IN_IS_ADDR_ANY(a) ((a)->s_addr == htonl(INADDR_ANY)) |
4b67a7b2 VB |
357 | #undef IN_IS_ADDR_LINKLOCAL |
358 | #define IN_IS_ADDR_LINKLOCAL(a) (((a)->s_addr & htonl(0xffff0000)) == htonl(0xa9fe0000)) | |
0c6a1966 | 359 | #undef IN_IS_ADDR_GLOBAL |
4b67a7b2 | 360 | #define IN_IS_ADDR_GLOBAL(a) (!IN_IS_ADDR_LOOPBACK(a) && !IN_IS_ADDR_ANY(a) && !IN_IS_ADDR_LINKLOCAL(a)) |
0c6a1966 | 361 | #undef IN6_IS_ADDR_GLOBAL |
adbb6e54 VB |
362 | #define IN6_IS_ADDR_GLOBAL(a) \ |
363 | (!IN6_IS_ADDR_LOOPBACK(a) && !IN6_IS_ADDR_LINKLOCAL(a)) | |
adbb6e54 | 364 | |
63d2c3f8 VB |
365 | /* Add management addresses for the given family. We only take one of each |
366 | address family, unless a pattern is provided and is not all negative. For | |
367 | example !*:*,!10.* will only blacklist addresses. We will pick the first IPv4 | |
368 | address not matching 10.*. | |
369 | */ | |
370 | static int | |
371 | interfaces_helper_mgmt_for_af(struct lldpd *cfg, | |
372 | int af, | |
373 | struct interfaces_address_list *addrs, | |
374 | int global, int allnegative) | |
375 | { | |
376 | struct interfaces_address *addr; | |
377 | struct lldpd_mgmt *mgmt; | |
378 | char addrstrbuf[INET6_ADDRSTRLEN]; | |
379 | int found = 0; | |
380 | void *sin_addr_ptr; | |
381 | size_t sin_addr_size; | |
382 | ||
383 | TAILQ_FOREACH(addr, addrs, next) { | |
384 | if (addr->address.ss_family != lldpd_af(af)) | |
385 | continue; | |
386 | ||
387 | switch (af) { | |
388 | case LLDPD_AF_IPV4: | |
389 | sin_addr_ptr = &((struct sockaddr_in *)&addr->address)->sin_addr; | |
390 | sin_addr_size = sizeof(struct in_addr); | |
391 | if (global) { | |
392 | if (!IN_IS_ADDR_GLOBAL((struct in_addr *)sin_addr_ptr)) | |
393 | continue; | |
394 | } else { | |
395 | if (!IN_IS_ADDR_LINKLOCAL((struct in_addr *)sin_addr_ptr)) | |
396 | continue; | |
397 | } | |
398 | break; | |
399 | case LLDPD_AF_IPV6: | |
400 | sin_addr_ptr = &((struct sockaddr_in6 *)&addr->address)->sin6_addr; | |
401 | sin_addr_size = sizeof(struct in6_addr); | |
402 | if (global) { | |
403 | if (!IN6_IS_ADDR_GLOBAL((struct in6_addr *)sin_addr_ptr)) | |
404 | continue; | |
405 | } else { | |
406 | if (!IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)sin_addr_ptr)) | |
407 | continue; | |
408 | } | |
409 | break; | |
410 | default: | |
411 | assert(0); | |
412 | continue; | |
413 | } | |
414 | if (inet_ntop(lldpd_af(af), sin_addr_ptr, | |
415 | addrstrbuf, sizeof(addrstrbuf)) == NULL) { | |
416 | log_warn("interfaces", "unable to convert IP address to a string"); | |
417 | continue; | |
418 | } | |
419 | if (cfg->g_config.c_mgmt_pattern == NULL || | |
420 | pattern_match(addrstrbuf, cfg->g_config.c_mgmt_pattern, allnegative)) { | |
421 | mgmt = lldpd_alloc_mgmt(af, sin_addr_ptr, sin_addr_size, | |
422 | addr->index); | |
423 | if (mgmt == NULL) { | |
424 | assert(errno == ENOMEM); /* anything else is a bug */ | |
425 | log_warn("interfaces", "out of memory error"); | |
426 | return found; | |
427 | } | |
428 | log_debug("interfaces", "add management address %s", addrstrbuf); | |
429 | TAILQ_INSERT_TAIL(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries); | |
430 | found = 1; | |
431 | ||
432 | /* Don't take additional address if the pattern is all negative. */ | |
433 | if (allnegative) break; | |
434 | } | |
435 | } | |
436 | return found; | |
437 | } | |
438 | ||
adbb6e54 VB |
439 | /* Find a management address in all available interfaces, even those that were |
440 | already handled. This is a special interface handler because it does not | |
441 | really handle interface related information (management address is attached | |
442 | to the local chassis). */ | |
443 | void | |
444 | interfaces_helper_mgmt(struct lldpd *cfg, | |
445 | struct interfaces_address_list *addrs) | |
446 | { | |
adbb6e54 | 447 | int allnegative = 0; |
63d2c3f8 | 448 | int af; |
abfea7d0 | 449 | const char *pattern = cfg->g_config.c_mgmt_pattern; |
adbb6e54 VB |
450 | |
451 | lldpd_chassis_mgmt_cleanup(LOCAL_CHASSIS(cfg)); | |
1c2217aa AA |
452 | if (!cfg->g_config.c_mgmt_advertise) |
453 | return; | |
adbb6e54 | 454 | |
abfea7d0 VB |
455 | /* Is the pattern provided an actual IP address? */ |
456 | if (pattern && strpbrk(pattern, "!,*?") == NULL) { | |
457 | struct in6_addr addr; | |
458 | size_t addr_size; | |
459 | for (af = LLDPD_AF_UNSPEC + 1; | |
460 | af != LLDPD_AF_LAST; af++) { | |
461 | switch (af) { | |
462 | case LLDPD_AF_IPV4: addr_size = sizeof(struct in_addr); break; | |
463 | case LLDPD_AF_IPV6: addr_size = sizeof(struct in6_addr); break; | |
464 | default: assert(0); | |
465 | } | |
466 | if (inet_pton(lldpd_af(af), pattern, &addr) == 1) | |
467 | break; | |
468 | } | |
469 | if (af == LLDPD_AF_LAST) { | |
470 | log_debug("interfaces", | |
471 | "interface management pattern is an incorrect IP"); | |
472 | } else { | |
473 | struct lldpd_mgmt *mgmt; | |
474 | mgmt = lldpd_alloc_mgmt(af, &addr, addr_size, 0); | |
475 | if (mgmt == NULL) { | |
476 | log_warn("interfaces", "out of memory error"); | |
477 | return; | |
478 | } | |
479 | log_debug("interfaces", "add exact management address %s", | |
480 | pattern); | |
481 | TAILQ_INSERT_TAIL(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries); | |
482 | } | |
483 | return; | |
484 | } | |
485 | ||
adbb6e54 | 486 | /* Is the pattern provided all negative? */ |
abfea7d0 VB |
487 | if (pattern == NULL) allnegative = 1; |
488 | else if (pattern[0] == '!') { | |
adbb6e54 VB |
489 | /* If each comma is followed by '!', its an all |
490 | negative pattern */ | |
abfea7d0 | 491 | const char *sep = pattern; |
adbb6e54 VB |
492 | while ((sep = strchr(sep, ',')) && |
493 | (*(++sep) == '!')); | |
494 | if (sep == NULL) allnegative = 1; | |
495 | } | |
496 | ||
497 | /* Find management addresses */ | |
498 | for (af = LLDPD_AF_UNSPEC + 1; af != LLDPD_AF_LAST; af++) { | |
63d2c3f8 VB |
499 | (void)(interfaces_helper_mgmt_for_af(cfg, af, addrs, 1, allnegative) || |
500 | interfaces_helper_mgmt_for_af(cfg, af, addrs, 0, allnegative)); | |
adbb6e54 VB |
501 | } |
502 | } | |
503 | ||
504 | /* Fill up port name and description */ | |
505 | void | |
8fbd3195 ST |
506 | interfaces_helper_port_name_desc(struct lldpd *cfg, |
507 | struct lldpd_hardware *hardware, | |
adbb6e54 VB |
508 | struct interfaces_device *iface) |
509 | { | |
510 | struct lldpd_port *port = &hardware->h_lport; | |
511 | ||
8fbd3195 ST |
512 | /* We need to set the portid to what the client configured. |
513 | This can be done from the CLI. | |
adbb6e54 | 514 | */ |
a403df66 VB |
515 | int has_alias = (iface->alias != NULL && strlen(iface->alias) != 0); |
516 | int portid_type = cfg->g_config.c_lldp_portid_type; | |
517 | if (portid_type == LLDP_PORTID_SUBTYPE_IFNAME || | |
518 | (portid_type == LLDP_PORTID_SUBTYPE_UNKNOWN && has_alias) || | |
519 | (portid_type == LLDP_PORTID_SUBTYPE_LOCAL && has_alias)) { | |
520 | if (portid_type != LLDP_PORTID_SUBTYPE_LOCAL) { | |
521 | log_debug("interfaces", "use ifname for %s", | |
522 | hardware->h_ifname); | |
523 | port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME; | |
524 | port->p_id_len = strlen(hardware->h_ifname); | |
525 | free(port->p_id); | |
526 | if ((port->p_id = calloc(1, port->p_id_len)) == NULL) | |
527 | fatal("interfaces", NULL); | |
528 | memcpy(port->p_id, hardware->h_ifname, port->p_id_len); | |
529 | } | |
8fbd3195 | 530 | |
8fbd3195 ST |
531 | /* use the actual alias in the port description */ |
532 | log_debug("interfaces", "using alias in description for %s", | |
533 | hardware->h_ifname); | |
4db5e8c6 | 534 | free(port->p_descr); |
a403df66 VB |
535 | if (has_alias) { |
536 | port->p_descr = strdup(iface->alias); | |
48eaeeb7 VB |
537 | } else { |
538 | /* We don't have anything else to put here and for CDP | |
539 | * with need something non-NULL */ | |
540 | port->p_descr = strdup(hardware->h_ifname); | |
a403df66 | 541 | } |
8fbd3195 | 542 | } else { |
a403df66 VB |
543 | if (portid_type != LLDP_PORTID_SUBTYPE_LOCAL) { |
544 | log_debug("interfaces", "use MAC address for %s", | |
545 | hardware->h_ifname); | |
546 | port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR; | |
547 | free(port->p_id); | |
548 | if ((port->p_id = calloc(1, ETHER_ADDR_LEN)) == NULL) | |
549 | fatal("interfaces", NULL); | |
550 | memcpy(port->p_id, hardware->h_lladdr, ETHER_ADDR_LEN); | |
551 | port->p_id_len = ETHER_ADDR_LEN; | |
552 | } | |
553 | ||
8fbd3195 ST |
554 | /* use the ifname in the port description until alias is set */ |
555 | log_debug("interfaces", "using ifname in description for %s", | |
556 | hardware->h_ifname); | |
4db5e8c6 | 557 | free(port->p_descr); |
adbb6e54 | 558 | port->p_descr = strdup(hardware->h_ifname); |
adbb6e54 | 559 | } |
adbb6e54 VB |
560 | } |
561 | ||
bdfe4193 VB |
562 | void |
563 | interfaces_helper_add_hardware(struct lldpd *cfg, | |
564 | struct lldpd_hardware *hardware) | |
565 | { | |
566 | TRACE(LLDPD_INTERFACES_NEW(hardware->h_ifname)); | |
567 | TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries); | |
568 | } | |
569 | ||
adbb6e54 VB |
570 | void |
571 | interfaces_helper_physical(struct lldpd *cfg, | |
88bc404f | 572 | struct interfaces_device_list *interfaces, |
22e8cd65 | 573 | struct lldpd_ops *ops, |
88bc404f | 574 | int(*init)(struct lldpd *, struct lldpd_hardware *)) |
adbb6e54 VB |
575 | { |
576 | struct interfaces_device *iface; | |
577 | struct lldpd_hardware *hardware; | |
578 | ||
579 | TAILQ_FOREACH(iface, interfaces, next) { | |
c6aa43e5 | 580 | if (!(iface->type & IFACE_PHYSICAL_T)) continue; |
0fa2254b | 581 | if (iface->ignore) continue; |
adbb6e54 VB |
582 | |
583 | log_debug("interfaces", "%s is an acceptable ethernet device", | |
584 | iface->name); | |
585 | if ((hardware = lldpd_get_hardware(cfg, | |
586 | iface->name, | |
587 | iface->index, | |
22e8cd65 | 588 | ops)) == NULL) { |
adbb6e54 VB |
589 | if ((hardware = lldpd_alloc_hardware(cfg, |
590 | iface->name, | |
591 | iface->index)) == NULL) { | |
592 | log_warnx("interfaces", "Unable to allocate space for %s", | |
593 | iface->name); | |
594 | continue; | |
595 | } | |
88bc404f | 596 | if (init(cfg, hardware) != 0) { |
e735a319 | 597 | log_warnx("interfaces", |
adbb6e54 VB |
598 | "unable to initialize %s", |
599 | hardware->h_ifname); | |
600 | lldpd_hardware_cleanup(cfg, hardware); | |
601 | continue; | |
602 | } | |
22e8cd65 | 603 | hardware->h_ops = ops; |
7d758eec VB |
604 | hardware->h_mangle = (iface->upper && |
605 | iface->upper->type & IFACE_BOND_T); | |
bdfe4193 | 606 | interfaces_helper_add_hardware(cfg, hardware); |
adbb6e54 VB |
607 | } else { |
608 | if (hardware->h_flags) continue; /* Already seen this time */ | |
609 | lldpd_port_cleanup(&hardware->h_lport, 0); | |
610 | } | |
611 | ||
612 | hardware->h_flags = iface->flags; /* Should be non-zero */ | |
0fa2254b | 613 | iface->ignore = 1; /* Future handlers |
adbb6e54 VB |
614 | don't have to |
615 | care about this | |
616 | interface. */ | |
617 | ||
618 | /* Get local address */ | |
4e5f34c5 | 619 | memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN); |
adbb6e54 VB |
620 | |
621 | /* Fill information about port */ | |
8fbd3195 | 622 | interfaces_helper_port_name_desc(cfg, hardware, iface); |
adbb6e54 VB |
623 | |
624 | /* Fill additional info */ | |
625 | hardware->h_mtu = iface->mtu ? iface->mtu : 1500; | |
d60d0bd7 VB |
626 | |
627 | #ifdef ENABLE_DOT3 | |
628 | if (iface->upper && iface->upper->type & IFACE_BOND_T) | |
629 | hardware->h_lport.p_aggregid = iface->upper->index; | |
630 | #endif | |
adbb6e54 VB |
631 | } |
632 | } | |
99704cd9 | 633 | |
f84199dd VB |
634 | void |
635 | interfaces_helper_promisc(struct lldpd *cfg, | |
636 | struct lldpd_hardware *hardware) | |
637 | { | |
638 | if (!cfg->g_config.c_promisc) return; | |
639 | if (priv_iface_promisc(hardware->h_ifname) != 0) { | |
640 | log_warnx("interfaces", | |
641 | "unable to enable promiscuous mode for %s", | |
642 | hardware->h_ifname); | |
643 | } | |
644 | } | |
645 | ||
99704cd9 | 646 | /** |
5347914e | 647 | * Send the packet using the hardware function. Optionnaly mangle the MAC address. |
99704cd9 VB |
648 | * |
649 | * With bonds, we have duplicate MAC address on different physical | |
5347914e VB |
650 | * interfaces. We need to alter the source MAC address when we send on an |
651 | * inactive slave. The `h_mangle` flah is used to know if we need to do | |
652 | * something like that. | |
99704cd9 | 653 | */ |
5347914e VB |
654 | int |
655 | interfaces_send_helper(struct lldpd *cfg, | |
656 | struct lldpd_hardware *hardware, | |
657 | char *buffer, size_t size) | |
99704cd9 | 658 | { |
5347914e VB |
659 | if (size < 2 * ETHER_ADDR_LEN) { |
660 | log_warnx("interfaces", | |
661 | "packet to send on %s is too small!", | |
662 | hardware->h_ifname); | |
663 | return 0; | |
664 | } | |
665 | if (hardware->h_mangle) { | |
99704cd9 | 666 | #define MAC_UL_ADMINISTERED_BIT_MASK 0x02 |
5347914e VB |
667 | char *src_mac = buffer + ETHER_ADDR_LEN; |
668 | char arbitrary[] = { 0x00, 0x60, 0x08, 0x69, 0x97, 0xef}; | |
669 | ||
670 | switch (cfg->g_config.c_bond_slave_src_mac_type) { | |
671 | case LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED: | |
c6d4d322 VB |
672 | if (!(*src_mac & MAC_UL_ADMINISTERED_BIT_MASK)) { |
673 | *src_mac |= MAC_UL_ADMINISTERED_BIT_MASK; | |
5347914e VB |
674 | break; |
675 | } | |
c6d4d322 | 676 | /* Fallback to fixed value */ |
5347914e VB |
677 | case LLDP_BOND_SLAVE_SRC_MAC_TYPE_FIXED: |
678 | memcpy(src_mac, arbitrary, ETHER_ADDR_LEN); | |
679 | break; | |
680 | case LLDP_BOND_SLAVE_SRC_MAC_TYPE_ZERO: | |
99704cd9 | 681 | memset(src_mac, 0, ETHER_ADDR_LEN); |
5347914e | 682 | break; |
99704cd9 | 683 | } |
99704cd9 | 684 | } |
5347914e | 685 | return hardware->h_ops->send(cfg, hardware, buffer, size); |
99704cd9 | 686 | } |