1 /* -*- mode: c; c-file-style: "openbsd" -*- */
3 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
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.
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.
22 client_handle_none(struct lldpd
*cfg
, enum hmsg_type
*type
,
23 void *input
, int input_len
, void **output
, int *subscribed
)
25 log_info("rpc", "received noop request from client");
30 /* Return the global configuration */
32 client_handle_get_configuration(struct lldpd
*cfg
, enum hmsg_type
*type
,
33 void *input
, int input_len
, void **output
, int *subscribed
)
36 log_debug("rpc", "client requested configuration");
37 output_len
= lldpd_config_serialize(&cfg
->g_config
, output
);
38 if (output_len
<= 0) {
46 xstrdup(const char *str
)
48 if (!str
) return NULL
;
52 /* Change the global configuration */
54 client_handle_set_configuration(struct lldpd
*cfg
, enum hmsg_type
*type
,
55 void *input
, int input_len
, void **output
, int *subscribed
)
57 struct lldpd_config
*config
;
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) {
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))))
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");
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
;
83 if (CHANGED(c_tx_hold
) && config
->c_tx_hold
> 0) {
84 log_debug("rpc", "client change transmit hold to %d",
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
;
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
;
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
);
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
);
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
);
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");
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
;
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
;
220 log_info("rpc", "Invalid bond slave src mac type: %d\n",
221 config
->c_bond_slave_src_mac_type
);
225 lldpd_config_cleanup(config
);
231 /* Return the list of interfaces.
233 Output: list of interface names (lldpd_interface_list)
236 client_handle_get_interfaces(struct lldpd
*cfg
, enum hmsg_type
*type
,
237 void *input
, int input_len
, void **output
, int *subscribed
)
239 struct lldpd_interface
*iff
, *iff_next
;
240 struct lldpd_hardware
*hardware
;
243 /* Build the list of interfaces */
244 struct lldpd_interface_list ifs
;
246 log_debug("rpc", "client request the list of interfaces");
248 TAILQ_FOREACH(hardware
, &cfg
->g_hardware
, h_entries
) {
249 if ((iff
= (struct lldpd_interface
*)malloc(sizeof(
250 struct lldpd_interface
))) == NULL
)
252 iff
->name
= hardware
->h_ifname
;
253 TAILQ_INSERT_TAIL(&ifs
, iff
, next
);
256 output_len
= lldpd_interface_list_serialize(&ifs
, output
);
257 if (output_len
<= 0) {
262 /* Free the temporary list */
263 for (iff
= TAILQ_FIRST(&ifs
);
266 iff_next
= TAILQ_NEXT(iff
, next
);
267 TAILQ_REMOVE(&ifs
, iff
, next
);
274 /* Return the local chassis.
276 Output: local chassis (lldpd_chassis)
279 client_handle_get_local_chassis(struct lldpd
*cfg
, enum hmsg_type
*type
,
280 void *input
, int input_len
, void **output
, int *subscribed
)
282 struct lldpd_chassis
*chassis
= LOCAL_CHASSIS(cfg
);
285 log_debug("rpc", "client request the local chassis");
286 output_len
= lldpd_chassis_serialize(chassis
, output
);
287 if (output_len
<= 0) {
295 /* Return all available information related to an interface
296 Input: name of the interface (serialized)
297 Output: Information about the interface (lldpd_hardware)
300 client_handle_get_interface(struct lldpd
*cfg
, enum hmsg_type
*type
,
301 void *input
, int input_len
, void **output
, int *subscribed
)
304 struct lldpd_hardware
*hardware
;
307 /* Get name of the interface */
308 if (marshal_unserialize(string
, input
, input_len
, &p
) <= 0) {
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
);
320 if (output_len
<= 0) {
327 log_warnx("rpc", "no interface %s found", name
);
333 /* Return all available information related to an interface
334 Input: name of the interface (serialized)
335 Output: Information about the interface (lldpd_hardware)
338 client_handle_get_default_port(struct lldpd
*cfg
, enum hmsg_type
*type
,
339 void *input
, int input_len
, void **output
, int *subscribed
)
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) {
351 _client_handle_set_port(struct lldpd
*cfg
,
352 struct lldpd_port
*port
, struct lldpd_port_set
*set
)
354 #ifdef ENABLE_LLDPMED
355 struct lldpd_med_loc
*loc
= NULL
;
358 log_debug("rpc", "requested change to Port 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;
365 if (set
->local_descr
) {
366 log_debug("rpc", "requested change to Port Description");
368 port
->p_descr
= strdup(set
->local_descr
);
369 port
->p_descr_force
= 1;
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;
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;
382 case LLDPD_RXTX_BOTH
:
383 log_debug("rpc", "requested RX/TX mode");
384 port
->p_disable_rx
= port
->p_disable_tx
= 0;
386 case LLDPD_RXTX_DISABLED
:
387 log_debug("rpc", "requested disabled mode");
388 port
->p_disable_rx
= port
->p_disable_tx
= 1;
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
);
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
;
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
);
412 &port
->p_med_location
[set
->med_location
->format
- 1];
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
);
418 port
->p_med_cap_enabled
|= LLDP_MED_CAP_LOCATION
;
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
;
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
;
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
));
444 if (set
->custom_list_clear
) {
445 log_debug("rpc", "requested custom TLVs clear");
446 lldpd_custom_list_cleanup(port
);
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":
456 set
->custom
->subtype
);
457 switch (set
->custom_tlv_op
) {
458 case CUSTOM_TLV_REMOVE
:
459 lldpd_custom_tlv_cleanup(port
, set
->custom
);
462 lldpd_custom_tlv_add(port
, set
->custom
);
464 case CUSTOM_TLV_REPLACE
:
466 lldpd_custom_tlv_cleanup(port
, set
->custom
);
467 lldpd_custom_tlv_add(port
, set
->custom
);
476 /* Set some port related settings (policy, location, power)
477 Input: name of the interface, policy/location/power setting to be modified
481 client_handle_set_port(struct lldpd
*cfg
, enum hmsg_type
*type
,
482 void *input
, int input_len
, void **output
, int *subscribed
)
485 struct lldpd_port_set
*set
= NULL
;
486 struct lldpd_hardware
*hardware
= NULL
;
488 if (lldpd_port_set_unserialize(input
, input_len
, &set
) <= 0) {
493 log_warnx("rpc", "no interface provided");
494 goto set_port_finished
;
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
;
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
;
517 log_warn("rpc", "no interface %s found", set
->ifname
);
519 levent_update_now(cfg
);
522 if (!ret
) *type
= NONE
;
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
);
533 free(set
->dot3_power
);
537 free(set
->custom
->oui_info
);
545 /* Register subscribtion to neighbor changes */
547 client_handle_subscribe(struct lldpd
*cfg
, enum hmsg_type
*type
,
548 void *input
, int input_len
, void **output
, int *subscribed
)
550 log_debug("rpc", "client subscribe to changes");
555 struct client_handle
{
558 ssize_t (*handle
)(struct lldpd
*, enum hmsg_type
*,
559 void *, int, void **, int *);
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
},
575 client_handle_client(struct lldpd
*cfg
,
576 ssize_t(*send
)(void *, int, void *, size_t),
578 enum hmsg_type type
, void *buffer
, size_t n
,
581 struct client_handle
*ch
;
582 void *answer
; ssize_t len
, sent
;
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
));
589 len
= ch
->handle(cfg
, &type
, buffer
, n
, &answer
,
591 sent
= send(out
, type
, answer
, len
);
597 log_warnx("rpc", "unknown message request (%d) received",