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