]>
Commit | Line | Data |
---|---|---|
4b292b55 | 1 | /* -*- mode: c; c-file-style: "openbsd" -*- */ |
43c02e7b VB |
2 | /* |
3 | * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> | |
4 | * | |
51434125 | 5 | * Permission to use, copy, modify, and/or distribute this software for any |
43c02e7b VB |
6 | * purpose with or without fee is hereby granted, provided that the above |
7 | * copyright notice and this permission notice appear in all copies. | |
8 | * | |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | */ | |
17 | ||
18 | #include "lldpd.h" | |
bdfe4193 | 19 | #include "trace.h" |
43c02e7b VB |
20 | |
21 | #include <stdio.h> | |
22 | #include <unistd.h> | |
23 | #include <errno.h> | |
24 | #include <signal.h> | |
25 | #include <sys/stat.h> | |
26 | #include <fcntl.h> | |
43c02e7b | 27 | #include <time.h> |
a2993d83 | 28 | #include <libgen.h> |
e6b36c87 | 29 | #include <assert.h> |
43c02e7b VB |
30 | #include <sys/utsname.h> |
31 | #include <sys/types.h> | |
c036b15d | 32 | #include <sys/wait.h> |
43c02e7b VB |
33 | #include <sys/socket.h> |
34 | #include <sys/select.h> | |
35 | #include <sys/time.h> | |
36 | #include <sys/ioctl.h> | |
37 | #include <arpa/inet.h> | |
690b944c | 38 | #include <netinet/if_ether.h> |
2e91d1a1 VB |
39 | #include <pwd.h> |
40 | #include <grp.h> | |
43c02e7b | 41 | |
8888d191 | 42 | static void usage(void); |
43c02e7b | 43 | |
8888d191 | 44 | static struct protocol protos[] = |
43c02e7b | 45 | { |
0c877af0 | 46 | { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL, |
a98ed042 VB |
47 | LLDP_ADDR_NEAREST_BRIDGE, |
48 | LLDP_ADDR_NEAREST_NONTPMR_BRIDGE, | |
49 | LLDP_ADDR_NEAREST_CUSTOMER_BRIDGE }, | |
4bad1937 | 50 | #ifdef ENABLE_CDP |
43c02e7b | 51 | { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess, |
f2dcb180 | 52 | CDP_MULTICAST_ADDR }, |
43c02e7b | 53 | { LLDPD_MODE_CDPV2, 0, "CDPv2", 'c', cdpv2_send, cdp_decode, cdpv2_guess, |
f2dcb180 | 54 | CDP_MULTICAST_ADDR }, |
4bad1937 VB |
55 | #endif |
56 | #ifdef ENABLE_SONMP | |
43c02e7b | 57 | { LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL, |
f2dcb180 | 58 | SONMP_MULTICAST_ADDR }, |
4bad1937 VB |
59 | #endif |
60 | #ifdef ENABLE_EDP | |
43c02e7b | 61 | { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL, |
f2dcb180 | 62 | EDP_MULTICAST_ADDR }, |
4bad1937 VB |
63 | #endif |
64 | #ifdef ENABLE_FDP | |
031118c4 | 65 | { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL, |
f2dcb180 | 66 | FDP_MULTICAST_ADDR }, |
4bad1937 | 67 | #endif |
43c02e7b | 68 | { 0, 0, "any", ' ', NULL, NULL, NULL, |
f2dcb180 | 69 | {0,0,0,0,0,0} } |
43c02e7b VB |
70 | }; |
71 | ||
8888d191 | 72 | static char **saved_argv; |
6bb9c4e0 VB |
73 | #ifdef HAVE___PROGNAME |
74 | extern const char *__progname; | |
75 | #else | |
76 | # define __progname "lldpd" | |
77 | #endif | |
43c02e7b | 78 | |
8888d191 | 79 | static void |
43c02e7b VB |
80 | usage(void) |
81 | { | |
4593c0dc VB |
82 | fprintf(stderr, "Usage: %s [OPTIONS ...]\n", __progname); |
83 | fprintf(stderr, "Version: %s\n", PACKAGE_STRING); | |
53132616 VB |
84 | |
85 | fprintf(stderr, "\n"); | |
86 | ||
87 | fprintf(stderr, "-d Do not daemonize.\n"); | |
5e64a80e | 88 | fprintf(stderr, "-r Receive-only mode\n"); |
53132616 VB |
89 | fprintf(stderr, "-i Disable LLDP-MED inventory TLV transmission.\n"); |
90 | fprintf(stderr, "-k Disable advertising of kernel release, version, machine.\n"); | |
fde7a7ce | 91 | fprintf(stderr, "-S descr Override the default system description.\n"); |
d4afb919 | 92 | fprintf(stderr, "-P name Override the default hardware platform.\n"); |
d4e4c804 | 93 | fprintf(stderr, "-m IP Specify the IPv4 management addresses of this system.\n"); |
0262adbb | 94 | fprintf(stderr, "-u file Specify the Unix-domain socket used for communication with lldpctl(8).\n"); |
42b39485 | 95 | fprintf(stderr, "-H mode Specify the behaviour when detecting multiple neighbors.\n"); |
fbda1f9f | 96 | fprintf(stderr, "-I iface Limit interfaces to use.\n"); |
8347587d | 97 | #ifdef ENABLE_LLDPMED |
53132616 VB |
98 | fprintf(stderr, "-M class Enable emission of LLDP-MED frame. 'class' should be one of:\n"); |
99 | fprintf(stderr, " 1 Generic Endpoint (Class I)\n"); | |
100 | fprintf(stderr, " 2 Media Endpoint (Class II)\n"); | |
101 | fprintf(stderr, " 3 Communication Device Endpoints (Class III)\n"); | |
102 | fprintf(stderr, " 4 Network Connectivity Device\n"); | |
8347587d VB |
103 | #endif |
104 | #ifdef USE_SNMP | |
53132616 | 105 | fprintf(stderr, "-x Enable SNMP subagent.\n"); |
53132616 VB |
106 | #endif |
107 | fprintf(stderr, "\n"); | |
108 | ||
8347587d VB |
109 | #if defined ENABLE_CDP || defined ENABLE_EDP || defined ENABLE_FDP || defined ENABLE_SONMP |
110 | fprintf(stderr, "Additional protocol support.\n"); | |
111 | #ifdef ENABLE_CDP | |
53132616 | 112 | fprintf(stderr, "-c Enable the support of CDP protocol. (Cisco)\n"); |
8347587d VB |
113 | #endif |
114 | #ifdef ENABLE_EDP | |
53132616 | 115 | fprintf(stderr, "-e Enable the support of EDP protocol. (Extreme)\n"); |
8347587d VB |
116 | #endif |
117 | #ifdef ENABLE_FDP | |
53132616 | 118 | fprintf(stderr, "-f Enable the support of FDP protocol. (Foundry)\n"); |
8347587d VB |
119 | #endif |
120 | #ifdef ENABLE_SONMP | |
53132616 | 121 | fprintf(stderr, "-s Enable the support of SONMP protocol. (Nortel)\n"); |
8347587d | 122 | #endif |
53132616 VB |
123 | |
124 | fprintf(stderr, "\n"); | |
8347587d | 125 | #endif |
53132616 | 126 | |
e809a587 | 127 | fprintf(stderr, "see manual page lldpd(8) for more information\n"); |
43c02e7b VB |
128 | exit(1); |
129 | } | |
130 | ||
6e75df87 | 131 | struct lldpd_hardware * |
32945d6a | 132 | lldpd_get_hardware(struct lldpd *cfg, char *name, int index) |
43c02e7b | 133 | { |
6e75df87 VB |
134 | struct lldpd_hardware *hardware; |
135 | TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { | |
849954d7 | 136 | if ((strcmp(hardware->h_ifname, name) == 0) && |
32945d6a | 137 | (hardware->h_ifindex == index)) |
6e75df87 | 138 | break; |
43c02e7b | 139 | } |
6e75df87 | 140 | return hardware; |
43c02e7b VB |
141 | } |
142 | ||
9da663f7 VB |
143 | /** |
144 | * Allocate the default local port. This port will be cloned each time we need a | |
145 | * new local port. | |
146 | */ | |
147 | static void | |
148 | lldpd_alloc_default_local_port(struct lldpd *cfg) | |
149 | { | |
150 | struct lldpd_port *port; | |
151 | ||
152 | if ((port = (struct lldpd_port *) | |
153 | calloc(1, sizeof(struct lldpd_port))) == NULL) | |
154 | fatal("main", NULL); | |
155 | ||
156 | #ifdef ENABLE_DOT1 | |
157 | TAILQ_INIT(&port->p_vlans); | |
158 | TAILQ_INIT(&port->p_ppvids); | |
159 | TAILQ_INIT(&port->p_pids); | |
160 | #endif | |
161 | #ifdef ENABLE_CUSTOM | |
162 | TAILQ_INIT(&port->p_custom_list); | |
163 | #endif | |
164 | cfg->g_default_local_port = port; | |
165 | } | |
166 | ||
167 | /** | |
168 | * Clone a given port. The destination needs to be already allocated. | |
169 | */ | |
170 | static int | |
171 | lldpd_clone_port(struct lldpd_port *destination, struct lldpd_port *source) | |
172 | { | |
173 | ||
174 | u_int8_t *output = NULL; | |
175 | ssize_t output_len; | |
176 | struct lldpd_port *cloned = NULL; | |
177 | output_len = lldpd_port_serialize(source, (void**)&output); | |
178 | if (output_len == -1 || | |
179 | lldpd_port_unserialize(output, output_len, &cloned) <= 0) { | |
180 | log_warnx("alloc", "unable to clone default port"); | |
c31f0d8a VB |
181 | free(output); |
182 | return -1; | |
9da663f7 VB |
183 | } |
184 | memcpy(destination, cloned, sizeof(struct lldpd_port)); | |
c31f0d8a VB |
185 | free(cloned); |
186 | free(output); | |
9da663f7 VB |
187 | #ifdef ENABLE_DOT1 |
188 | marshal_repair_tailq(lldpd_vlan, &destination->p_vlans, v_entries); | |
189 | marshal_repair_tailq(lldpd_ppvid, &destination->p_ppvids, p_entries); | |
190 | marshal_repair_tailq(lldpd_pi, &destination->p_pids, p_entries); | |
191 | #endif | |
192 | #ifdef ENABLE_CUSTOM | |
193 | marshal_repair_tailq(lldpd_custom, &destination->p_custom_list, next); | |
194 | #endif | |
195 | return 0; | |
9da663f7 VB |
196 | } |
197 | ||
6e75df87 | 198 | struct lldpd_hardware * |
e12c2365 | 199 | lldpd_alloc_hardware(struct lldpd *cfg, char *name, int index) |
43c02e7b | 200 | { |
6e75df87 | 201 | struct lldpd_hardware *hardware; |
43c02e7b | 202 | |
6f8925be VB |
203 | log_debug("alloc", "allocate a new local port (%s)", name); |
204 | ||
6e75df87 VB |
205 | if ((hardware = (struct lldpd_hardware *) |
206 | calloc(1, sizeof(struct lldpd_hardware))) == NULL) | |
207 | return NULL; | |
43c02e7b | 208 | |
9da663f7 VB |
209 | /* Clone default local port */ |
210 | if (lldpd_clone_port(&hardware->h_lport, cfg->g_default_local_port) == -1) { | |
211 | log_warnx("alloc", "unable to clone default port"); | |
212 | free(hardware); | |
213 | return NULL; | |
214 | } | |
215 | ||
d6e889b6 | 216 | hardware->h_cfg = cfg; |
6e75df87 | 217 | strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname)); |
e12c2365 | 218 | hardware->h_ifindex = index; |
6e75df87 | 219 | hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg); |
9df5ec3d | 220 | hardware->h_lport.p_chassis->c_refcount++; |
6e75df87 | 221 | TAILQ_INIT(&hardware->h_rports); |
43c02e7b | 222 | |
6e75df87 VB |
223 | #ifdef ENABLE_LLDPMED |
224 | if (LOCAL_CHASSIS(cfg)->c_med_cap_available) { | |
4b292b55 | 225 | hardware->h_lport.p_med_cap_enabled = LLDP_MED_CAP_CAP; |
8ec333bd | 226 | if (!cfg->g_config.c_noinventory) |
4b292b55 | 227 | hardware->h_lport.p_med_cap_enabled |= LLDP_MED_CAP_IV; |
6e75df87 VB |
228 | } |
229 | #endif | |
d6e889b6 VB |
230 | |
231 | levent_hardware_init(hardware); | |
6e75df87 | 232 | return hardware; |
43c02e7b VB |
233 | } |
234 | ||
e6b36c87 JV |
235 | struct lldpd_mgmt * |
236 | lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface) | |
237 | { | |
238 | struct lldpd_mgmt *mgmt; | |
6f8925be VB |
239 | |
240 | log_debug("alloc", "allocate a new management address (family: %d)", family); | |
241 | ||
e6b36c87 JV |
242 | if (family <= LLDPD_AF_UNSPEC || family >= LLDPD_AF_LAST) { |
243 | errno = EAFNOSUPPORT; | |
244 | return NULL; | |
245 | } | |
246 | if (addrsize > LLDPD_MGMT_MAXADDRSIZE) { | |
247 | errno = EOVERFLOW; | |
248 | return NULL; | |
249 | } | |
250 | mgmt = calloc(1, sizeof(struct lldpd_mgmt)); | |
251 | if (mgmt == NULL) { | |
252 | errno = ENOMEM; | |
253 | return NULL; | |
254 | } | |
255 | mgmt->m_family = family; | |
e6b36c87 JV |
256 | memcpy(&mgmt->m_addr, addrptr, addrsize); |
257 | mgmt->m_addrsize = addrsize; | |
258 | mgmt->m_iface = iface; | |
259 | return mgmt; | |
260 | } | |
261 | ||
d9be8ea0 | 262 | void |
6e75df87 | 263 | lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware) |
d9be8ea0 | 264 | { |
6f8925be VB |
265 | log_debug("alloc", "cleanup hardware port %s", hardware->h_ifname); |
266 | ||
17d34115 | 267 | free(hardware->h_lport_previous); |
acb5f65b VB |
268 | free(hardware->h_lchassis_previous_id); |
269 | free(hardware->h_lport_previous_id); | |
4b292b55 | 270 | lldpd_port_cleanup(&hardware->h_lport, 1); |
dbfa89c6 | 271 | if (hardware->h_ops && hardware->h_ops->cleanup) |
6e75df87 | 272 | hardware->h_ops->cleanup(cfg, hardware); |
d6e889b6 | 273 | levent_hardware_release(hardware); |
d9be8ea0 VB |
274 | free(hardware); |
275 | } | |
276 | ||
47820fc4 VB |
277 | static void |
278 | lldpd_display_neighbors(struct lldpd *cfg) | |
279 | { | |
bb37268d | 280 | if (!cfg->g_config.c_set_ifdescr) return; |
47820fc4 VB |
281 | struct lldpd_hardware *hardware; |
282 | TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { | |
283 | struct lldpd_port *port; | |
284 | char *description; | |
285 | const char *neighbor = NULL; | |
286 | unsigned neighbors = 0; | |
287 | TAILQ_FOREACH(port, &hardware->h_rports, p_entries) { | |
288 | if (SMART_HIDDEN(port)) continue; | |
289 | neighbors++; | |
290 | neighbor = port->p_chassis->c_name; | |
291 | } | |
292 | if (neighbors == 0) | |
293 | priv_iface_description(hardware->h_ifname, | |
015ecdd5 | 294 | ""); |
12313820 | 295 | else if (neighbors == 1 && neighbor && *neighbor != '\0') { |
015ecdd5 | 296 | if (asprintf(&description, "%s", |
47820fc4 VB |
297 | neighbor) != -1) { |
298 | priv_iface_description(hardware->h_ifname, description); | |
299 | free(description); | |
300 | } | |
301 | } else { | |
015ecdd5 | 302 | if (asprintf(&description, "%d neighbor%s", |
47820fc4 VB |
303 | neighbors, (neighbors > 1)?"s":"") != -1) { |
304 | priv_iface_description(hardware->h_ifname, | |
305 | description); | |
306 | free(description); | |
307 | } | |
308 | } | |
309 | } | |
310 | } | |
311 | ||
66879d46 VB |
312 | static void |
313 | lldpd_count_neighbors(struct lldpd *cfg) | |
314 | { | |
e7103aff | 315 | #if HAVE_SETPROCTITLE |
480f1c61 VB |
316 | struct lldpd_chassis *chassis; |
317 | const char *neighbor; | |
66879d46 | 318 | unsigned neighbors = 0; |
480f1c61 VB |
319 | TAILQ_FOREACH(chassis, &cfg->g_chassis, c_entries) { |
320 | neighbors++; | |
321 | neighbor = chassis->c_name; | |
66879d46 | 322 | } |
480f1c61 VB |
323 | neighbors--; |
324 | if (neighbors == 0) | |
bd36a4d3 | 325 | setproctitle("no neighbor."); |
12313820 | 326 | else if (neighbors == 1 && neighbor && *neighbor != '\0') |
bd36a4d3 | 327 | setproctitle("connected to %s.", neighbor); |
480f1c61 | 328 | else |
bd36a4d3 | 329 | setproctitle("%d neighbor%s.", neighbors, |
480f1c61 | 330 | (neighbors > 1)?"s":""); |
e7103aff | 331 | #endif |
47820fc4 | 332 | lldpd_display_neighbors(cfg); |
66879d46 VB |
333 | } |
334 | ||
4e90a9e0 VB |
335 | static void |
336 | notify_clients_deletion(struct lldpd_hardware *hardware, | |
25de85a4 | 337 | struct lldpd_port *rport) |
4e90a9e0 | 338 | { |
bdfe4193 VB |
339 | TRACE(LLDPD_NEIGHBOR_DELETE(hardware->h_ifname, |
340 | rport->p_chassis->c_name, | |
341 | rport->p_descr)); | |
4e90a9e0 | 342 | levent_ctl_notify(hardware->h_ifname, NEIGHBOR_CHANGE_DELETED, |
25de85a4 VB |
343 | rport); |
344 | #ifdef USE_SNMP | |
345 | agent_notify(hardware, NEIGHBOR_CHANGE_DELETED, rport); | |
346 | #endif | |
4e90a9e0 VB |
347 | } |
348 | ||
579bedd5 VB |
349 | static void |
350 | lldpd_reset_timer(struct lldpd *cfg) | |
351 | { | |
352 | /* Reset timer for ports that have been changed. */ | |
353 | struct lldpd_hardware *hardware; | |
354 | TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { | |
17d34115 VB |
355 | /* We keep a flat copy of the local port to see if there is any |
356 | * change. To do this, we zero out fields that are not | |
357 | * significant, marshal the port, then restore. */ | |
579bedd5 | 358 | struct lldpd_port *port = &hardware->h_lport; |
196757ce VB |
359 | /* Take the current flags into account to detect a change. */ |
360 | port->_p_hardware_flags = hardware->h_flags; | |
579bedd5 | 361 | u_int8_t *output = NULL; |
324d73a3 | 362 | ssize_t output_len; |
28414fbd | 363 | char save[LLDPD_PORT_START_MARKER]; |
579bedd5 | 364 | memcpy(save, port, sizeof(save)); |
87dfd175 VB |
365 | /* coverity[suspicious_sizeof] |
366 | We intentionally partially memset port */ | |
579bedd5 | 367 | memset(port, 0, sizeof(save)); |
985a4cb5 | 368 | output_len = lldpd_port_serialize(port, (void**)&output); |
579bedd5 VB |
369 | memcpy(port, save, sizeof(save)); |
370 | if (output_len == -1) { | |
371 | log_warnx("localchassis", | |
372 | "unable to serialize local port %s to check for differences", | |
373 | hardware->h_ifname); | |
374 | continue; | |
375 | } | |
17d34115 VB |
376 | |
377 | /* Compare with the previous value */ | |
378 | if (hardware->h_lport_previous && | |
379 | output_len == hardware->h_lport_previous_len && | |
380 | !memcmp(output, hardware->h_lport_previous, output_len)) { | |
254e5134 | 381 | log_debug("localchassis", |
17d34115 | 382 | "no change detected for port %s", |
579bedd5 | 383 | hardware->h_ifname); |
579bedd5 VB |
384 | } else { |
385 | log_debug("localchassis", | |
17d34115 | 386 | "change detected for port %s, resetting its timer", |
579bedd5 | 387 | hardware->h_ifname); |
17d34115 | 388 | levent_schedule_pdu(hardware); |
579bedd5 | 389 | } |
17d34115 VB |
390 | |
391 | /* Update the value */ | |
392 | free(hardware->h_lport_previous); | |
393 | hardware->h_lport_previous = output; | |
394 | hardware->h_lport_previous_len = output_len; | |
579bedd5 VB |
395 | } |
396 | } | |
397 | ||
24cb9684 VB |
398 | static void |
399 | lldpd_all_chassis_cleanup(struct lldpd *cfg) | |
400 | { | |
401 | struct lldpd_chassis *chassis, *chassis_next; | |
402 | log_debug("localchassis", "cleanup all chassis"); | |
403 | ||
404 | for (chassis = TAILQ_FIRST(&cfg->g_chassis); chassis; | |
405 | chassis = chassis_next) { | |
406 | chassis_next = TAILQ_NEXT(chassis, c_entries); | |
407 | if (chassis->c_refcount == 0) { | |
408 | TAILQ_REMOVE(&cfg->g_chassis, chassis, c_entries); | |
409 | lldpd_chassis_cleanup(chassis, 1); | |
410 | } | |
411 | } | |
412 | } | |
413 | ||
3333d2a8 | 414 | void |
43c02e7b VB |
415 | lldpd_cleanup(struct lldpd *cfg) |
416 | { | |
417 | struct lldpd_hardware *hardware, *hardware_next; | |
418 | ||
3333d2a8 | 419 | log_debug("localchassis", "cleanup all ports"); |
6f8925be | 420 | |
43c02e7b VB |
421 | for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL; |
422 | hardware = hardware_next) { | |
423 | hardware_next = TAILQ_NEXT(hardware, h_entries); | |
6e75df87 | 424 | if (!hardware->h_flags) { |
bdfe4193 | 425 | TRACE(LLDPD_INTERFACES_DELETE(hardware->h_ifname)); |
43c02e7b | 426 | TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries); |
ef3707da | 427 | lldpd_remote_cleanup(hardware, notify_clients_deletion, 1); |
6e75df87 | 428 | lldpd_hardware_cleanup(cfg, hardware); |
0da01fd6 | 429 | } else { |
7f0877f0 VB |
430 | lldpd_remote_cleanup(hardware, notify_clients_deletion, |
431 | !(hardware->h_flags & IFF_RUNNING)); | |
0da01fd6 | 432 | } |
43c02e7b | 433 | } |
aadc9936 | 434 | |
3333d2a8 | 435 | levent_schedule_cleanup(cfg); |
24cb9684 VB |
436 | lldpd_all_chassis_cleanup(cfg); |
437 | lldpd_count_neighbors(cfg); | |
43c02e7b VB |
438 | } |
439 | ||
d938c51f VB |
440 | /* Update chassis `ochassis' with values from `chassis'. The later one is not |
441 | expected to be part of a list! It will also be wiped from memory. */ | |
566c635d | 442 | static void |
d938c51f VB |
443 | lldpd_move_chassis(struct lldpd_chassis *ochassis, |
444 | struct lldpd_chassis *chassis) { | |
5fd6695c VB |
445 | struct lldpd_mgmt *mgmt, *mgmt_next; |
446 | ||
566c635d VB |
447 | /* We want to keep refcount, index and list stuff from the current |
448 | * chassis */ | |
d938c51f | 449 | TAILQ_ENTRY(lldpd_chassis) entries; |
566c635d VB |
450 | int refcount = ochassis->c_refcount; |
451 | int index = ochassis->c_index; | |
452 | memcpy(&entries, &ochassis->c_entries, | |
453 | sizeof(entries)); | |
566c635d | 454 | lldpd_chassis_cleanup(ochassis, 0); |
d938c51f VB |
455 | |
456 | /* Make the copy. */ | |
457 | /* WARNING: this is a kludgy hack, we need in-place copy and cannot use | |
458 | * marshaling. */ | |
566c635d | 459 | memcpy(ochassis, chassis, sizeof(struct lldpd_chassis)); |
d938c51f VB |
460 | TAILQ_INIT(&ochassis->c_mgmt); |
461 | ||
462 | /* Copy of management addresses */ | |
d938c51f VB |
463 | for (mgmt = TAILQ_FIRST(&chassis->c_mgmt); |
464 | mgmt != NULL; | |
465 | mgmt = mgmt_next) { | |
466 | mgmt_next = TAILQ_NEXT(mgmt, m_entries); | |
467 | TAILQ_REMOVE(&chassis->c_mgmt, mgmt, m_entries); | |
468 | TAILQ_INSERT_TAIL(&ochassis->c_mgmt, mgmt, m_entries); | |
469 | } | |
470 | ||
566c635d VB |
471 | /* Restore saved values */ |
472 | ochassis->c_refcount = refcount; | |
473 | ochassis->c_index = index; | |
474 | memcpy(&ochassis->c_entries, &entries, sizeof(entries)); | |
d938c51f VB |
475 | |
476 | /* Get rid of the new chassis */ | |
477 | free(chassis); | |
566c635d VB |
478 | } |
479 | ||
8888d191 | 480 | static int |
43c02e7b VB |
481 | lldpd_guess_type(struct lldpd *cfg, char *frame, int s) |
482 | { | |
483 | int i; | |
4e5f34c5 | 484 | if (s < ETHER_ADDR_LEN) |
43c02e7b VB |
485 | return -1; |
486 | for (i=0; cfg->g_protocols[i].mode != 0; i++) { | |
487 | if (!cfg->g_protocols[i].enabled) | |
488 | continue; | |
489 | if (cfg->g_protocols[i].guess == NULL) { | |
a98ed042 VB |
490 | if (memcmp(frame, cfg->g_protocols[i].mac1, ETHER_ADDR_LEN) == 0 || |
491 | memcmp(frame, cfg->g_protocols[i].mac2, ETHER_ADDR_LEN) == 0 || | |
492 | memcmp(frame, cfg->g_protocols[i].mac3, ETHER_ADDR_LEN) == 0) { | |
6f8925be VB |
493 | log_debug("decode", "guessed protocol is %s (from MAC address)", |
494 | cfg->g_protocols[i].name); | |
43c02e7b | 495 | return cfg->g_protocols[i].mode; |
6f8925be | 496 | } |
43c02e7b | 497 | } else { |
6f8925be VB |
498 | if (cfg->g_protocols[i].guess(frame, s)) { |
499 | log_debug("decode", "guessed protocol is %s (from detector function)", | |
500 | cfg->g_protocols[i].name); | |
43c02e7b | 501 | return cfg->g_protocols[i].mode; |
6f8925be | 502 | } |
43c02e7b VB |
503 | } |
504 | } | |
505 | return -1; | |
506 | } | |
507 | ||
8888d191 | 508 | static void |
43c02e7b | 509 | lldpd_decode(struct lldpd *cfg, char *frame, int s, |
0bc32943 | 510 | struct lldpd_hardware *hardware) |
43c02e7b | 511 | { |
281a5cd4 | 512 | int i; |
77507b69 | 513 | struct lldpd_chassis *chassis, *ochassis = NULL; |
4e90a9e0 | 514 | struct lldpd_port *port, *oport = NULL, *aport; |
43c02e7b VB |
515 | int guess = LLDPD_MODE_LLDP; |
516 | ||
6f8925be VB |
517 | log_debug("decode", "decode a received frame on %s", |
518 | hardware->h_ifname); | |
519 | ||
4e5f34c5 | 520 | if (s < sizeof(struct ether_header) + 4) |
49697208 | 521 | /* Too short, just discard it */ |
50a89ca7 | 522 | return; |
02cf0357 | 523 | |
49697208 | 524 | /* Decapsulate VLAN frames */ |
02cf0357 VB |
525 | struct ether_header eheader; |
526 | memcpy(&eheader, frame, sizeof(struct ether_header)); | |
527 | if (eheader.ether_type == htons(ETHERTYPE_VLAN)) { | |
49697208 | 528 | /* VLAN decapsulation means to shift 4 bytes left the frame from |
4e5f34c5 VB |
529 | * offset 2*ETHER_ADDR_LEN */ |
530 | memmove(frame + 2*ETHER_ADDR_LEN, frame + 2*ETHER_ADDR_LEN + 4, s - 2*ETHER_ADDR_LEN); | |
49697208 VB |
531 | s -= 4; |
532 | } | |
50a89ca7 | 533 | |
77507b69 VB |
534 | TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) { |
535 | if ((oport->p_lastframe != NULL) && | |
536 | (oport->p_lastframe->size == s) && | |
537 | (memcmp(oport->p_lastframe->frame, frame, s) == 0)) { | |
538 | /* Already received the same frame */ | |
6f8925be | 539 | log_debug("decode", "duplicate frame, no need to decode"); |
77507b69 VB |
540 | oport->p_lastupdate = time(NULL); |
541 | return; | |
542 | } | |
43c02e7b VB |
543 | } |
544 | ||
f2dcb180 VB |
545 | guess = lldpd_guess_type(cfg, frame, s); |
546 | for (i=0; cfg->g_protocols[i].mode != 0; i++) { | |
547 | if (!cfg->g_protocols[i].enabled) | |
548 | continue; | |
549 | if (cfg->g_protocols[i].mode == guess) { | |
6f8925be VB |
550 | log_debug("decode", "using decode function for %s protocol", |
551 | cfg->g_protocols[i].name); | |
281a5cd4 VB |
552 | if (cfg->g_protocols[i].decode(cfg, frame, |
553 | s, hardware, &chassis, &port) == -1) { | |
6f8925be VB |
554 | log_debug("decode", "function for %s protocol did not decode this frame", |
555 | cfg->g_protocols[i].name); | |
f2dcb180 | 556 | return; |
6f8925be | 557 | } |
77507b69 VB |
558 | chassis->c_protocol = port->p_protocol = |
559 | cfg->g_protocols[i].mode; | |
f2dcb180 | 560 | break; |
43c02e7b | 561 | } |
f2dcb180 VB |
562 | } |
563 | if (cfg->g_protocols[i].mode == 0) { | |
6f8925be | 564 | log_debug("decode", "unable to guess frame type on %s", |
ba5116b5 | 565 | hardware->h_ifname); |
43c02e7b | 566 | return; |
f2dcb180 | 567 | } |
bdfe4193 VB |
568 | TRACE(LLDPD_FRAME_DECODED( |
569 | hardware->h_ifname, | |
570 | cfg->g_protocols[i].name, | |
571 | chassis->c_name, | |
572 | port->p_descr)); | |
43c02e7b | 573 | |
77507b69 | 574 | /* Do we already have the same MSAP somewhere? */ |
42589660 | 575 | int count = 0; |
6f8925be | 576 | log_debug("decode", "search for the same MSAP"); |
77507b69 | 577 | TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) { |
42589660 VB |
578 | if (port->p_protocol == oport->p_protocol) { |
579 | count++; | |
580 | if ((port->p_id_subtype == oport->p_id_subtype) && | |
581 | (port->p_id_len == oport->p_id_len) && | |
582 | (memcmp(port->p_id, oport->p_id, port->p_id_len) == 0) && | |
583 | (chassis->c_id_subtype == oport->p_chassis->c_id_subtype) && | |
584 | (chassis->c_id_len == oport->p_chassis->c_id_len) && | |
585 | (memcmp(chassis->c_id, oport->p_chassis->c_id, | |
586 | chassis->c_id_len) == 0)) { | |
587 | ochassis = oport->p_chassis; | |
588 | log_debug("decode", "MSAP is already known"); | |
589 | break; | |
590 | } | |
77507b69 | 591 | } |
43c02e7b | 592 | } |
42589660 | 593 | /* Do we have room for a new MSAP? */ |
ad21b578 ST |
594 | if (!oport && cfg->g_config.c_max_neighbors) { |
595 | if (count == (cfg->g_config.c_max_neighbors - 1)) { | |
596 | log_debug("decode", | |
597 | "max neighbors %d reached for port %s, " | |
598 | "dropping any new ones silently", | |
599 | cfg->g_config.c_max_neighbors, | |
600 | hardware->h_ifname); | |
601 | } else if (count > cfg->g_config.c_max_neighbors - 1) { | |
602 | log_debug("decode", | |
42589660 VB |
603 | "too many neighbors for port %s, drop this new one", |
604 | hardware->h_ifname); | |
605 | lldpd_port_cleanup(port, 1); | |
606 | lldpd_chassis_cleanup(chassis, 1); | |
607 | free(port); | |
608 | return; | |
ad21b578 | 609 | } |
42589660 | 610 | } |
77507b69 VB |
611 | /* No, but do we already know the system? */ |
612 | if (!oport) { | |
6f8925be | 613 | log_debug("decode", "MSAP is unknown, search for the chassis"); |
77507b69 VB |
614 | TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) { |
615 | if ((chassis->c_protocol == ochassis->c_protocol) && | |
616 | (chassis->c_id_subtype == ochassis->c_id_subtype) && | |
617 | (chassis->c_id_len == ochassis->c_id_len) && | |
618 | (memcmp(chassis->c_id, ochassis->c_id, | |
619 | chassis->c_id_len) == 0)) | |
620 | break; | |
43c02e7b | 621 | } |
43c02e7b | 622 | } |
43c02e7b | 623 | |
77507b69 VB |
624 | if (oport) { |
625 | /* The port is known, remove it before adding it back */ | |
626 | TAILQ_REMOVE(&hardware->h_rports, oport, p_entries); | |
4b292b55 | 627 | lldpd_port_cleanup(oport, 1); |
4e624dc2 | 628 | free(oport); |
77507b69 VB |
629 | } |
630 | if (ochassis) { | |
d938c51f | 631 | lldpd_move_chassis(ochassis, chassis); |
77507b69 VB |
632 | chassis = ochassis; |
633 | } else { | |
634 | /* Chassis not known, add it */ | |
6f8925be | 635 | log_debug("decode", "unknown chassis, add it to the list"); |
77507b69 | 636 | chassis->c_index = ++cfg->g_lastrid; |
77507b69 VB |
637 | chassis->c_refcount = 0; |
638 | TAILQ_INSERT_TAIL(&cfg->g_chassis, chassis, c_entries); | |
f4c43902 | 639 | i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++; |
6f8925be | 640 | log_debug("decode", "%d different systems are known", i); |
77507b69 VB |
641 | } |
642 | /* Add port */ | |
643 | port->p_lastchange = port->p_lastupdate = time(NULL); | |
644 | if ((port->p_lastframe = (struct lldpd_frame *)malloc(s + | |
4e90a9e0 | 645 | sizeof(struct lldpd_frame))) != NULL) { |
77507b69 VB |
646 | port->p_lastframe->size = s; |
647 | memcpy(port->p_lastframe->frame, frame, s); | |
648 | } | |
649 | TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries); | |
650 | port->p_chassis = chassis; | |
651 | port->p_chassis->c_refcount++; | |
9df5ec3d VB |
652 | /* Several cases are possible : |
653 | 1. chassis is new, its refcount was 0. It is now attached | |
654 | to this port, its refcount is 1. | |
655 | 2. chassis already exists and was attached to another | |
656 | port, we increase its refcount accordingly. | |
657 | 3. chassis already exists and was attached to the same | |
658 | port, its refcount was decreased with | |
659 | lldpd_port_cleanup() and is now increased again. | |
660 | ||
661 | In all cases, if the port already existed, it has been | |
662 | freed with lldpd_port_cleanup() and therefore, the refcount | |
663 | of the chassis that was attached to it is decreased. | |
664 | */ | |
87dfd175 VB |
665 | /* coverity[use_after_free] |
666 | TAILQ_REMOVE does the right thing */ | |
4e90a9e0 | 667 | i = 0; TAILQ_FOREACH(aport, &hardware->h_rports, p_entries) |
42b39485 | 668 | i++; |
6f8925be VB |
669 | log_debug("decode", "%d neighbors for %s", i, |
670 | hardware->h_ifname); | |
4e90a9e0 | 671 | |
25de85a4 VB |
672 | if (!oport) hardware->h_insert_cnt++; |
673 | ||
4e90a9e0 | 674 | /* Notify */ |
6f8925be VB |
675 | log_debug("decode", "send notifications for changes on %s", |
676 | hardware->h_ifname); | |
bdfe4193 VB |
677 | if (oport) { |
678 | TRACE(LLDPD_NEIGHBOR_UPDATE(hardware->h_ifname, | |
679 | chassis->c_name, | |
680 | port->p_descr, | |
681 | i)); | |
682 | levent_ctl_notify(hardware->h_ifname, NEIGHBOR_CHANGE_UPDATED, port); | |
25de85a4 | 683 | #ifdef USE_SNMP |
bdfe4193 | 684 | agent_notify(hardware, NEIGHBOR_CHANGE_UPDATED, port); |
25de85a4 | 685 | #endif |
bdfe4193 VB |
686 | } else { |
687 | TRACE(LLDPD_NEIGHBOR_NEW(hardware->h_ifname, | |
688 | chassis->c_name, | |
689 | port->p_descr, | |
690 | i)); | |
691 | levent_ctl_notify(hardware->h_ifname, NEIGHBOR_CHANGE_ADDED, port); | |
692 | #ifdef USE_SNMP | |
693 | agent_notify(hardware, NEIGHBOR_CHANGE_ADDED, port); | |
694 | #endif | |
695 | } | |
4e90a9e0 | 696 | |
be511d00 VB |
697 | #ifdef ENABLE_LLDPMED |
698 | if (!oport && port->p_chassis->c_med_type) { | |
b9de0ca6 | 699 | /* New neighbor, fast start */ |
700 | if (hardware->h_cfg->g_config.c_enable_fast_start && | |
be511d00 VB |
701 | !hardware->h_tx_fast) { |
702 | log_debug("decode", "%s: entering fast start due to " | |
703 | "new neighbor", hardware->h_ifname); | |
b9de0ca6 | 704 | hardware->h_tx_fast = hardware->h_cfg->g_config.c_tx_fast_init; |
705 | } | |
706 | ||
707 | levent_schedule_pdu(hardware); | |
708 | } | |
be511d00 | 709 | #endif |
b9de0ca6 | 710 | |
43c02e7b VB |
711 | return; |
712 | } | |
713 | ||
c036b15d VB |
714 | /* Get the output of lsb_release -s -d. This is a slow function. It should be |
715 | called once. It return NULL if any problem happens. Otherwise, this is a | |
716 | statically allocated buffer. The result includes the trailing \n */ | |
717 | static char * | |
718 | lldpd_get_lsb_release() { | |
719 | static char release[1024]; | |
720 | char *const command[] = { "lsb_release", "-s", "-d", NULL }; | |
721 | int pid, status, devnull, count; | |
722 | int pipefd[2]; | |
723 | ||
6f8925be VB |
724 | log_debug("localchassis", "grab LSB release"); |
725 | ||
c036b15d | 726 | if (pipe(pipefd)) { |
6f8925be | 727 | log_warn("localchassis", "unable to get a pair of pipes"); |
c036b15d VB |
728 | return NULL; |
729 | } | |
730 | ||
45bf0bd0 VB |
731 | pid = vfork(); |
732 | switch (pid) { | |
733 | case -1: | |
6f8925be | 734 | log_warn("localchassis", "unable to fork"); |
c036b15d | 735 | return NULL; |
c036b15d VB |
736 | case 0: |
737 | /* Child, exec lsb_release */ | |
738 | close(pipefd[0]); | |
739 | if ((devnull = open("/dev/null", O_RDWR, 0)) != -1) { | |
740 | dup2(devnull, STDIN_FILENO); | |
741 | dup2(devnull, STDERR_FILENO); | |
742 | dup2(pipefd[1], STDOUT_FILENO); | |
743 | if (devnull > 2) close(devnull); | |
744 | if (pipefd[1] > 2) close(pipefd[1]); | |
745 | execvp("lsb_release", command); | |
746 | } | |
45bf0bd0 | 747 | _exit(127); |
c036b15d VB |
748 | break; |
749 | default: | |
750 | /* Father, read the output from the children */ | |
751 | close(pipefd[1]); | |
752 | count = 0; | |
753 | do { | |
754 | status = read(pipefd[0], release+count, sizeof(release)-count); | |
755 | if ((status == -1) && (errno == EINTR)) continue; | |
756 | if (status > 0) | |
757 | count += status; | |
758 | } while (count < sizeof(release) && (status > 0)); | |
759 | if (status < 0) { | |
6f8925be | 760 | log_info("localchassis", "unable to read from lsb_release"); |
c036b15d VB |
761 | close(pipefd[0]); |
762 | waitpid(pid, &status, 0); | |
763 | return NULL; | |
764 | } | |
765 | close(pipefd[0]); | |
766 | if (count >= sizeof(release)) { | |
6f8925be | 767 | log_info("localchassis", "output of lsb_release is too large"); |
c036b15d VB |
768 | waitpid(pid, &status, 0); |
769 | return NULL; | |
770 | } | |
771 | status = -1; | |
772 | if (waitpid(pid, &status, 0) != pid) | |
773 | return NULL; | |
774 | if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) { | |
6f8925be | 775 | log_info("localchassis", "lsb_release information not available"); |
c036b15d VB |
776 | return NULL; |
777 | } | |
778 | if (!count) { | |
6f8925be | 779 | log_info("localchassis", "lsb_release returned an empty string"); |
c036b15d VB |
780 | return NULL; |
781 | } | |
782 | release[count] = '\0'; | |
783 | return release; | |
784 | } | |
785 | /* Should not be here */ | |
786 | return NULL; | |
787 | } | |
788 | ||
ae87586a MT |
789 | /* Same like lldpd_get_lsb_release but reads /etc/os-release for PRETTY_NAME=. */ |
790 | static char * | |
791 | lldpd_get_os_release() { | |
792 | static char release[1024]; | |
9757bfbc SK |
793 | char line[1024]; |
794 | char *key, *val; | |
795 | char *ptr1 = release; | |
ae87586a | 796 | |
6f8925be | 797 | log_debug("localchassis", "grab OS release"); |
f24382c4 VB |
798 | FILE *fp = fopen("/etc/os-release", "r"); |
799 | if (!fp) { | |
800 | log_debug("localchassis", "could not open /etc/os-release"); | |
801 | fp = fopen("/usr/lib/os-release", "r"); | |
802 | } | |
ae87586a | 803 | if (!fp) { |
f24382c4 VB |
804 | log_info("localchassis", |
805 | "could not open either /etc/os-release or /usr/lib/os-release"); | |
ae87586a MT |
806 | return NULL; |
807 | } | |
808 | ||
e2b09091 | 809 | while ((fgets(line, sizeof(line), fp) != NULL)) { |
ae87586a MT |
810 | key = strtok(line, "="); |
811 | val = strtok(NULL, "="); | |
812 | ||
e2b09091 VB |
813 | if (strncmp(key, "PRETTY_NAME", sizeof(line)) == 0) { |
814 | strlcpy(release, val, sizeof(line)); | |
ae87586a MT |
815 | break; |
816 | } | |
817 | } | |
818 | fclose(fp); | |
819 | ||
820 | /* Remove trailing newline and all " in the string. */ | |
dc21d049 VB |
821 | ptr1 = release + strlen(release) - 1; |
822 | while (ptr1 != release && | |
823 | ((*ptr1 == '"') || (*ptr1 == '\n'))) { | |
824 | *ptr1 = '\0'; | |
825 | ptr1--; | |
ae87586a | 826 | } |
dc21d049 VB |
827 | if (release[0] == '"') |
828 | return release+1; | |
ae87586a MT |
829 | return release; |
830 | } | |
831 | ||
8482abe9 VB |
832 | static void |
833 | lldpd_hide_ports(struct lldpd *cfg, struct lldpd_hardware *hardware, int mask) { | |
42b39485 VB |
834 | struct lldpd_port *port; |
835 | int protocols[LLDPD_MODE_MAX+1]; | |
8482abe9 VB |
836 | char buffer[256]; |
837 | int i, j, k, found; | |
42b39485 VB |
838 | unsigned int min; |
839 | ||
6f8925be VB |
840 | log_debug("smartfilter", "apply smart filter for port %s", |
841 | hardware->h_ifname); | |
842 | ||
8482abe9 VB |
843 | /* Compute the number of occurrences of each protocol */ |
844 | for (i = 0; i <= LLDPD_MODE_MAX; i++) protocols[i] = 0; | |
845 | TAILQ_FOREACH(port, &hardware->h_rports, p_entries) | |
846 | protocols[port->p_protocol]++; | |
847 | ||
848 | /* Turn the protocols[] array into an array of | |
849 | enabled/disabled protocols. 1 means enabled, 0 | |
850 | means disabled. */ | |
851 | min = (unsigned int)-1; | |
852 | for (i = 0; i <= LLDPD_MODE_MAX; i++) | |
853 | if (protocols[i] && (protocols[i] < min)) | |
854 | min = protocols[i]; | |
855 | found = 0; | |
856 | for (i = 0; i <= LLDPD_MODE_MAX; i++) | |
857 | if ((protocols[i] == min) && !found) { | |
858 | /* If we need a tie breaker, we take | |
859 | the first protocol only */ | |
8ec333bd | 860 | if (cfg->g_config.c_smart & mask & |
8482abe9 VB |
861 | (SMART_OUTGOING_ONE_PROTO | SMART_INCOMING_ONE_PROTO)) |
862 | found = 1; | |
863 | protocols[i] = 1; | |
864 | } else protocols[i] = 0; | |
865 | ||
866 | /* We set the p_hidden flag to 1 if the protocol is disabled */ | |
867 | TAILQ_FOREACH(port, &hardware->h_rports, p_entries) { | |
868 | if (mask == SMART_OUTGOING) | |
869 | port->p_hidden_out = protocols[port->p_protocol]?0:1; | |
870 | else | |
871 | port->p_hidden_in = protocols[port->p_protocol]?0:1; | |
872 | } | |
873 | ||
874 | /* If we want only one neighbor, we take the first one */ | |
8ec333bd | 875 | if (cfg->g_config.c_smart & mask & |
8482abe9 | 876 | (SMART_OUTGOING_ONE_NEIGH | SMART_INCOMING_ONE_NEIGH)) { |
42b39485 | 877 | found = 0; |
8482abe9 VB |
878 | TAILQ_FOREACH(port, &hardware->h_rports, p_entries) { |
879 | if (mask == SMART_OUTGOING) { | |
880 | if (found) port->p_hidden_out = 1; | |
881 | if (!port->p_hidden_out) | |
882 | found = 1; | |
883 | } | |
884 | if (mask == SMART_INCOMING) { | |
885 | if (found) port->p_hidden_in = 1; | |
886 | if (!port->p_hidden_in) | |
42b39485 | 887 | found = 1; |
42b39485 VB |
888 | } |
889 | } | |
8482abe9 | 890 | } |
42b39485 | 891 | |
8482abe9 VB |
892 | /* Print a debug message summarizing the operation */ |
893 | for (i = 0; i <= LLDPD_MODE_MAX; i++) protocols[i] = 0; | |
894 | k = j = 0; | |
895 | TAILQ_FOREACH(port, &hardware->h_rports, p_entries) { | |
896 | if (!(((mask == SMART_OUTGOING) && port->p_hidden_out) || | |
897 | ((mask == SMART_INCOMING) && port->p_hidden_in))) { | |
898 | k++; | |
899 | protocols[port->p_protocol] = 1; | |
42b39485 | 900 | } |
8482abe9 VB |
901 | j++; |
902 | } | |
903 | buffer[0] = '\0'; | |
904 | for (i=0; cfg->g_protocols[i].mode != 0; i++) { | |
905 | if (cfg->g_protocols[i].enabled && protocols[cfg->g_protocols[i].mode]) { | |
906 | if (strlen(buffer) + | |
907 | strlen(cfg->g_protocols[i].name) + 3 > sizeof(buffer)) { | |
908 | /* Unlikely, our buffer is too small */ | |
909 | memcpy(buffer + sizeof(buffer) - 4, "...", 4); | |
910 | break; | |
42b39485 | 911 | } |
8482abe9 | 912 | if (buffer[0]) |
1dfac346 VB |
913 | strncat(buffer, ", ", 2); |
914 | strncat(buffer, cfg->g_protocols[i].name, strlen(cfg->g_protocols[i].name)); | |
42b39485 VB |
915 | } |
916 | } | |
6f8925be VB |
917 | log_debug("smartfilter", "%s: %s: %d visible neighbors (out of %d)", |
918 | hardware->h_ifname, | |
919 | (mask == SMART_OUTGOING)?"out filter":"in filter", | |
920 | k, j); | |
921 | log_debug("smartfilter", "%s: protocols: %s", | |
922 | hardware->h_ifname, buffer[0]?buffer:"(none)"); | |
42b39485 VB |
923 | } |
924 | ||
566c635d VB |
925 | /* Hide unwanted ports depending on smart mode set by the user */ |
926 | static void | |
927 | lldpd_hide_all(struct lldpd *cfg) | |
928 | { | |
929 | struct lldpd_hardware *hardware; | |
930 | ||
8ec333bd | 931 | if (!cfg->g_config.c_smart) |
566c635d | 932 | return; |
6f8925be | 933 | log_debug("smartfilter", "apply smart filter results on all ports"); |
566c635d | 934 | TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { |
8ec333bd | 935 | if (cfg->g_config.c_smart & SMART_INCOMING_FILTER) |
566c635d | 936 | lldpd_hide_ports(cfg, hardware, SMART_INCOMING); |
8ec333bd | 937 | if (cfg->g_config.c_smart & SMART_OUTGOING_FILTER) |
566c635d VB |
938 | lldpd_hide_ports(cfg, hardware, SMART_OUTGOING); |
939 | } | |
940 | } | |
941 | ||
d6e889b6 VB |
942 | void |
943 | lldpd_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd) | |
43c02e7b | 944 | { |
d6e889b6 VB |
945 | char *buffer = NULL; |
946 | int n; | |
6f8925be VB |
947 | log_debug("receive", "receive a frame on %s", |
948 | hardware->h_ifname); | |
d6e889b6 | 949 | if ((buffer = (char *)malloc(hardware->h_mtu)) == NULL) { |
6f8925be | 950 | log_warn("receive", "failed to alloc reception buffer"); |
d6e889b6 VB |
951 | return; |
952 | } | |
953 | if ((n = hardware->h_ops->recv(cfg, hardware, | |
954 | fd, buffer, | |
955 | hardware->h_mtu)) == -1) { | |
3e2e693c | 956 | log_debug("receive", "discard frame received on %s", |
6f8925be | 957 | hardware->h_ifname); |
d6e889b6 VB |
958 | free(buffer); |
959 | return; | |
960 | } | |
e7331ce9 VB |
961 | if (hardware->h_lport.p_disable_rx) { |
962 | log_debug("receive", "RX disabled, ignore the frame on %s", | |
963 | hardware->h_ifname); | |
964 | free(buffer); | |
965 | return; | |
966 | } | |
e4ff3ed5 VB |
967 | if (cfg->g_config.c_paused) { |
968 | log_debug("receive", "paused, ignore the frame on %s", | |
969 | hardware->h_ifname); | |
970 | free(buffer); | |
971 | return; | |
972 | } | |
d6e889b6 | 973 | hardware->h_rx_cnt++; |
6f8925be VB |
974 | log_debug("receive", "decode received frame on %s", |
975 | hardware->h_ifname); | |
bdfe4193 | 976 | TRACE(LLDPD_FRAME_RECEIVED(hardware->h_ifname, buffer, (size_t)n)); |
d6e889b6 VB |
977 | lldpd_decode(cfg, buffer, n, hardware); |
978 | lldpd_hide_all(cfg); /* Immediatly hide */ | |
66879d46 | 979 | lldpd_count_neighbors(cfg); |
d6e889b6 | 980 | free(buffer); |
43c02e7b VB |
981 | } |
982 | ||
e770b720 VB |
983 | static void |
984 | lldpd_send_shutdown(struct lldpd_hardware *hardware) | |
985 | { | |
986 | struct lldpd *cfg = hardware->h_cfg; | |
987 | if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) return; | |
e7331ce9 | 988 | if (hardware->h_lport.p_disable_tx) return; |
e770b720 VB |
989 | if ((hardware->h_flags & IFF_RUNNING) == 0) |
990 | return; | |
991 | ||
992 | /* It's safe to call `lldp_send_shutdown()` because shutdown LLDPU will | |
993 | * only be emitted if LLDP was sent on that port. */ | |
994 | if (lldp_send_shutdown(hardware->h_cfg, hardware) != 0) | |
995 | log_warnx("send", "unable to send shutdown LLDPDU on %s", | |
996 | hardware->h_ifname); | |
997 | } | |
998 | ||
579bedd5 VB |
999 | void |
1000 | lldpd_send(struct lldpd_hardware *hardware) | |
43c02e7b | 1001 | { |
579bedd5 | 1002 | struct lldpd *cfg = hardware->h_cfg; |
77507b69 | 1003 | struct lldpd_port *port; |
0d86e62f | 1004 | int i, sent; |
f7db0dd8 | 1005 | |
e4ff3ed5 | 1006 | if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) return; |
e7331ce9 | 1007 | if (hardware->h_lport.p_disable_tx) return; |
579bedd5 VB |
1008 | if ((hardware->h_flags & IFF_RUNNING) == 0) |
1009 | return; | |
6f8925be | 1010 | |
579bedd5 VB |
1011 | log_debug("send", "send PDU on %s", hardware->h_ifname); |
1012 | sent = 0; | |
1013 | for (i=0; cfg->g_protocols[i].mode != 0; i++) { | |
1014 | if (!cfg->g_protocols[i].enabled) | |
1015 | continue; | |
1016 | /* We send only if we have at least one remote system | |
1017 | * speaking this protocol or if the protocol is forced */ | |
1018 | if (cfg->g_protocols[i].enabled > 1) { | |
1019 | cfg->g_protocols[i].send(cfg, hardware); | |
1020 | sent++; | |
43c02e7b | 1021 | continue; |
43c02e7b | 1022 | } |
579bedd5 VB |
1023 | TAILQ_FOREACH(port, &hardware->h_rports, p_entries) { |
1024 | /* If this remote port is disabled, we don't | |
1025 | * consider it */ | |
1026 | if (port->p_hidden_out) | |
1027 | continue; | |
1028 | if (port->p_protocol == | |
1029 | cfg->g_protocols[i].mode) { | |
bdfe4193 VB |
1030 | TRACE(LLDPD_FRAME_SEND(hardware->h_ifname, |
1031 | cfg->g_protocols[i].name)); | |
579bedd5 VB |
1032 | log_debug("send", "send PDU on %s with protocol %s", |
1033 | hardware->h_ifname, | |
1034 | cfg->g_protocols[i].name); | |
46baf627 VB |
1035 | cfg->g_protocols[i].send(cfg, |
1036 | hardware); | |
579bedd5 | 1037 | sent++; |
46baf627 VB |
1038 | break; |
1039 | } | |
46baf627 | 1040 | } |
43c02e7b | 1041 | } |
579bedd5 VB |
1042 | |
1043 | if (!sent) { | |
1044 | /* Nothing was sent for this port, let's speak the first | |
1045 | * available protocol. */ | |
1046 | for (i = 0; cfg->g_protocols[i].mode != 0; i++) { | |
1047 | if (!cfg->g_protocols[i].enabled) continue; | |
bdfe4193 VB |
1048 | TRACE(LLDPD_FRAME_SEND(hardware->h_ifname, |
1049 | cfg->g_protocols[i].name)); | |
579bedd5 VB |
1050 | log_debug("send", "fallback to protocol %s for %s", |
1051 | cfg->g_protocols[i].name, hardware->h_ifname); | |
1052 | cfg->g_protocols[i].send(cfg, | |
1053 | hardware); | |
1054 | break; | |
1055 | } | |
1056 | if (cfg->g_protocols[i].mode == 0) | |
1057 | log_warnx("send", "no protocol enabled, dunno what to send"); | |
1058 | } | |
43c02e7b VB |
1059 | } |
1060 | ||
89840df0 | 1061 | #ifdef ENABLE_LLDPMED |
8888d191 | 1062 | static void |
89840df0 VB |
1063 | lldpd_med(struct lldpd_chassis *chassis) |
1064 | { | |
55606d4b VB |
1065 | static short int once = 0; |
1066 | if (!once) { | |
1067 | chassis->c_med_hw = dmi_hw(); | |
1068 | chassis->c_med_fw = dmi_fw(); | |
1069 | chassis->c_med_sn = dmi_sn(); | |
1070 | chassis->c_med_manuf = dmi_manuf(); | |
1071 | chassis->c_med_model = dmi_model(); | |
1072 | chassis->c_med_asset = dmi_asset(); | |
1073 | once = 1; | |
1074 | } | |
89840df0 VB |
1075 | } |
1076 | #endif | |
1077 | ||
e66b7f34 | 1078 | static int |
c3e340b6 | 1079 | lldpd_routing_enabled(struct lldpd *cfg) |
e66b7f34 | 1080 | { |
c3e340b6 | 1081 | int routing; |
a2b113ef | 1082 | |
1083 | if ((LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_ROUTER) == 0) | |
1084 | return 0; | |
1085 | ||
c3e340b6 VB |
1086 | if ((routing = interfaces_routing_enabled(cfg)) == -1) { |
1087 | log_debug("localchassis", "unable to check if routing is enabled"); | |
1088 | return 0; | |
1089 | } | |
1090 | return routing; | |
e66b7f34 VB |
1091 | } |
1092 | ||
8888d191 | 1093 | static void |
6e75df87 | 1094 | lldpd_update_localchassis(struct lldpd *cfg) |
43c02e7b | 1095 | { |
6e75df87 VB |
1096 | struct utsname un; |
1097 | char *hp; | |
43c02e7b | 1098 | |
6f8925be | 1099 | log_debug("localchassis", "update information for local chassis"); |
aa015c26 | 1100 | assert(LOCAL_CHASSIS(cfg) != NULL); |
6f8925be | 1101 | |
43c02e7b | 1102 | /* Set system name and description */ |
c3e340b6 | 1103 | if (uname(&un) < 0) |
6f8925be | 1104 | fatal("localchassis", "failed to get system information"); |
ce347d29 JJ |
1105 | if (cfg->g_config.c_hostname) { |
1106 | log_debug("localchassis", "use overridden system name `%s`", cfg->g_config.c_hostname); | |
1107 | hp = cfg->g_config.c_hostname; | |
1108 | } else { | |
1fa7d39f | 1109 | if ((hp = priv_gethostname()) == NULL) |
ce347d29 JJ |
1110 | fatal("localchassis", "failed to get system name"); |
1111 | } | |
77507b69 VB |
1112 | free(LOCAL_CHASSIS(cfg)->c_name); |
1113 | free(LOCAL_CHASSIS(cfg)->c_descr); | |
1114 | if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL) | |
6f8925be | 1115 | fatal("localchassis", NULL); |
8ec333bd | 1116 | if (cfg->g_config.c_description) { |
80c974a8 | 1117 | log_debug("localchassis", "use overridden description `%s`", cfg->g_config.c_description); |
40ce835b | 1118 | if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s", |
8ec333bd | 1119 | cfg->g_config.c_description) == -1) |
6f8925be | 1120 | fatal("localchassis", "failed to set full system description"); |
40ce835b | 1121 | } else { |
8ec333bd | 1122 | if (cfg->g_config.c_advertise_version) { |
6f8925be | 1123 | log_debug("localchassis", "advertise system version"); |
a9db9b44 | 1124 | if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s %s", |
c036b15d | 1125 | cfg->g_lsb_release?cfg->g_lsb_release:"", |
a9db9b44 | 1126 | un.sysname, un.release, un.version, un.machine) |
40ce835b | 1127 | == -1) |
6f8925be | 1128 | fatal("localchassis", "failed to set full system description"); |
40ce835b | 1129 | } else { |
6f8925be | 1130 | log_debug("localchassis", "do not advertise system version"); |
40ce835b | 1131 | if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s", |
c036b15d | 1132 | cfg->g_lsb_release?cfg->g_lsb_release:un.sysname) == -1) |
6f8925be | 1133 | fatal("localchassis", "failed to set minimal system description"); |
40ce835b ST |
1134 | } |
1135 | } | |
96beef68 VB |
1136 | if (cfg->g_config.c_platform == NULL) |
1137 | cfg->g_config.c_platform = strdup(un.sysname); | |
43c02e7b | 1138 | |
c3e340b6 VB |
1139 | /* Check routing */ |
1140 | if (lldpd_routing_enabled(cfg)) { | |
1141 | log_debug("localchassis", "routing is enabled, enable router capability"); | |
e66b7f34 VB |
1142 | LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_ROUTER; |
1143 | } else | |
1144 | LOCAL_CHASSIS(cfg)->c_cap_enabled &= ~LLDP_CAP_ROUTER; | |
242845c7 | 1145 | |
89840df0 | 1146 | #ifdef ENABLE_LLDPMED |
77507b69 VB |
1147 | if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_TELEPHONE) |
1148 | LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE; | |
1149 | lldpd_med(LOCAL_CHASSIS(cfg)); | |
1150 | free(LOCAL_CHASSIS(cfg)->c_med_sw); | |
8ec333bd | 1151 | if (cfg->g_config.c_advertise_version) |
de1b1b3a VB |
1152 | LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release); |
1153 | else | |
1154 | LOCAL_CHASSIS(cfg)->c_med_sw = strdup("Unknown"); | |
89840df0 | 1155 | #endif |
a2b113ef | 1156 | if ((LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_STATION) && |
1157 | (LOCAL_CHASSIS(cfg)->c_cap_enabled == 0)) | |
6b095471 | 1158 | LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_STATION; |
43c02e7b | 1159 | |
b4ac8083 VB |
1160 | /* Set chassis ID if needed. This is only done if chassis ID |
1161 | has not been set previously (with the MAC address of an | |
1162 | interface for example) | |
1163 | */ | |
1164 | if (LOCAL_CHASSIS(cfg)->c_id == NULL) { | |
6f8925be | 1165 | log_debug("localchassis", "no chassis ID is currently set, use chassis name"); |
b4ac8083 | 1166 | if (!(LOCAL_CHASSIS(cfg)->c_id = strdup(LOCAL_CHASSIS(cfg)->c_name))) |
6f8925be | 1167 | fatal("localchassis", NULL); |
b4ac8083 VB |
1168 | LOCAL_CHASSIS(cfg)->c_id_len = strlen(LOCAL_CHASSIS(cfg)->c_name); |
1169 | LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL; | |
6e75df87 VB |
1170 | } |
1171 | } | |
1172 | ||
0484f180 | 1173 | void |
6e75df87 VB |
1174 | lldpd_update_localports(struct lldpd *cfg) |
1175 | { | |
6e75df87 | 1176 | struct lldpd_hardware *hardware; |
6e75df87 | 1177 | |
9e5d99d4 | 1178 | log_debug("localchassis", "update information for local ports"); |
6f8925be | 1179 | |
6e75df87 VB |
1180 | /* h_flags is set to 0 for each port. If the port is updated, h_flags |
1181 | * will be set to a non-zero value. This will allow us to clean up any | |
1182 | * non up-to-date port */ | |
43c02e7b VB |
1183 | TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) |
1184 | hardware->h_flags = 0; | |
1185 | ||
bdfe4193 | 1186 | TRACE(LLDPD_INTERFACES_UPDATE()); |
e12c2365 | 1187 | interfaces_update(cfg); |
bfdc2a8c | 1188 | lldpd_cleanup(cfg); |
579bedd5 | 1189 | lldpd_reset_timer(cfg); |
6e75df87 | 1190 | } |
43c02e7b | 1191 | |
d6e889b6 | 1192 | void |
6e75df87 VB |
1193 | lldpd_loop(struct lldpd *cfg) |
1194 | { | |
1195 | /* Main loop. | |
6e75df87 | 1196 | 1. Update local ports information |
579bedd5 | 1197 | 2. Update local chassis information |
6e75df87 | 1198 | */ |
6f8925be | 1199 | log_debug("loop", "start new loop"); |
849954d7 | 1200 | LOCAL_CHASSIS(cfg)->c_cap_enabled = 0; |
579bedd5 VB |
1201 | /* Information for local ports is triggered even when it is possible to |
1202 | * update them on some other event because we want to refresh them if we | |
1203 | * missed something. */ | |
1204 | log_debug("loop", "update information for local ports"); | |
1205 | lldpd_update_localports(cfg); | |
6f8925be | 1206 | log_debug("loop", "update information for local chassis"); |
6e75df87 | 1207 | lldpd_update_localchassis(cfg); |
66879d46 | 1208 | lldpd_count_neighbors(cfg); |
43c02e7b VB |
1209 | } |
1210 | ||
8888d191 | 1211 | static void |
d6e889b6 | 1212 | lldpd_exit(struct lldpd *cfg) |
43c02e7b | 1213 | { |
6e75df87 | 1214 | struct lldpd_hardware *hardware, *hardware_next; |
9e5d99d4 | 1215 | log_debug("main", "exit lldpd"); |
e770b720 VB |
1216 | |
1217 | TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) | |
1218 | lldpd_send_shutdown(hardware); | |
1219 | ||
d6e889b6 | 1220 | close(cfg->g_ctl); |
0262adbb | 1221 | priv_ctl_cleanup(cfg->g_ctlname); |
9e5d99d4 | 1222 | log_debug("main", "cleanup hardware information"); |
d6e889b6 | 1223 | for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL; |
6e75df87 VB |
1224 | hardware = hardware_next) { |
1225 | hardware_next = TAILQ_NEXT(hardware, h_entries); | |
9e5d99d4 | 1226 | log_debug("main", "cleanup interface %s", hardware->h_ifname); |
ef3707da | 1227 | lldpd_remote_cleanup(hardware, NULL, 1); |
d6e889b6 | 1228 | lldpd_hardware_cleanup(cfg, hardware); |
43c02e7b | 1229 | } |
24cb9684 | 1230 | interfaces_cleanup(cfg); |
9c93725f | 1231 | lldpd_port_cleanup(cfg->g_default_local_port, 1); |
24cb9684 | 1232 | lldpd_all_chassis_cleanup(cfg); |
9c93725f | 1233 | free(cfg->g_default_local_port); |
13181ede | 1234 | free(cfg->g_config.c_platform); |
f144d837 | 1235 | levent_shutdown(cfg); |
43c02e7b VB |
1236 | } |
1237 | ||
51534ef3 VB |
1238 | /** |
1239 | * Run lldpcli to configure lldpd. | |
1240 | * | |
1241 | * @return PID of running lldpcli or -1 if error. | |
1242 | */ | |
1243 | static pid_t | |
9856f279 | 1244 | lldpd_configure(int use_syslog, int debug, const char *path, const char *ctlname) |
51534ef3 | 1245 | { |
45bf0bd0 | 1246 | pid_t lldpcli = vfork(); |
51534ef3 VB |
1247 | int devnull; |
1248 | ||
9856f279 VB |
1249 | char sdebug[debug + 4]; |
1250 | if (use_syslog) | |
1251 | strlcpy(sdebug, "-s", 3); | |
1252 | else { | |
1253 | /* debug = 0 -> -sd */ | |
1254 | /* debug = 1 -> -sdd */ | |
1255 | /* debug = 2 -> -sddd */ | |
1256 | memset(sdebug, 'd', sizeof(sdebug)); | |
1257 | sdebug[debug + 3] = '\0'; | |
1258 | sdebug[0] = '-'; sdebug[1] = 's'; | |
1259 | } | |
45bf0bd0 VB |
1260 | log_debug("main", "invoke %s %s", path, sdebug); |
1261 | ||
51534ef3 VB |
1262 | switch (lldpcli) { |
1263 | case -1: | |
1264 | log_warn("main", "unable to fork"); | |
1265 | return -1; | |
1266 | case 0: | |
1267 | /* Child, exec lldpcli */ | |
1268 | if ((devnull = open("/dev/null", O_RDWR, 0)) != -1) { | |
51534ef3 | 1269 | dup2(devnull, STDIN_FILENO); |
eb78d6e3 | 1270 | dup2(devnull, STDOUT_FILENO); |
51534ef3 VB |
1271 | if (devnull > 2) close(devnull); |
1272 | ||
45bf0bd0 VB |
1273 | execl(path, "lldpcli", sdebug, |
1274 | "-u", ctlname, | |
e9447d45 VB |
1275 | "-C", SYSCONFDIR "/lldpd.conf", |
1276 | "-C", SYSCONFDIR "/lldpd.d", | |
45bf0bd0 | 1277 | "resume", |
d6454938 | 1278 | (char *)NULL); |
45bf0bd0 VB |
1279 | log_warn("main", "unable to execute %s", path); |
1280 | log_warnx("main", "configuration is incomplete, lldpd needs to be unpaused"); | |
51534ef3 | 1281 | } |
45bf0bd0 | 1282 | _exit(127); |
51534ef3 VB |
1283 | break; |
1284 | default: | |
1285 | /* Father, don't do anything stupid */ | |
1286 | return lldpcli; | |
1287 | } | |
1288 | /* Should not be here */ | |
1289 | return -1; | |
1290 | } | |
1291 | ||
8482abe9 VB |
1292 | struct intint { int a; int b; }; |
1293 | static const struct intint filters[] = { | |
1294 | { 0, 0 }, | |
1295 | { 1, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | | |
1296 | SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO }, | |
1297 | { 2, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO }, | |
1298 | { 3, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO }, | |
1299 | { 4, SMART_INCOMING_FILTER | SMART_OUTGOING_FILTER }, | |
1300 | { 5, SMART_INCOMING_FILTER }, | |
1301 | { 6, SMART_OUTGOING_FILTER }, | |
1302 | { 7, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH | | |
1303 | SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO }, | |
1304 | { 8, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH }, | |
1305 | { 9, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH | | |
1306 | SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO }, | |
1307 | { 10, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH }, | |
1308 | { 11, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH }, | |
1309 | { 12, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH | | |
1310 | SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH }, | |
1311 | { 13, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH | | |
1312 | SMART_OUTGOING_FILTER }, | |
1313 | { 14, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | | |
1314 | SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH }, | |
1315 | { 15, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | | |
1316 | SMART_OUTGOING_FILTER }, | |
1317 | { 16, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH | | |
1318 | SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH }, | |
1319 | { 17, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH | | |
1320 | SMART_OUTGOING_FILTER }, | |
1321 | { 18, SMART_INCOMING_FILTER | | |
1322 | SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH }, | |
1323 | { 19, SMART_INCOMING_FILTER | | |
1324 | SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO }, | |
1325 | { -1, 0 } | |
1326 | }; | |
1327 | ||
e66b7f34 | 1328 | #ifndef HOST_OS_OSX |
2591639f VB |
1329 | /** |
1330 | * Tell if we have been started by upstart. | |
1331 | */ | |
1332 | static int | |
1333 | lldpd_started_by_upstart() | |
1334 | { | |
322aafc9 | 1335 | #ifdef HOST_OS_LINUX |
2591639f VB |
1336 | const char *upstartjob = getenv("UPSTART_JOB"); |
1337 | if (!(upstartjob && !strcmp(upstartjob, "lldpd"))) | |
1338 | return 0; | |
1339 | log_debug("main", "running with upstart, don't fork but stop"); | |
1340 | raise(SIGSTOP); | |
31594635 | 1341 | unsetenv("UPSTART_JOB"); |
2591639f | 1342 | return 1; |
322aafc9 VB |
1343 | #else |
1344 | return 0; | |
1345 | #endif | |
1346 | } | |
1347 | ||
1348 | /** | |
1349 | * Tell if we have been started by systemd. | |
1350 | */ | |
1351 | static int | |
1352 | lldpd_started_by_systemd() | |
1353 | { | |
1354 | #ifdef HOST_OS_LINUX | |
1355 | int fd = -1; | |
1356 | const char *notifysocket = getenv("NOTIFY_SOCKET"); | |
1357 | if (!notifysocket || | |
1358 | !strchr("@/", notifysocket[0]) || | |
1359 | strlen(notifysocket) < 2) | |
1360 | return 0; | |
1361 | ||
1362 | log_debug("main", "running with systemd, don't fork but signal ready"); | |
ceb37922 | 1363 | if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { |
322aafc9 VB |
1364 | log_warn("main", "unable to open systemd notification socket %s", |
1365 | notifysocket); | |
1366 | return 0; | |
1367 | } | |
1368 | ||
1369 | struct sockaddr_un su = { .sun_family = AF_UNIX }; | |
1370 | strlcpy(su.sun_path, notifysocket, sizeof(su.sun_path)); | |
1371 | if (notifysocket[0] == '@') su.sun_path[0] = 0; | |
1372 | ||
1373 | struct iovec iov = { | |
1374 | .iov_base = "READY=1", | |
506273e9 | 1375 | .iov_len = strlen("READY=1") |
322aafc9 VB |
1376 | }; |
1377 | struct msghdr hdr = { | |
1378 | .msg_name = &su, | |
1379 | .msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(notifysocket), | |
1380 | .msg_iov = &iov, | |
1381 | .msg_iovlen = 1 | |
1382 | }; | |
31594635 | 1383 | unsetenv("NOTIFY_SOCKET"); |
322aafc9 VB |
1384 | if (sendmsg(fd, &hdr, MSG_NOSIGNAL) < 0) { |
1385 | log_warn("main", "unable to send notification to systemd"); | |
1386 | close(fd); | |
1387 | return 0; | |
1388 | } | |
1389 | close(fd); | |
1390 | return 1; | |
1391 | #else | |
1392 | return 0; | |
1393 | #endif | |
2591639f | 1394 | } |
e66b7f34 | 1395 | #endif |
2591639f | 1396 | |
2472bfda VB |
1397 | #ifdef HOST_OS_LINUX |
1398 | static void | |
1399 | version_convert(const char *sversion, unsigned iversion[], size_t n) | |
1400 | { | |
1401 | const char *p = sversion; | |
1402 | char *end; | |
1403 | for (size_t i = 0; i < n; i++) { | |
1404 | iversion[i] = strtol(p, &end, 10); | |
1405 | if (*end != '.') break; | |
1406 | p = end + 1; | |
1407 | } | |
1408 | } | |
1409 | ||
1410 | static void | |
1411 | version_check(void) | |
1412 | { | |
1413 | struct utsname uts; | |
1414 | if (uname(&uts) == -1) return; | |
1415 | unsigned version_min[3] = {}; | |
1416 | unsigned version_cur[3] = {}; | |
1417 | version_convert(uts.release, version_cur, 3); | |
1418 | version_convert(MIN_LINUX_KERNEL_VERSION, version_min, 3); | |
1419 | if (version_min[0] > version_cur[0] || | |
1420 | (version_min[0] == version_cur[0] && version_min[1] > version_cur[1]) || | |
05a708dc VB |
1421 | (version_min[0] == version_cur[0] && version_min[1] == version_cur[1] && |
1422 | version_min[2] > version_cur[2])) { | |
2472bfda VB |
1423 | log_warnx("lldpd", "minimal kernel version required is %s, got %s", |
1424 | MIN_LINUX_KERNEL_VERSION, uts.release); | |
1425 | log_warnx("lldpd", "lldpd may be unable to detect bonds and bridges correctly"); | |
1426 | #ifndef ENABLE_OLDIES | |
1427 | log_warnx("lldpd", "consider recompiling with --enable-oldies option"); | |
1428 | #endif | |
1429 | } | |
1430 | } | |
1431 | #else | |
1432 | static void version_check(void) {} | |
1433 | #endif | |
1434 | ||
43c02e7b | 1435 | int |
1e0d651f | 1436 | lldpd_main(int argc, char *argv[], char *envp[]) |
43c02e7b VB |
1437 | { |
1438 | struct lldpd *cfg; | |
77507b69 | 1439 | struct lldpd_chassis *lchassis; |
9856f279 | 1440 | int ch, debug = 0, use_syslog = 1, daemonize = 1; |
8a378b16 | 1441 | const char *errstr; |
e809a587 VB |
1442 | #ifdef USE_SNMP |
1443 | int snmp = 0; | |
abc04205 | 1444 | const char *agentx = NULL; /* AgentX socket */ |
e809a587 | 1445 | #endif |
1c1c519f | 1446 | const char *ctlname = NULL; |
d4e4c804 | 1447 | char *mgmtp = NULL; |
5339e725 | 1448 | char *cidp = NULL; |
ba85f9f4 | 1449 | char *interfaces = NULL; |
984bbcbc VB |
1450 | /* We do not want more options here. Please add them in lldpcli instead |
1451 | * unless there is a very good reason. Most command-line options will | |
1452 | * get deprecated at some point. */ | |
51534ef3 | 1453 | char *popt, opts[] = |
7b6cbfe2 | 1454 | "H:vhkrdD:p:xX:m:u:4:6:I:C:p:M:P:S:iL:@ "; |
de1b1b3a | 1455 | int i, found, advertise_version = 1; |
89840df0 | 1456 | #ifdef ENABLE_LLDPMED |
e809a587 | 1457 | int lldpmed = 0, noinventory = 0; |
d6d42d56 | 1458 | int enable_fast_start = 1; |
89840df0 | 1459 | #endif |
d4afb919 VB |
1460 | char *descr_override = NULL; |
1461 | char *platform_override = NULL; | |
c036b15d | 1462 | char *lsb_release = NULL; |
51534ef3 | 1463 | const char *lldpcli = LLDPCLI_PATH; |
7b6cbfe2 | 1464 | const char *pidfile = LLDPD_PID_FILE; |
8482abe9 | 1465 | int smart = 15; |
efa6a7a3 | 1466 | int receiveonly = 0, version = 0; |
2e91d1a1 VB |
1467 | int ctl; |
1468 | ||
28bf4022 | 1469 | #ifdef ENABLE_PRIVSEP |
2e91d1a1 VB |
1470 | /* Non privileged user */ |
1471 | struct passwd *user; | |
1472 | struct group *group; | |
1473 | uid_t uid; | |
1474 | gid_t gid; | |
28bf4022 | 1475 | #endif |
43c02e7b VB |
1476 | |
1477 | saved_argv = argv; | |
1478 | ||
1e0d651f VB |
1479 | #if HAVE_SETPROCTITLE_INIT |
1480 | setproctitle_init(argc, argv, envp); | |
1481 | #endif | |
1482 | ||
43c02e7b VB |
1483 | /* |
1484 | * Get and parse command line options | |
1485 | */ | |
d68f1b81 VB |
1486 | if ((popt = strchr(opts, '@')) != NULL) { |
1487 | for (i=0; | |
1488 | protos[i].mode != 0 && *popt != '\0'; | |
1489 | i++) | |
1490 | *(popt++) = protos[i].arg; | |
1491 | *popt = '\0'; | |
1492 | } | |
43c02e7b VB |
1493 | while ((ch = getopt(argc, argv, opts)) != -1) { |
1494 | switch (ch) { | |
b162b740 VB |
1495 | case 'h': |
1496 | usage(); | |
1497 | break; | |
96e49a40 | 1498 | case 'v': |
efa6a7a3 | 1499 | version++; |
96e49a40 | 1500 | break; |
43c02e7b | 1501 | case 'd': |
9856f279 VB |
1502 | if (daemonize) |
1503 | daemonize = 0; | |
1504 | else if (use_syslog) | |
1505 | use_syslog = 0; | |
1506 | else | |
1507 | debug++; | |
43c02e7b | 1508 | break; |
9e5d99d4 VB |
1509 | case 'D': |
1510 | log_accept(optarg); | |
1511 | break; | |
7b6cbfe2 VB |
1512 | case 'p': |
1513 | pidfile = optarg; | |
1514 | break; | |
537a8043 VB |
1515 | case 'r': |
1516 | receiveonly = 1; | |
1517 | break; | |
d4e4c804 | 1518 | case 'm': |
9b11faad VB |
1519 | if (mgmtp) { |
1520 | fprintf(stderr, "-m can only be used once\n"); | |
1521 | usage(); | |
1522 | } | |
abc04205 | 1523 | mgmtp = strdup(optarg); |
43c02e7b | 1524 | break; |
0262adbb | 1525 | case 'u': |
9b11faad VB |
1526 | if (ctlname) { |
1527 | fprintf(stderr, "-u can only be used once\n"); | |
1528 | usage(); | |
1529 | } | |
0262adbb ZM |
1530 | ctlname = optarg; |
1531 | break; | |
ba85f9f4 | 1532 | case 'I': |
9b11faad VB |
1533 | if (interfaces) { |
1534 | fprintf(stderr, "-I can only be used once\n"); | |
1535 | usage(); | |
1536 | } | |
abc04205 | 1537 | interfaces = strdup(optarg); |
ba85f9f4 | 1538 | break; |
5339e725 | 1539 | case 'C': |
9b11faad VB |
1540 | if (cidp) { |
1541 | fprintf(stderr, "-C can only be used once\n"); | |
1542 | usage(); | |
1543 | } | |
abc04205 | 1544 | cidp = strdup(optarg); |
5339e725 | 1545 | break; |
51534ef3 VB |
1546 | case 'L': |
1547 | if (strlen(optarg)) lldpcli = optarg; | |
1548 | else lldpcli = NULL; | |
037d4c8e | 1549 | break; |
de1b1b3a VB |
1550 | case 'k': |
1551 | advertise_version = 0; | |
1552 | break; | |
e809a587 | 1553 | #ifdef ENABLE_LLDPMED |
115ff55c | 1554 | case 'M': |
8a378b16 VB |
1555 | lldpmed = strtonum(optarg, 1, 4, &errstr); |
1556 | if (errstr) { | |
e809a587 | 1557 | fprintf(stderr, "-M requires an argument between 1 and 4\n"); |
89840df0 | 1558 | usage(); |
e809a587 | 1559 | } |
89840df0 | 1560 | break; |
e809a587 | 1561 | case 'i': |
e809a587 | 1562 | noinventory = 1; |
115ff55c | 1563 | break; |
e809a587 | 1564 | #else |
115ff55c VB |
1565 | case 'M': |
1566 | case 'i': | |
e809a587 VB |
1567 | fprintf(stderr, "LLDP-MED support is not built-in\n"); |
1568 | usage(); | |
e809a587 | 1569 | break; |
115ff55c | 1570 | #endif |
e809a587 | 1571 | #ifdef USE_SNMP |
bbea66e1 V |
1572 | case 'x': |
1573 | snmp = 1; | |
1574 | break; | |
1575 | case 'X': | |
9b11faad VB |
1576 | if (agentx) { |
1577 | fprintf(stderr, "-X can only be used once\n"); | |
1578 | usage(); | |
1579 | } | |
43c02e7b | 1580 | snmp = 1; |
bbea66e1 V |
1581 | agentx = optarg; |
1582 | break; | |
e809a587 | 1583 | #else |
bbea66e1 V |
1584 | case 'x': |
1585 | case 'X': | |
e809a587 VB |
1586 | fprintf(stderr, "SNMP support is not built-in\n"); |
1587 | usage(); | |
1588 | #endif | |
43c02e7b | 1589 | break; |
40ce835b | 1590 | case 'S': |
9b11faad VB |
1591 | if (descr_override) { |
1592 | fprintf(stderr, "-S can only be used once\n"); | |
1593 | usage(); | |
1594 | } | |
40ce835b ST |
1595 | descr_override = strdup(optarg); |
1596 | break; | |
d4afb919 | 1597 | case 'P': |
9b11faad VB |
1598 | if (platform_override) { |
1599 | fprintf(stderr, "-P can only be used once\n"); | |
1600 | usage(); | |
1601 | } | |
d4afb919 VB |
1602 | platform_override = strdup(optarg); |
1603 | break; | |
42b39485 | 1604 | case 'H': |
8a378b16 VB |
1605 | smart = strtonum(optarg, 0, sizeof(filters)/sizeof(filters[0]), |
1606 | &errstr); | |
1607 | if (errstr) { | |
1608 | fprintf(stderr, "-H requires an int between 0 and %zu\n", | |
1609 | sizeof(filters)/sizeof(filters[0])); | |
1610 | usage(); | |
1611 | } | |
42b39485 | 1612 | break; |
43c02e7b VB |
1613 | default: |
1614 | found = 0; | |
1615 | for (i=0; protos[i].mode != 0; i++) { | |
43c02e7b | 1616 | if (ch == protos[i].arg) { |
b8a802bc VB |
1617 | found = 1; |
1618 | protos[i].enabled++; | |
43c02e7b VB |
1619 | } |
1620 | } | |
1621 | if (!found) | |
1622 | usage(); | |
1623 | } | |
1624 | } | |
8482abe9 | 1625 | |
efa6a7a3 VB |
1626 | if (version) { |
1627 | version_display(stdout, "lldpd", version > 1); | |
1628 | exit(0); | |
1629 | } | |
1630 | ||
59ca5ddb | 1631 | if (ctlname == NULL) ctlname = LLDPD_CTL_SOCKET; |
1c1c519f | 1632 | |
8482abe9 VB |
1633 | /* Set correct smart mode */ |
1634 | for (i=0; (filters[i].a != -1) && (filters[i].a != smart); i++); | |
1635 | if (filters[i].a == -1) { | |
1636 | fprintf(stderr, "Incorrect mode for -H\n"); | |
1637 | usage(); | |
1638 | } | |
1639 | smart = filters[i].b; | |
6f8925be | 1640 | |
9856f279 | 1641 | log_init(use_syslog, debug, __progname); |
4b9a5a23 | 1642 | tzset(); /* Get timezone info before chroot */ |
52767c4d VB |
1643 | if (use_syslog && daemonize) { |
1644 | /* So, we use syslog and we daemonize (or we are started by | |
1645 | * upstart/systemd). No need to continue writing to stdout. */ | |
1646 | int fd; | |
1647 | if ((fd = open("/dev/null", O_RDWR, 0)) != -1) { | |
1648 | dup2(fd, STDIN_FILENO); | |
1649 | dup2(fd, STDOUT_FILENO); | |
1650 | dup2(fd, STDERR_FILENO); | |
1651 | if (fd > 2) close(fd); | |
1652 | } | |
1653 | } | |
f98e5666 | 1654 | log_debug("main", "lldpd " PACKAGE_VERSION " starting..."); |
2472bfda | 1655 | version_check(); |
6f8925be | 1656 | |
2e91d1a1 | 1657 | /* Grab uid and gid to use for priv sep */ |
28bf4022 | 1658 | #ifdef ENABLE_PRIVSEP |
2e91d1a1 | 1659 | if ((user = getpwnam(PRIVSEP_USER)) == NULL) |
ff0dde0e | 1660 | fatalx("main", "no " PRIVSEP_USER " user for privilege separation, please create it"); |
2e91d1a1 VB |
1661 | uid = user->pw_uid; |
1662 | if ((group = getgrnam(PRIVSEP_GROUP)) == NULL) | |
ff0dde0e | 1663 | fatalx("main", "no " PRIVSEP_GROUP " group for privilege separation, please create it"); |
2e91d1a1 | 1664 | gid = group->gr_gid; |
28bf4022 | 1665 | #endif |
2e91d1a1 VB |
1666 | |
1667 | /* Create and setup socket */ | |
8172214f | 1668 | int retry = 1; |
6f8925be | 1669 | log_debug("main", "creating control socket"); |
0262adbb | 1670 | while ((ctl = ctl_create(ctlname)) == -1) { |
8172214f VB |
1671 | if (retry-- && errno == EADDRINUSE) { |
1672 | /* Check if a daemon is really listening */ | |
1673 | int tfd; | |
1674 | log_info("main", "unable to create control socket because it already exists"); | |
1675 | log_info("main", "check if another instance is running"); | |
0262adbb | 1676 | if ((tfd = ctl_connect(ctlname)) != -1) { |
8172214f VB |
1677 | /* Another instance is running */ |
1678 | close(tfd); | |
1679 | log_warnx("main", "another instance is running, please stop it"); | |
a87db231 | 1680 | fatalx("main", "giving up"); |
8172214f VB |
1681 | } else if (errno == ECONNREFUSED) { |
1682 | /* Nobody is listening */ | |
1683 | log_info("main", "old control socket is present, clean it"); | |
0262adbb | 1684 | ctl_cleanup(ctlname); |
8172214f VB |
1685 | continue; |
1686 | } | |
1687 | log_warn("main", "cannot determine if another daemon is already running"); | |
a87db231 | 1688 | fatalx("main", "giving up"); |
8172214f | 1689 | } |
ba54dbd0 | 1690 | log_warn("main", "unable to create control socket at %s", ctlname); |
a87db231 | 1691 | fatalx("main", "giving up"); |
2e91d1a1 | 1692 | } |
28bf4022 | 1693 | #ifdef ENABLE_PRIVSEP |
0262adbb | 1694 | if (chown(ctlname, uid, gid) == -1) |
6f8925be | 1695 | log_warn("main", "unable to chown control socket"); |
0262adbb | 1696 | if (chmod(ctlname, |
2e91d1a1 VB |
1697 | S_IRUSR | S_IWUSR | S_IXUSR | |
1698 | S_IRGRP | S_IWGRP | S_IXGRP) == -1) | |
6f8925be | 1699 | log_warn("main", "unable to chmod control socket"); |
28bf4022 | 1700 | #endif |
2e91d1a1 | 1701 | |
595184b0 VB |
1702 | /* Disable SIGPIPE */ |
1703 | signal(SIGPIPE, SIG_IGN); | |
1704 | ||
003620d3 ST |
1705 | /* Disable SIGHUP, until handlers are installed */ |
1706 | signal(SIGHUP, SIG_IGN); | |
1707 | ||
a14e6dca VB |
1708 | /* Daemonization, unless started by upstart, systemd or launchd or debug */ |
1709 | #ifndef HOST_OS_OSX | |
1710 | if (daemonize && | |
1711 | !lldpd_started_by_upstart() && !lldpd_started_by_systemd()) { | |
1712 | int pid; | |
1713 | char *spid; | |
fdcf78fb | 1714 | log_info("main", "going into background"); |
52767c4d | 1715 | if (daemon(0, 1) != 0) |
a14e6dca VB |
1716 | fatal("main", "failed to detach daemon"); |
1717 | if ((pid = open(pidfile, | |
1718 | O_TRUNC | O_CREAT | O_WRONLY, 0666)) == -1) | |
1719 | fatal("main", "unable to open pid file " LLDPD_PID_FILE | |
1720 | " (or the specified one)"); | |
1721 | if (asprintf(&spid, "%d\n", getpid()) == -1) | |
1722 | fatal("main", "unable to create pid file " LLDPD_PID_FILE | |
1723 | " (or the specified one)"); | |
1724 | if (write(pid, spid, strlen(spid)) == -1) | |
1725 | fatal("main", "unable to write pid file " LLDPD_PID_FILE | |
1726 | " (or the specified one)"); | |
1727 | free(spid); | |
1728 | close(pid); | |
1729 | } | |
1730 | #endif | |
1731 | ||
a14e6dca VB |
1732 | /* Configuration with lldpcli */ |
1733 | if (lldpcli) { | |
1734 | log_debug("main", "invoking lldpcli for configuration"); | |
1735 | if (lldpd_configure(use_syslog, debug, lldpcli, ctlname) == -1) | |
1736 | fatal("main", "unable to spawn lldpcli"); | |
1737 | } | |
1738 | ||
ae87586a MT |
1739 | /* Try to read system information from /etc/os-release if possible. |
1740 | Fall back to lsb_release for compatibility. */ | |
6f8925be | 1741 | log_debug("main", "get OS/LSB release information"); |
ae87586a MT |
1742 | lsb_release = lldpd_get_os_release(); |
1743 | if (!lsb_release) { | |
1744 | lsb_release = lldpd_get_lsb_release(); | |
1745 | } | |
c036b15d | 1746 | |
e47140de VB |
1747 | log_debug("main", "initialize privilege separation"); |
1748 | #ifdef ENABLE_PRIVSEP | |
1749 | priv_init(PRIVSEP_CHROOT, ctl, uid, gid); | |
1750 | #else | |
1751 | priv_init(PRIVSEP_CHROOT, ctl, 0, 0); | |
1752 | #endif | |
1753 | ||
d6e889b6 | 1754 | /* Initialization of global configuration */ |
43c02e7b VB |
1755 | if ((cfg = (struct lldpd *) |
1756 | calloc(1, sizeof(struct lldpd))) == NULL) | |
6f8925be | 1757 | fatal("main", NULL); |
43c02e7b | 1758 | |
9da663f7 | 1759 | lldpd_alloc_default_local_port(cfg); |
0262adbb | 1760 | cfg->g_ctlname = ctlname; |
2e91d1a1 | 1761 | cfg->g_ctl = ctl; |
8ec333bd VB |
1762 | cfg->g_config.c_mgmt_pattern = mgmtp; |
1763 | cfg->g_config.c_cid_pattern = cidp; | |
1764 | cfg->g_config.c_iface_pattern = interfaces; | |
1765 | cfg->g_config.c_smart = smart; | |
ba529c82 VB |
1766 | if (lldpcli) |
1767 | cfg->g_config.c_paused = 1; | |
8ec333bd | 1768 | cfg->g_config.c_receiveonly = receiveonly; |
e4ff3ed5 | 1769 | cfg->g_config.c_tx_interval = LLDPD_TX_INTERVAL; |
c10302a3 | 1770 | cfg->g_config.c_tx_hold = LLDPD_TX_HOLD; |
42589660 | 1771 | cfg->g_config.c_max_neighbors = LLDPD_MAX_NEIGHBORS; |
be511d00 | 1772 | #ifdef ENABLE_LLDPMED |
486a6133 | 1773 | cfg->g_config.c_enable_fast_start = enable_fast_start; |
b9de0ca6 | 1774 | cfg->g_config.c_tx_fast_init = LLDPD_FAST_INIT; |
1775 | cfg->g_config.c_tx_fast_interval = LLDPD_FAST_TX_INTERVAL; | |
be511d00 | 1776 | #endif |
d6e889b6 VB |
1777 | #ifdef USE_SNMP |
1778 | cfg->g_snmp = snmp; | |
1779 | cfg->g_snmp_agentx = agentx; | |
1780 | #endif /* USE_SNMP */ | |
dfbd7185 | 1781 | cfg->g_config.c_bond_slave_src_mac_type = \ |
0e29d794 | 1782 | LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED; |
43c02e7b VB |
1783 | |
1784 | /* Get ioctl socket */ | |
6f8925be | 1785 | log_debug("main", "get an ioctl socket"); |
43c02e7b | 1786 | if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) |
6f8925be | 1787 | fatal("main", "failed to get ioctl socket"); |
43c02e7b | 1788 | |
c036b15d | 1789 | /* Description */ |
dc21d049 VB |
1790 | if (!(cfg->g_config.c_advertise_version = advertise_version) && |
1791 | lsb_release && lsb_release[strlen(lsb_release) - 1] == '\n') | |
c036b15d VB |
1792 | lsb_release[strlen(lsb_release) - 1] = '\0'; |
1793 | cfg->g_lsb_release = lsb_release; | |
40ce835b | 1794 | if (descr_override) |
8ec333bd | 1795 | cfg->g_config.c_description = descr_override; |
40ce835b | 1796 | |
d4afb919 | 1797 | if (platform_override) |
8ec333bd | 1798 | cfg->g_config.c_platform = platform_override; |
d4afb919 | 1799 | |
43c02e7b | 1800 | /* Set system capabilities */ |
6f8925be | 1801 | log_debug("main", "set system capabilities"); |
77507b69 VB |
1802 | if ((lchassis = (struct lldpd_chassis*) |
1803 | calloc(1, sizeof(struct lldpd_chassis))) == NULL) | |
6f8925be | 1804 | fatal("localchassis", NULL); |
ca838758 | 1805 | cfg->g_config.c_cap_advertise = 1; |
77507b69 | 1806 | lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN | |
6b095471 | 1807 | LLDP_CAP_ROUTER | LLDP_CAP_STATION; |
1c2217aa | 1808 | cfg->g_config.c_mgmt_advertise = 1; |
e6b36c87 | 1809 | TAILQ_INIT(&lchassis->c_mgmt); |
89840df0 VB |
1810 | #ifdef ENABLE_LLDPMED |
1811 | if (lldpmed > 0) { | |
4b292b55 | 1812 | if (lldpmed == LLDP_MED_CLASS_III) |
77507b69 VB |
1813 | lchassis->c_cap_available |= LLDP_CAP_TELEPHONE; |
1814 | lchassis->c_med_type = lldpmed; | |
4b292b55 VB |
1815 | lchassis->c_med_cap_available = LLDP_MED_CAP_CAP | |
1816 | LLDP_MED_CAP_IV | LLDP_MED_CAP_LOCATION | | |
1817 | LLDP_MED_CAP_POLICY | LLDP_MED_CAP_MDI_PSE | LLDP_MED_CAP_MDI_PD; | |
8ec333bd | 1818 | cfg->g_config.c_noinventory = noinventory; |
42bddd41 | 1819 | } else |
8ec333bd | 1820 | cfg->g_config.c_noinventory = 1; |
89840df0 | 1821 | #endif |
43c02e7b VB |
1822 | |
1823 | /* Set TTL */ | |
c10302a3 | 1824 | lchassis->c_ttl = cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold; |
43c02e7b | 1825 | |
6f8925be | 1826 | log_debug("main", "initialize protocols"); |
43c02e7b | 1827 | cfg->g_protocols = protos; |
b8a802bc VB |
1828 | for (i=0; protos[i].mode != 0; i++) { |
1829 | ||
1830 | /* With -ll, disable LLDP */ | |
1831 | if (protos[i].mode == LLDPD_MODE_LLDP) | |
1832 | protos[i].enabled %= 3; | |
1833 | /* With -ccc force CDPV2, enable CDPV1 */ | |
1834 | if (protos[i].mode == LLDPD_MODE_CDPV1 && protos[i].enabled == 3) { | |
1835 | protos[i].enabled = 1; | |
1836 | } | |
1837 | /* With -cc force CDPV1, enable CDPV2 */ | |
1838 | if (protos[i].mode == LLDPD_MODE_CDPV2 && protos[i].enabled == 2) { | |
1839 | protos[i].enabled = 1; | |
1840 | } | |
1841 | ||
611aba00 MS |
1842 | /* With -cccc disable CDPV1, enable CDPV2 */ |
1843 | if (protos[i].mode == LLDPD_MODE_CDPV1 && protos[i].enabled >= 4) { | |
1844 | protos[i].enabled = 0; | |
1845 | } | |
1846 | ||
1847 | /* With -cccc disable CDPV1, enable CDPV2; -ccccc will force CDPv2 */ | |
1848 | if (protos[i].mode == LLDPD_MODE_CDPV2 && protos[i].enabled == 4) { | |
1849 | protos[i].enabled = 1; | |
1850 | } | |
1851 | ||
0c877af0 | 1852 | if (protos[i].enabled > 1) |
6f8925be | 1853 | log_info("main", "protocol %s enabled and forced", protos[i].name); |
0c877af0 | 1854 | else if (protos[i].enabled) |
6f8925be | 1855 | log_info("main", "protocol %s enabled", protos[i].name); |
0c877af0 | 1856 | else |
6f8925be | 1857 | log_info("main", "protocol %s disabled", protos[i].name); |
b8a802bc | 1858 | } |
43c02e7b VB |
1859 | |
1860 | TAILQ_INIT(&cfg->g_hardware); | |
77507b69 VB |
1861 | TAILQ_INIT(&cfg->g_chassis); |
1862 | TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries); | |
9898ac07 | 1863 | lchassis->c_refcount++; /* We should always keep a reference to local chassis */ |
43c02e7b | 1864 | |
d6e889b6 | 1865 | /* Main loop */ |
6f8925be | 1866 | log_debug("main", "start main loop"); |
d6e889b6 | 1867 | levent_loop(cfg); |
24cb9684 | 1868 | lchassis->c_refcount--; |
d6e889b6 | 1869 | lldpd_exit(cfg); |
8262175d | 1870 | free(cfg); |
43c02e7b VB |
1871 | |
1872 | return (0); | |
1873 | } |