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_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
);
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
);
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
);
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");
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
;
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
;
208 log_info("rpc", "Invalid bond slave src mac type: %d\n",
209 config
->c_bond_slave_src_mac_type
);
213 lldpd_config_cleanup(config
);
219 /* Return the list of interfaces.
221 Output: list of interface names (lldpd_interface_list)
224 client_handle_get_interfaces(struct lldpd
*cfg
, enum hmsg_type
*type
,
225 void *input
, int input_len
, void **output
, int *subscribed
)
227 struct lldpd_interface
*iff
, *iff_next
;
228 struct lldpd_hardware
*hardware
;
231 /* Build the list of interfaces */
232 struct lldpd_interface_list ifs
;
234 log_debug("rpc", "client request the list of interfaces");
236 TAILQ_FOREACH(hardware
, &cfg
->g_hardware
, h_entries
) {
237 if ((iff
= (struct lldpd_interface
*)malloc(sizeof(
238 struct lldpd_interface
))) == NULL
)
240 iff
->name
= hardware
->h_ifname
;
241 TAILQ_INSERT_TAIL(&ifs
, iff
, next
);
244 output_len
= lldpd_interface_list_serialize(&ifs
, output
);
245 if (output_len
<= 0) {
250 /* Free the temporary list */
251 for (iff
= TAILQ_FIRST(&ifs
);
254 iff_next
= TAILQ_NEXT(iff
, next
);
255 TAILQ_REMOVE(&ifs
, iff
, next
);
262 /* Return the local chassis.
264 Output: local chassis (lldpd_chassis)
267 client_handle_get_local_chassis(struct lldpd
*cfg
, enum hmsg_type
*type
,
268 void *input
, int input_len
, void **output
, int *subscribed
)
270 struct lldpd_chassis
*chassis
= LOCAL_CHASSIS(cfg
);
273 log_debug("rpc", "client request the local chassis");
274 output_len
= lldpd_chassis_serialize(chassis
, output
);
275 if (output_len
<= 0) {
283 /* Return all available information related to an interface
284 Input: name of the interface (serialized)
285 Output: Information about the interface (lldpd_hardware)
288 client_handle_get_interface(struct lldpd
*cfg
, enum hmsg_type
*type
,
289 void *input
, int input_len
, void **output
, int *subscribed
)
292 struct lldpd_hardware
*hardware
;
295 /* Get name of the interface */
296 if (marshal_unserialize(string
, input
, input_len
, &p
) <= 0) {
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
);
308 if (output_len
<= 0) {
315 log_warnx("rpc", "no interface %s found", name
);
321 /* Return all available information related to an interface
322 Input: name of the interface (serialized)
323 Output: Information about the interface (lldpd_hardware)
326 client_handle_get_default_port(struct lldpd
*cfg
, enum hmsg_type
*type
,
327 void *input
, int input_len
, void **output
, int *subscribed
)
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) {
339 _client_handle_set_port(struct lldpd
*cfg
,
340 struct lldpd_port
*port
, struct lldpd_port_set
*set
)
342 #ifdef ENABLE_LLDPMED
343 struct lldpd_med_loc
*loc
= NULL
;
346 log_debug("rpc", "requested change to Port 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;
353 if (set
->local_descr
) {
354 log_debug("rpc", "requested change to Port Description");
356 port
->p_descr
= strdup(set
->local_descr
);
357 port
->p_descr_force
= 1;
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;
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;
370 case LLDPD_RXTX_BOTH
:
371 log_debug("rpc", "requested RX/TX mode");
372 port
->p_disable_rx
= port
->p_disable_tx
= 0;
374 case LLDPD_RXTX_DISABLED
:
375 log_debug("rpc", "requested disabled mode");
376 port
->p_disable_rx
= port
->p_disable_tx
= 1;
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
);
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
;
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
);
400 &port
->p_med_location
[set
->med_location
->format
- 1];
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
);
406 port
->p_med_cap_enabled
|= LLDP_MED_CAP_LOCATION
;
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
;
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
;
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
));
432 if (set
->custom_list_clear
) {
433 log_debug("rpc", "requested custom TLVs clear");
434 lldpd_custom_list_cleanup(port
);
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":
444 set
->custom
->subtype
);
445 switch (set
->custom_tlv_op
) {
446 case CUSTOM_TLV_REMOVE
:
447 lldpd_custom_tlv_cleanup(port
, set
->custom
);
450 lldpd_custom_tlv_add(port
, set
->custom
);
452 case CUSTOM_TLV_REPLACE
:
454 lldpd_custom_tlv_cleanup(port
, set
->custom
);
455 lldpd_custom_tlv_add(port
, set
->custom
);
464 /* Set some port related settings (policy, location, power)
465 Input: name of the interface, policy/location/power setting to be modified
469 client_handle_set_port(struct lldpd
*cfg
, enum hmsg_type
*type
,
470 void *input
, int input_len
, void **output
, int *subscribed
)
473 struct lldpd_port_set
*set
= NULL
;
474 struct lldpd_hardware
*hardware
= NULL
;
476 if (lldpd_port_set_unserialize(input
, input_len
, &set
) <= 0) {
481 log_warnx("rpc", "no interface provided");
482 goto set_port_finished
;
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
;
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
;
505 log_warn("rpc", "no interface %s found", set
->ifname
);
507 levent_update_now(cfg
);
510 if (!ret
) *type
= NONE
;
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
);
521 free(set
->dot3_power
);
525 free(set
->custom
->oui_info
);
533 /* Register subscribtion to neighbor changes */
535 client_handle_subscribe(struct lldpd
*cfg
, enum hmsg_type
*type
,
536 void *input
, int input_len
, void **output
, int *subscribed
)
538 log_debug("rpc", "client subscribe to changes");
543 struct client_handle
{
546 ssize_t (*handle
)(struct lldpd
*, enum hmsg_type
*,
547 void *, int, void **, int *);
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
},
563 client_handle_client(struct lldpd
*cfg
,
564 ssize_t(*send
)(void *, int, void *, size_t),
566 enum hmsg_type type
, void *buffer
, size_t n
,
569 struct client_handle
*ch
;
570 void *answer
; ssize_t len
, sent
;
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
));
577 len
= ch
->handle(cfg
, &type
, buffer
, n
, &answer
,
579 sent
= send(out
, type
, answer
, len
);
585 log_warnx("rpc", "unknown message request (%d) received",