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