2 * Copyright (c) 2011-2016 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
18 * @brief LLDP CLI handling
21 #include <vnet/lisp-cp/lisp_types.h>
22 #include <vnet/lldp/lldp_node.h>
24 #ifndef ETHER_ADDR_LEN
25 #include <net/ethernet.h>
28 typedef enum lldp_cfg_err
36 lldp_cfg_err_to_clib_err (lldp_cfg_err_t e)
43 case lldp_not_supported:
44 return clib_error_return (0, "not supported");
45 case lldp_invalid_arg:
46 return clib_error_return (0, "invalid argument");
52 lldp_cfg_intf_set (u32 hw_if_index, int enable)
54 lldp_main_t *lm = &lldp_main;
55 vnet_main_t *vnm = lm->vnet_main;
56 ethernet_main_t *em = ðernet_main;
57 const vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
58 const ethernet_interface_t *eif = ethernet_get_interface (em, hw_if_index);
62 return lldp_not_supported;
67 lldp_intf_t *n = lldp_get_intf (lm, hw_if_index);
73 n = lldp_create_intf (lm, hw_if_index);
74 const vnet_sw_interface_t *sw =
75 vnet_get_sw_interface (lm->vnet_main, hi->sw_if_index);
76 if (sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
78 lldp_schedule_intf (lm, n);
83 lldp_intf_t *n = lldp_get_intf (lm, hi->sw_if_index);
84 lldp_delete_intf (lm, n);
91 lldp_intf_cmd (vlib_main_t * vm, unformat_input_t * input,
92 vlib_cli_command_t * cmd)
94 lldp_main_t *lm = &lldp_main;
95 vnet_main_t *vnm = lm->vnet_main;
99 if (unformat (input, "%U %U", unformat_vnet_hw_interface, vnm, &hw_if_index,
100 unformat_vlib_enable_disable, &enable))
103 lldp_cfg_err_to_clib_err (lldp_cfg_intf_set (hw_if_index, enable));
107 return clib_error_return (0, "unknown input `%U'",
108 format_unformat_error, input);
113 static lldp_cfg_err_t
114 lldp_cfg_set (u8 ** host, int hold_time, int tx_interval)
116 lldp_main_t *lm = &lldp_main;
120 vec_free (lm->sys_name);
121 lm->sys_name = *host;
126 if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD)
128 return lldp_invalid_arg;
130 if (lm->msg_tx_hold != hold_time)
132 lm->msg_tx_hold = hold_time;
138 if (tx_interval < LLDP_MIN_TX_INTERVAL ||
139 tx_interval > LLDP_MAX_TX_INTERVAL)
141 return lldp_invalid_arg;
143 if (lm->msg_tx_interval != tx_interval)
146 lm->msg_tx_interval = tx_interval;
151 vlib_process_signal_event (lm->vlib_main, lm->lldp_process_node_index,
152 LLDP_EVENT_RESCHEDULE, 0);
157 static clib_error_t *
158 lldp_cfg_cmd (vlib_main_t * vm, unformat_input_t * input,
159 vlib_cli_command_t * cmd)
164 clib_error_t *ret = NULL;
166 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
168 if (unformat (input, "system-name %s", &host))
171 else if (unformat (input, "tx-hold %d", &hold_time))
173 if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD)
176 clib_error_return (0,
177 "invalid tx-hold `%d' (out of range <%d,%d>)",
178 hold_time, LLDP_MIN_TX_HOLD,
183 else if (unformat (input, "tx-interval %d", &tx_interval))
185 if (tx_interval < LLDP_MIN_TX_INTERVAL ||
186 tx_interval > LLDP_MAX_TX_INTERVAL)
189 clib_error_return (0,
190 "invalid tx-interval `%d' (out of range <%d,%d>)",
191 tx_interval, LLDP_MIN_TX_INTERVAL,
192 LLDP_MAX_TX_INTERVAL);
198 ret = clib_error_return (0, "unknown input `%U'",
199 format_unformat_error, input);
204 lldp_cfg_err_to_clib_err (lldp_cfg_set (&host, hold_time, tx_interval));
211 VLIB_CLI_COMMAND(set_interface_lldp_cmd, static) = {
212 .path = "set interface lldp",
213 .short_help = "set interface lldp <interface> (enable | disable) ",
214 .function = lldp_intf_cmd,
217 VLIB_CLI_COMMAND(set_lldp_cmd, static) = {
219 .short_help = "set lldp [system-name <string>] [tx-hold <value>] "
220 "[tx-interval <value>]",
221 .function = lldp_cfg_cmd,
226 lldp_chassis_id_subtype_str (lldp_chassis_id_subtype_t t)
230 #define F(num, val, str) \
233 foreach_chassis_id_subtype (F)
236 return "unknown chassis subtype";
240 lldp_port_id_subtype_str (lldp_port_id_subtype_t t)
244 #define F(num, val, str) \
247 foreach_port_id_subtype (F)
250 return "unknown port subtype";
254 * format port id subtype&value
256 * @param va - 1st argument - unsigned - port id subtype
257 * @param va - 2nd argument - u8* - port id
258 * @param va - 3rd argument - unsigned - port id length
259 * @param va - 4th argument - int - 1 for detailed output, 0 for simple
262 format_lldp_port_id (u8 * s, va_list * va)
264 const lldp_port_id_subtype_t subtype = va_arg (*va, unsigned);
265 const u8 *id = va_arg (*va, u8 *);
266 const unsigned len = va_arg (*va, unsigned);
267 const int detail = va_arg (*va, int);
274 case LLDP_PORT_ID_SUBTYPE_NAME (intf_alias):
276 case LLDP_PORT_ID_SUBTYPE_NAME (port_comp):
278 case LLDP_PORT_ID_SUBTYPE_NAME (local):
280 case LLDP_PORT_ID_SUBTYPE_NAME (intf_name):
283 s = format (s, "%U(%s)", format_ascii_bytes, id, len,
284 lldp_port_id_subtype_str (subtype));
288 s = format (s, "%U", format_ascii_bytes, id, len);
291 case LLDP_PORT_ID_SUBTYPE_NAME (mac_addr):
292 if (ETHER_ADDR_LEN == len)
296 s = format (s, "%U(%s)", format_mac_address, id,
297 lldp_port_id_subtype_str (subtype));
301 s = format (s, "%U", format_mac_address, id);
306 case LLDP_PORT_ID_SUBTYPE_NAME (net_addr):
312 s = format (s, "%U(%s)", format_hex_bytes, id, len,
313 lldp_port_id_subtype_str (subtype));
317 s = format (s, "%U", format_hex_bytes, id, len);
325 * format chassis id subtype&value
327 * @param s format string
328 * @param va - 1st argument - unsigned - chassis id subtype
329 * @param va - 2nd argument - u8* - chassis id
330 * @param va - 3rd argument - unsigned - chassis id length
331 * @param va - 4th argument - int - 1 for detailed output, 0 for simple
334 format_lldp_chassis_id (u8 * s, va_list * va)
336 const lldp_chassis_id_subtype_t subtype =
337 va_arg (*va, lldp_chassis_id_subtype_t);
338 const u8 *id = va_arg (*va, u8 *);
339 const unsigned len = va_arg (*va, unsigned);
340 const int detail = va_arg (*va, int);
347 case LLDP_CHASS_ID_SUBTYPE_NAME (chassis_comp):
349 case LLDP_CHASS_ID_SUBTYPE_NAME (intf_alias):
351 case LLDP_CHASS_ID_SUBTYPE_NAME (port_comp):
353 case LLDP_PORT_ID_SUBTYPE_NAME (local):
355 case LLDP_CHASS_ID_SUBTYPE_NAME (intf_name):
358 s = format (s, "%U(%s)", format_ascii_bytes, id, len,
359 lldp_chassis_id_subtype_str (subtype));
363 s = format (s, "%U", format_ascii_bytes, id, len);
366 case LLDP_CHASS_ID_SUBTYPE_NAME (mac_addr):
367 if (ETHER_ADDR_LEN == len)
371 s = format (s, "%U(%s)", format_mac_address, id,
372 lldp_chassis_id_subtype_str (subtype));
376 s = format (s, "%U", format_mac_address, id);
381 case LLDP_CHASS_ID_SUBTYPE_NAME (net_addr):
386 s = format (s, "%U(%s)", format_hex_bytes, id, len,
387 lldp_chassis_id_subtype_str (subtype));
391 s = format (s, "%U", format_hex_bytes, id, len);
399 * convert a tlv code to human-readable string
402 lldp_tlv_code_str (lldp_tlv_code_t t)
409 foreach_lldp_tlv_type (F)
412 return "unknown lldp tlv";
416 * format a single LLDP TLV
418 * @param s format string
419 * @param va variable list - pointer to lldp_tlv_t is expected
422 format_lldp_tlv (u8 * s, va_list * va)
424 const lldp_tlv_t *tlv = va_arg (*va, lldp_tlv_t *);
429 u16 l = lldp_tlv_get_length (tlv);
430 switch (lldp_tlv_get_code (tlv))
432 case LLDP_TLV_NAME (chassis_id):
433 s = format (s, "%U", format_lldp_chassis_id,
434 ((lldp_chassis_id_tlv_t *) tlv)->subtype,
435 ((lldp_chassis_id_tlv_t *) tlv)->id,
436 l - STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype), 1);
438 case LLDP_TLV_NAME (port_id):
439 s = format (s, "%U", format_lldp_port_id,
440 ((lldp_port_id_tlv_t *) tlv)->subtype,
441 ((lldp_port_id_tlv_t *) tlv)->id,
442 l - STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype), 1);
444 case LLDP_TLV_NAME (ttl):
445 s = format (s, "%d", ntohs (((lldp_ttl_tlv_t *) tlv)->ttl));
447 case LLDP_TLV_NAME (sys_name):
449 case LLDP_TLV_NAME (sys_desc):
450 s = format (s, "%U", format_ascii_bytes, tlv->v, l);
453 s = format (s, "%U", format_hex_bytes, tlv->v, l);
460 format_time_ago (u8 * s, va_list * va)
462 f64 ago = va_arg (*va, double);
463 f64 now = va_arg (*va, double);
466 return format (s, "never");
468 return format (s, "%.1fs ago", now - ago);
472 format_lldp_intfs_detail (u8 * s, vlib_main_t * vm, const lldp_main_t * lm)
474 vnet_main_t *vnm = &vnet_main;
475 const lldp_intf_t *n;
476 const vnet_hw_interface_t *hw;
477 const vnet_sw_interface_t *sw;
478 s = format (s, "LLDP configuration:\n");
481 s = format (s, "Configured system name: %U\n", format_ascii_bytes,
482 lm->sys_name, vec_len (lm->sys_name));
484 s = format (s, "Configured tx-hold: %d\n", (int) lm->msg_tx_hold);
485 s = format (s, "Configured tx-interval: %d\n", (int) lm->msg_tx_interval);
486 s = format (s, "\nLLDP-enabled interface table:\n");
487 f64 now = vlib_time_now (vm);
492 hw = vnet_get_hw_interface(vnm, n->hw_if_index);
493 sw = vnet_get_sw_interface(lm->vnet_main, hw->sw_if_index);
494 /* Interface shutdown */
495 if (!(sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
497 s = format(s, "\nInterface name: %s\nInterface/peer state: "
498 "interface down\nLast packet sent: %U\n",
499 hw->name, format_time_ago, n->last_sent, now);
501 else if (now < n->last_heard + n->ttl)
504 "\nInterface name: %s\nInterface/peer state: "
505 "active\nPeer chassis ID: %U\nRemote port ID: %U\nLast "
506 "packet sent: %U\nLast packet received: %U\n",
507 hw->name, format_lldp_chassis_id, n->chassis_id_subtype,
508 n->chassis_id, vec_len(n->chassis_id), 1,
509 format_lldp_port_id, n->port_id_subtype, n->port_id,
510 vec_len(n->port_id), 1, format_time_ago, n->last_sent,
511 now, format_time_ago, n->last_heard, now);
515 s = format(s, "\nInterface name: %s\nInterface/peer state: "
516 "inactive(timeout)\nLast known peer chassis ID: "
517 "%U\nLast known peer port ID: %U\nLast packet sent: "
518 "%U\nLast packet received: %U\n",
519 hw->name, format_lldp_chassis_id, n->chassis_id_subtype,
520 n->chassis_id, vec_len(n->chassis_id), 1,
521 format_lldp_port_id, n->port_id_subtype, n->port_id,
522 vec_len(n->port_id), 1, format_time_ago, n->last_sent,
523 now, format_time_ago, n->last_heard, now);
531 format_lldp_intfs (u8 * s, va_list * va)
533 vlib_main_t *vm = va_arg (*va, vlib_main_t *);
534 const lldp_main_t *lm = va_arg (*va, lldp_main_t *);
535 const int detail = va_arg (*va, int);
536 vnet_main_t *vnm = &vnet_main;
537 const lldp_intf_t *n;
541 return format_lldp_intfs_detail (s, vm, lm);
544 f64 now = vlib_time_now (vm);
545 s = format (s, "%-25s %-25s %-25s %=15s %=15s %=10s\n", "Local interface",
546 "Peer chassis ID", "Remote port ID", "Last heard", "Last sent",
552 const vnet_hw_interface_t *hw =
553 vnet_get_hw_interface(vnm, n->hw_if_index);
554 const vnet_sw_interface_t *sw =
555 vnet_get_sw_interface(lm->vnet_main, hw->sw_if_index);
556 /* Interface shutdown */
557 if (!(sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
559 if (now < n->last_heard + n->ttl)
561 s = format(s, "%-25s %-25U %-25U %=15U %=15U %=10s\n", hw->name,
562 format_lldp_chassis_id, n->chassis_id_subtype,
563 n->chassis_id, vec_len(n->chassis_id), 0,
564 format_lldp_port_id, n->port_id_subtype, n->port_id,
565 vec_len(n->port_id), 0, format_time_ago, n->last_heard,
566 now, format_time_ago, n->last_sent, now, "active");
570 s = format(s, "%-25s %-25s %-25s %=15U %=15U %=10s\n", hw->name,
571 "", "", format_time_ago, n->last_heard, now,
572 format_time_ago, n->last_sent, now, "inactive");
579 static clib_error_t *
580 show_lldp (vlib_main_t * vm, unformat_input_t * input,
581 CLIB_UNUSED (vlib_cli_command_t * lmd))
583 lldp_main_t *lm = &lldp_main;
585 if (unformat (input, "detail"))
587 vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 1);
591 vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 0);
597 VLIB_CLI_COMMAND(show_lldp_command, static) = {
599 .short_help = "show lldp [detail]",
600 .function = show_lldp,
605 * packet trace format function, very similar to
606 * lldp_packet_scan except that we call the per TLV format
607 * functions instead of the per TLV processing functions
610 lldp_input_format_trace (u8 * s, va_list * args)
612 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
613 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
614 const lldp_input_trace_t *t = va_arg (*args, lldp_input_trace_t *);
616 const lldp_tlv_t *tlv;
618 while (((cur + lldp_tlv_get_length ((lldp_tlv_t *) cur)) <
621 tlv = (lldp_tlv_t *) cur;
624 s = format (s, "TLV #%d(%s): %U\n", lldp_tlv_get_code (tlv),
625 lldp_tlv_code_str (lldp_tlv_get_code (tlv)),
626 format_lldp_tlv, tlv);
630 s = format (s, " TLV #%d(%s): %U\n", lldp_tlv_get_code (tlv),
631 lldp_tlv_code_str (lldp_tlv_get_code (tlv)),
632 format_lldp_tlv, tlv);
634 cur += STRUCT_SIZE_OF (lldp_tlv_t, head) + lldp_tlv_get_length (tlv);
641 * fd.io coding-style-patch-verification: ON
644 * eval: (c-set-style "gnu")