]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/client.c
client: ability to set maximum of neighbors from lldpcli
[thirdparty/lldpd.git] / src / daemon / client.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3 * Copyright (c) 2008 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 static ssize_t
22 client_handle_none(struct lldpd *cfg, enum hmsg_type *type,
23 void *input, int input_len, void **output, int *subscribed)
24 {
25 log_info("rpc", "received noop request from client");
26 *type = NONE;
27 return 0;
28 }
29
30 /* Return the global configuration */
31 static ssize_t
32 client_handle_get_configuration(struct lldpd *cfg, enum hmsg_type *type,
33 void *input, int input_len, void **output, int *subscribed)
34 {
35 ssize_t output_len;
36 log_debug("rpc", "client requested configuration");
37 output_len = lldpd_config_serialize(&cfg->g_config, output);
38 if (output_len <= 0) {
39 output_len = 0;
40 *type = NONE;
41 }
42 return output_len;
43 }
44
45 static char*
46 xstrdup(const char *str)
47 {
48 if (!str) return NULL;
49 return strdup(str);
50 }
51
52 /* Change the global configuration */
53 static ssize_t
54 client_handle_set_configuration(struct lldpd *cfg, enum hmsg_type *type,
55 void *input, int input_len, void **output, int *subscribed)
56 {
57 struct lldpd_config *config;
58
59 log_debug("rpc", "client request a change in configuration");
60 /* Get the proposed configuration. */
61 if (lldpd_config_unserialize(input, input_len, &config) <= 0) {
62 *type = NONE;
63 return 0;
64 }
65
66 #define CHANGED(w) (config->w != cfg->g_config.w)
67 #define CHANGED_STR(w) (!(config->w == cfg->g_config.w || \
68 (config->w && cfg->g_config.w && !strcmp(config->w, cfg->g_config.w))))
69
70 /* What needs to be done? Transmit delay? */
71 if (CHANGED(c_tx_interval) && config->c_tx_interval != 0) {
72 if (config->c_tx_interval < 0) {
73 log_debug("rpc", "client asked for immediate retransmission");
74 } else {
75 log_debug("rpc", "client change transmit interval to %d",
76 config->c_tx_interval);
77 cfg->g_config.c_tx_interval = config->c_tx_interval;
78 cfg->g_config.c_ttl = cfg->g_config.c_tx_interval *
79 cfg->g_config.c_tx_hold;
80 }
81 levent_send_now(cfg);
82 }
83 if (CHANGED(c_tx_hold) && config->c_tx_hold > 0) {
84 log_debug("rpc", "client change transmit hold to %d",
85 config->c_tx_hold);
86 cfg->g_config.c_tx_hold = config->c_tx_hold;
87 cfg->g_config.c_ttl = cfg->g_config.c_tx_interval *
88 cfg->g_config.c_tx_hold;
89 }
90 if (CHANGED(c_max_neighbors) && config->c_max_neighbors > 0) {
91 log_debug("rpc", "client change maximum neighbors to %d",
92 config->c_max_neighbors);
93 cfg->g_config.c_max_neighbors = config->c_max_neighbors;
94 }
95 if (CHANGED(c_lldp_portid_type) &&
96 config->c_lldp_portid_type > LLDP_PORTID_SUBTYPE_UNKNOWN &&
97 config->c_lldp_portid_type <= LLDP_PORTID_SUBTYPE_MAX) {
98 log_debug("rpc", "change lldp portid tlv subtype to %d",
99 config->c_lldp_portid_type);
100 cfg->g_config.c_lldp_portid_type = config->c_lldp_portid_type;
101 levent_update_now(cfg);
102 }
103 if (CHANGED(c_lldp_agent_type) &&
104 config->c_lldp_agent_type > LLDP_AGENT_TYPE_UNKNOWN &&
105 config->c_lldp_agent_type <= LLDP_AGENT_TYPE_MAX) {
106 log_debug("rpc", "change lldp agent type to %d",
107 config->c_lldp_agent_type);
108 cfg->g_config.c_lldp_agent_type = config->c_lldp_agent_type;
109 levent_update_now(cfg);
110 }
111 /* Pause/resume */
112 if (CHANGED(c_paused)) {
113 log_debug("rpc", "client asked to %s lldpd",
114 config->c_paused?"pause":"resume");
115 cfg->g_config.c_paused = config->c_paused;
116 levent_send_now(cfg);
117 }
118
119 #ifdef ENABLE_LLDPMED
120 if (CHANGED(c_enable_fast_start)) {
121 cfg->g_config.c_enable_fast_start = config->c_enable_fast_start;
122 log_debug("rpc", "client asked to %s fast start",
123 cfg->g_config.c_enable_fast_start?"enable":"disable");
124 }
125 if (CHANGED(c_tx_fast_interval) &&
126 config->c_tx_fast_interval > 0) {
127 log_debug("rpc", "change fast interval to %d", config->c_tx_fast_interval);
128 cfg->g_config.c_tx_fast_interval = config->c_tx_fast_interval;
129 }
130 #endif
131 if (CHANGED_STR(c_iface_pattern)) {
132 log_debug("rpc", "change interface pattern to %s",
133 config->c_iface_pattern?config->c_iface_pattern:"(NULL)");
134 free(cfg->g_config.c_iface_pattern);
135 cfg->g_config.c_iface_pattern = xstrdup(config->c_iface_pattern);
136 levent_update_now(cfg);
137 }
138 if (CHANGED_STR(c_perm_ifaces)) {
139 log_debug("rpc", "change permanent interface pattern to %s",
140 config->c_perm_ifaces?config->c_perm_ifaces:"(NULL)");
141 free(cfg->g_config.c_perm_ifaces);
142 cfg->g_config.c_perm_ifaces = xstrdup(config->c_perm_ifaces);
143 levent_update_now(cfg);
144 }
145 if (CHANGED_STR(c_mgmt_pattern)) {
146 log_debug("rpc", "change management pattern to %s",
147 config->c_mgmt_pattern?config->c_mgmt_pattern:"(NULL)");
148 free(cfg->g_config.c_mgmt_pattern);
149 cfg->g_config.c_mgmt_pattern = xstrdup(config->c_mgmt_pattern);
150 levent_update_now(cfg);
151 }
152 if (CHANGED_STR(c_cid_string)) {
153 log_debug("rpc", "change chassis ID string to %s",
154 config->c_cid_string?config->c_cid_string:"(NULL)");
155 free(cfg->g_config.c_cid_string);
156 cfg->g_config.c_cid_string = xstrdup(config->c_cid_string);
157 free(LOCAL_CHASSIS(cfg)->c_id);
158 LOCAL_CHASSIS(cfg)->c_id = NULL;
159 lldpd_update_localchassis(cfg);
160 levent_update_now(cfg);
161 }
162 if (CHANGED_STR(c_description)) {
163 log_debug("rpc", "change chassis description to %s",
164 config->c_description?config->c_description:"(NULL)");
165 free(cfg->g_config.c_description);
166 cfg->g_config.c_description = xstrdup(config->c_description);
167 lldpd_update_localchassis(cfg);
168 levent_update_now(cfg);
169 }
170 if (CHANGED_STR(c_platform)) {
171 log_debug("rpc", "change platform description to %s",
172 config->c_platform?config->c_platform:"(NULL)");
173 free(cfg->g_config.c_platform);
174 cfg->g_config.c_platform = xstrdup(config->c_platform);
175 lldpd_update_localchassis(cfg);
176 levent_update_now(cfg);
177 }
178 if (CHANGED_STR(c_hostname)) {
179 log_debug("rpc", "change system name to %s",
180 config->c_hostname?config->c_hostname:"(NULL)");
181 free(cfg->g_config.c_hostname);
182 cfg->g_config.c_hostname = xstrdup(config->c_hostname);
183 lldpd_update_localchassis(cfg);
184 levent_update_now(cfg);
185 }
186 if (CHANGED(c_set_ifdescr)) {
187 log_debug("rpc", "%s setting of interface description based on discovered neighbors",
188 config->c_set_ifdescr?"enable":"disable");
189 cfg->g_config.c_set_ifdescr = config->c_set_ifdescr;
190 levent_update_now(cfg);
191 }
192 if (CHANGED(c_promisc)) {
193 log_debug("rpc", "%s promiscuous mode on managed interfaces",
194 config->c_promisc?"enable":"disable");
195 cfg->g_config.c_promisc = config->c_promisc;
196 levent_update_now(cfg);
197 }
198 if (CHANGED(c_cap_advertise)) {
199 log_debug("rpc", "%s chassis capabilities advertisement",
200 config->c_cap_advertise?"enable":"disable");
201 cfg->g_config.c_cap_advertise = config->c_cap_advertise;
202 levent_update_now(cfg);
203 }
204 if (CHANGED(c_mgmt_advertise)) {
205 log_debug("rpc", "%s management addresses advertisement",
206 config->c_mgmt_advertise?"enable":"disable");
207 cfg->g_config.c_mgmt_advertise = config->c_mgmt_advertise;
208 levent_update_now(cfg);
209 }
210 if (CHANGED(c_bond_slave_src_mac_type)) {
211 if (config->c_bond_slave_src_mac_type >
212 LLDP_BOND_SLAVE_SRC_MAC_TYPE_UNKNOWN &&
213 config->c_bond_slave_src_mac_type <=
214 LLDP_BOND_SLAVE_SRC_MAC_TYPE_MAX) {
215 log_debug("rpc", "change bond src mac type to %d",
216 config->c_bond_slave_src_mac_type);
217 cfg->g_config.c_bond_slave_src_mac_type =
218 config->c_bond_slave_src_mac_type;
219 } else {
220 log_info("rpc", "Invalid bond slave src mac type: %d\n",
221 config->c_bond_slave_src_mac_type);
222 }
223 }
224
225 lldpd_config_cleanup(config);
226 free(config);
227
228 return 0;
229 }
230
231 /* Return the list of interfaces.
232 Input: nothing.
233 Output: list of interface names (lldpd_interface_list)
234 */
235 static ssize_t
236 client_handle_get_interfaces(struct lldpd *cfg, enum hmsg_type *type,
237 void *input, int input_len, void **output, int *subscribed)
238 {
239 struct lldpd_interface *iff, *iff_next;
240 struct lldpd_hardware *hardware;
241 ssize_t output_len;
242
243 /* Build the list of interfaces */
244 struct lldpd_interface_list ifs;
245
246 log_debug("rpc", "client request the list of interfaces");
247 TAILQ_INIT(&ifs);
248 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
249 if ((iff = (struct lldpd_interface*)malloc(sizeof(
250 struct lldpd_interface))) == NULL)
251 fatal("rpc", NULL);
252 iff->name = hardware->h_ifname;
253 TAILQ_INSERT_TAIL(&ifs, iff, next);
254 }
255
256 output_len = lldpd_interface_list_serialize(&ifs, output);
257 if (output_len <= 0) {
258 output_len = 0;
259 *type = NONE;
260 }
261
262 /* Free the temporary list */
263 for (iff = TAILQ_FIRST(&ifs);
264 iff != NULL;
265 iff = iff_next) {
266 iff_next = TAILQ_NEXT(iff, next);
267 TAILQ_REMOVE(&ifs, iff, next);
268 free(iff);
269 }
270
271 return output_len;
272 }
273
274 /* Return the local chassis.
275 Input: nothing.
276 Output: local chassis (lldpd_chassis)
277 */
278 static ssize_t
279 client_handle_get_local_chassis(struct lldpd *cfg, enum hmsg_type *type,
280 void *input, int input_len, void **output, int *subscribed)
281 {
282 struct lldpd_chassis *chassis = LOCAL_CHASSIS(cfg);
283 ssize_t output_len;
284
285 log_debug("rpc", "client request the local chassis");
286 output_len = lldpd_chassis_serialize(chassis, output);
287 if (output_len <= 0) {
288 output_len = 0;
289 *type = NONE;
290 }
291
292 return output_len;
293 }
294
295 /* Return all available information related to an interface
296 Input: name of the interface (serialized)
297 Output: Information about the interface (lldpd_hardware)
298 */
299 static ssize_t
300 client_handle_get_interface(struct lldpd *cfg, enum hmsg_type *type,
301 void *input, int input_len, void **output, int *subscribed)
302 {
303 char *name;
304 struct lldpd_hardware *hardware;
305 void *p;
306
307 /* Get name of the interface */
308 if (marshal_unserialize(string, input, input_len, &p) <= 0) {
309 *type = NONE;
310 return 0;
311 }
312 name = p;
313
314 /* Search appropriate hardware */
315 log_debug("rpc", "client request interface %s", name);
316 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
317 if (!strcmp(hardware->h_ifname, name)) {
318 ssize_t output_len = lldpd_hardware_serialize(hardware, output);
319 free(name);
320 if (output_len <= 0) {
321 *type = NONE;
322 return 0;
323 }
324 return output_len;
325 }
326
327 log_warnx("rpc", "no interface %s found", name);
328 free(name);
329 *type = NONE;
330 return 0;
331 }
332
333 /* Return all available information related to an interface
334 Input: name of the interface (serialized)
335 Output: Information about the interface (lldpd_hardware)
336 */
337 static ssize_t
338 client_handle_get_default_port(struct lldpd *cfg, enum hmsg_type *type,
339 void *input, int input_len, void **output, int *subscribed)
340 {
341 log_debug("rpc", "client request the default local port");
342 ssize_t output_len = lldpd_port_serialize(cfg->g_default_local_port, output);
343 if (output_len <= 0) {
344 *type = NONE;
345 return 0;
346 }
347 return output_len;
348 }
349
350 static int
351 _client_handle_set_port(struct lldpd *cfg,
352 struct lldpd_port *port, struct lldpd_port_set *set)
353 {
354 #ifdef ENABLE_LLDPMED
355 struct lldpd_med_loc *loc = NULL;
356 #endif
357 if (set->local_id) {
358 log_debug("rpc", "requested change to Port ID");
359 free(port->p_id);
360 port->p_id = strdup(set->local_id);
361 port->p_id_len = strlen(set->local_id);
362 port->p_id_subtype = LLDP_PORTID_SUBTYPE_LOCAL;
363 port->p_descr_force = 0;
364 }
365 if (set->local_descr) {
366 log_debug("rpc", "requested change to Port Description");
367 free(port->p_descr);
368 port->p_descr = strdup(set->local_descr);
369 port->p_descr_force = 1;
370 }
371 switch (set->rxtx) {
372 case LLDPD_RXTX_TXONLY:
373 log_debug("rpc", "requested TX only mode");
374 port->p_disable_rx = 1;
375 port->p_disable_tx = 0;
376 break;
377 case LLDPD_RXTX_RXONLY:
378 log_debug("rpc", "requested RX only mode");
379 port->p_disable_rx = 0;
380 port->p_disable_tx = 1;
381 break;
382 case LLDPD_RXTX_BOTH:
383 log_debug("rpc", "requested RX/TX mode");
384 port->p_disable_rx = port->p_disable_tx = 0;
385 break;
386 case LLDPD_RXTX_DISABLED:
387 log_debug("rpc", "requested disabled mode");
388 port->p_disable_rx = port->p_disable_tx = 1;
389 break;
390 }
391 #ifdef ENABLE_LLDPMED
392 if (set->med_policy && set->med_policy->type > 0) {
393 log_debug("rpc", "requested change to MED policy");
394 if (set->med_policy->type > LLDP_MED_APPTYPE_LAST) {
395 log_warnx("rpc", "invalid policy provided: %d",
396 set->med_policy->type);
397 return -1;
398 }
399 memcpy(&port->p_med_policy[set->med_policy->type - 1],
400 set->med_policy, sizeof(struct lldpd_med_policy));
401 port->p_med_cap_enabled |= LLDP_MED_CAP_POLICY;
402 }
403 if (set->med_location && set->med_location->format > 0) {
404 char *newdata = NULL;
405 log_debug("rpc", "requested change to MED location");
406 if (set->med_location->format > LLDP_MED_LOCFORMAT_LAST) {
407 log_warnx("rpc", "invalid location format provided: %d",
408 set->med_location->format);
409 return -1;
410 }
411 loc = \
412 &port->p_med_location[set->med_location->format - 1];
413 free(loc->data);
414 memcpy(loc, set->med_location, sizeof(struct lldpd_med_loc));
415 if (!loc->data || !(newdata = malloc(loc->data_len))) loc->data_len = 0;
416 if (newdata) memcpy(newdata, loc->data, loc->data_len);
417 loc->data = newdata;
418 port->p_med_cap_enabled |= LLDP_MED_CAP_LOCATION;
419 }
420 if (set->med_power) {
421 log_debug("rpc", "requested change to MED power");
422 memcpy(&port->p_med_power, set->med_power,
423 sizeof(struct lldpd_med_power));
424 switch (set->med_power->devicetype) {
425 case LLDP_MED_POW_TYPE_PD:
426 port->p_med_cap_enabled |= LLDP_MED_CAP_MDI_PD;
427 port->p_med_cap_enabled &= ~LLDP_MED_CAP_MDI_PSE;
428 break;
429 case LLDP_MED_POW_TYPE_PSE:
430 port->p_med_cap_enabled |= LLDP_MED_CAP_MDI_PSE;
431 port->p_med_cap_enabled &= ~LLDP_MED_CAP_MDI_PD;
432 break;
433 }
434 }
435 #endif
436 #ifdef ENABLE_DOT3
437 if (set->dot3_power) {
438 log_debug("rpc", "requested change to Dot3 power");
439 memcpy(&port->p_power, set->dot3_power,
440 sizeof(struct lldpd_dot3_power));
441 }
442 #endif
443 #ifdef ENABLE_CUSTOM
444 if (set->custom_list_clear) {
445 log_debug("rpc", "requested custom TLVs clear");
446 lldpd_custom_list_cleanup(port);
447 } else {
448 if (set->custom) {
449 log_info("rpc", "custom TLV op %s oui %02x:%02x:%02x subtype %x",
450 (set->custom_tlv_op == CUSTOM_TLV_REMOVE)?"remove":
451 (set->custom_tlv_op == CUSTOM_TLV_ADD)?"add":
452 "replace",
453 set->custom->oui[0],
454 set->custom->oui[1],
455 set->custom->oui[2],
456 set->custom->subtype);
457 switch (set->custom_tlv_op) {
458 case CUSTOM_TLV_REMOVE:
459 lldpd_custom_tlv_cleanup(port, set->custom);
460 break;
461 case CUSTOM_TLV_ADD:
462 lldpd_custom_tlv_add(port, set->custom);
463 break;
464 case CUSTOM_TLV_REPLACE:
465 default:
466 lldpd_custom_tlv_cleanup(port, set->custom);
467 lldpd_custom_tlv_add(port, set->custom);
468 break;
469 }
470 }
471 }
472 #endif
473 return 0;
474 }
475
476 /* Set some port related settings (policy, location, power)
477 Input: name of the interface, policy/location/power setting to be modified
478 Output: nothing
479 */
480 static ssize_t
481 client_handle_set_port(struct lldpd *cfg, enum hmsg_type *type,
482 void *input, int input_len, void **output, int *subscribed)
483 {
484 int ret = 0;
485 struct lldpd_port_set *set = NULL;
486 struct lldpd_hardware *hardware = NULL;
487
488 if (lldpd_port_set_unserialize(input, input_len, &set) <= 0) {
489 *type = NONE;
490 return 0;
491 }
492 if (!set->ifname) {
493 log_warnx("rpc", "no interface provided");
494 goto set_port_finished;
495 }
496
497 /* Search the appropriate hardware */
498 if (strlen(set->ifname) == 0) {
499 log_debug("rpc", "client request change to default port");
500 if (_client_handle_set_port(cfg, cfg->g_default_local_port, set) == -1)
501 goto set_port_finished;
502 ret = 1;
503 } else {
504 log_debug("rpc", "client request change to port %s", set->ifname);
505 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
506 if (!strcmp(hardware->h_ifname, set->ifname)) {
507 struct lldpd_port *port = &hardware->h_lport;
508 if (_client_handle_set_port(cfg, port, set) == -1)
509 goto set_port_finished;
510 ret = 1;
511 break;
512 }
513 }
514 }
515
516 if (ret == 0)
517 log_warn("rpc", "no interface %s found", set->ifname);
518 else
519 levent_update_now(cfg);
520
521 set_port_finished:
522 if (!ret) *type = NONE;
523 free(set->ifname);
524 free(set->local_id);
525 free(set->local_descr);
526 #ifdef ENABLE_LLDPMED
527 free(set->med_policy);
528 if (set->med_location) free(set->med_location->data);
529 free(set->med_location);
530 free(set->med_power);
531 #endif
532 #ifdef ENABLE_DOT3
533 free(set->dot3_power);
534 #endif
535 #ifdef ENABLE_CUSTOM
536 if (set->custom) {
537 free(set->custom->oui_info);
538 free(set->custom);
539 }
540 #endif
541 free(set);
542 return 0;
543 }
544
545 /* Register subscribtion to neighbor changes */
546 static ssize_t
547 client_handle_subscribe(struct lldpd *cfg, enum hmsg_type *type,
548 void *input, int input_len, void **output, int *subscribed)
549 {
550 log_debug("rpc", "client subscribe to changes");
551 *subscribed = 1;
552 return 0;
553 }
554
555 struct client_handle {
556 enum hmsg_type type;
557 const char *name;
558 ssize_t (*handle)(struct lldpd*, enum hmsg_type *,
559 void *, int, void **, int *);
560 };
561
562 static struct client_handle client_handles[] = {
563 { NONE, "None", client_handle_none },
564 { GET_CONFIG, "Get configuration", client_handle_get_configuration },
565 { SET_CONFIG, "Set configuration", client_handle_set_configuration },
566 { GET_INTERFACES, "Get interfaces", client_handle_get_interfaces },
567 { GET_INTERFACE, "Get interface", client_handle_get_interface },
568 { GET_DEFAULT_PORT, "Get default port", client_handle_get_default_port },
569 { GET_CHASSIS, "Get local chassis", client_handle_get_local_chassis },
570 { SET_PORT, "Set port", client_handle_set_port },
571 { SUBSCRIBE, "Subscribe", client_handle_subscribe },
572 { 0, NULL } };
573
574 int
575 client_handle_client(struct lldpd *cfg,
576 ssize_t(*send)(void *, int, void *, size_t),
577 void *out,
578 enum hmsg_type type, void *buffer, size_t n,
579 int *subscribed)
580 {
581 struct client_handle *ch;
582 void *answer; ssize_t len, sent;
583
584 log_debug("rpc", "handle client request");
585 for (ch = client_handles; ch->handle != NULL; ch++) {
586 if (ch->type == type) {
587 TRACE(LLDPD_CLIENT_REQUEST(ch->name));
588 answer = NULL;
589 len = ch->handle(cfg, &type, buffer, n, &answer,
590 subscribed);
591 sent = send(out, type, answer, len);
592 free(answer);
593 return sent;
594 }
595 }
596
597 log_warnx("rpc", "unknown message request (%d) received",
598 type);
599 return -1;
600 }