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