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/ethernet/ethernet.h>
22 #include <vnet/ip/ip.h>
23 #include <lldp/lldp.h>
24 #include <lldp/lldp_node.h>
26 #ifndef ETHER_ADDR_LEN
27 #include <net/ethernet.h>
31 lldp_cfg_err_to_clib_err (lldp_cfg_err_t e)
38 case lldp_not_supported:
39 return clib_error_return (0, "not supported");
40 case lldp_invalid_arg:
41 return clib_error_return (0, "invalid argument");
42 case lldp_internal_error:
43 return clib_error_return (0, "internal error");
49 lldp_cfg_intf_set (u32 hw_if_index, u8 ** port_desc, u8 ** mgmt_ip4,
50 u8 ** mgmt_ip6, u8 ** mgmt_oid, int enable)
52 clib_error_t *error = 0;
53 lldp_main_t *lm = &lldp_main;
54 vnet_main_t *vnm = lm->vnet_main;
55 ethernet_main_t *em = ðernet_main;
56 const vnet_hw_interface_t *hi;
57 const ethernet_interface_t *eif;
59 if (pool_is_free_index (vnm->interface_main.hw_interfaces, hw_if_index))
61 return lldp_invalid_arg;
64 hi = vnet_get_hw_interface (vnm, hw_if_index);
65 eif = ethernet_get_interface (em, hw_if_index);
68 return lldp_not_supported;
73 lldp_intf_t *n = lldp_get_intf (lm, hw_if_index);
79 n = lldp_create_intf (lm, hw_if_index);
81 if (port_desc && *port_desc)
83 n->port_desc = *port_desc;
87 if (mgmt_ip4 && *mgmt_ip4)
89 n->mgmt_ip4 = *mgmt_ip4;
93 if (mgmt_ip6 && *mgmt_ip6)
95 n->mgmt_ip6 = *mgmt_ip6;
99 if (mgmt_oid && *mgmt_oid)
101 n->mgmt_oid = *mgmt_oid;
105 /* Add MAC address to an interface's filter */
106 if (hi->caps & VNET_HW_IF_CAP_MAC_FILTER)
109 vnet_hw_interface_add_del_mac_address (lm->vnet_main,
115 clib_error_free (error);
116 lldp_delete_intf (lm, n);
117 return lldp_internal_error;
121 const vnet_sw_interface_t *sw =
122 vnet_get_sw_interface (lm->vnet_main, hi->sw_if_index);
123 if (sw->flags & (VNET_SW_INTERFACE_FLAG_ADMIN_UP))
125 lldp_schedule_intf (lm, n);
130 lldp_intf_t *n = lldp_get_intf (lm, hi->sw_if_index);
131 lldp_delete_intf (lm, n);
132 /* Remove MAC address from the interface's filter */
133 if ((n) && (hi->caps & VNET_HW_IF_CAP_MAC_FILTER))
136 vnet_hw_interface_add_del_mac_address (lm->vnet_main,
142 clib_error_free (error);
150 static clib_error_t *
151 lldp_intf_cmd (vlib_main_t * vm, unformat_input_t * input,
152 vlib_cli_command_t * cmd)
154 lldp_main_t *lm = &lldp_main;
155 vnet_main_t *vnm = lm->vnet_main;
156 u32 sw_if_index = (u32) ~ 0;
158 u8 *port_desc = NULL;
159 u8 *mgmt_ip4 = NULL, *mgmt_ip6 = NULL, *mgmt_oid = NULL;
160 ip4_address_t ip4_addr;
161 ip6_address_t ip6_addr;
163 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
165 if (unformat (input, "sw_if_index %d", &sw_if_index))
168 (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
170 else if (unformat (input, "disable"))
172 else if (unformat (input, "port-desc %s", &port_desc))
175 if (unformat (input, "mgmt-ip4 %U", unformat_ip4_address, &ip4_addr))
177 vec_validate (mgmt_ip4, sizeof (ip4_address_t) - 1);
178 clib_memcpy (mgmt_ip4, &ip4_addr, sizeof (ip4_addr));
181 if (unformat (input, "mgmt-ip6 %U", unformat_ip6_address, &ip6_addr))
183 vec_validate (mgmt_ip6, sizeof (ip6_address_t) - 1);
184 clib_memcpy (mgmt_ip6, &ip6_addr, sizeof (ip6_addr));
186 else if (unformat (input, "mgmt-oid %s", &mgmt_oid))
192 if (sw_if_index == (u32) ~ 0)
193 return clib_error_return (0, "Interface name is invalid!");
195 return lldp_cfg_err_to_clib_err (lldp_cfg_intf_set (sw_if_index,
196 &port_desc, &mgmt_ip4,
197 &mgmt_ip6, &mgmt_oid,
202 lldp_cfg_set (u8 ** host, int hold_time, int tx_interval)
204 lldp_main_t *lm = &lldp_main;
209 vec_free (lm->sys_name);
210 lm->sys_name = *host;
216 if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD)
218 return lldp_invalid_arg;
220 if (lm->msg_tx_hold != hold_time)
222 lm->msg_tx_hold = hold_time;
229 if (tx_interval < LLDP_MIN_TX_INTERVAL ||
230 tx_interval > LLDP_MAX_TX_INTERVAL)
232 return lldp_invalid_arg;
234 if (lm->msg_tx_interval != tx_interval)
237 lm->msg_tx_interval = tx_interval;
243 vlib_process_signal_event (lm->vlib_main, lm->lldp_process_node_index,
244 LLDP_EVENT_RESCHEDULE, 0);
250 static clib_error_t *
251 lldp_cfg_cmd (vlib_main_t * vm, unformat_input_t * input,
252 vlib_cli_command_t * cmd)
257 clib_error_t *ret = NULL;
259 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
261 if (unformat (input, "system-name %s", &host))
264 else if (unformat (input, "tx-hold %d", &hold_time))
266 if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD)
269 clib_error_return (0,
270 "invalid tx-hold `%d' (out of range <%d,%d>)",
271 hold_time, LLDP_MIN_TX_HOLD,
276 else if (unformat (input, "tx-interval %d", &tx_interval))
278 if (tx_interval < LLDP_MIN_TX_INTERVAL ||
279 tx_interval > LLDP_MAX_TX_INTERVAL)
282 clib_error_return (0,
283 "invalid tx-interval `%d' (out of range <%d,%d>)",
284 tx_interval, LLDP_MIN_TX_INTERVAL,
285 LLDP_MAX_TX_INTERVAL);
295 lldp_cfg_err_to_clib_err (lldp_cfg_set (&host, hold_time, tx_interval));
301 VLIB_CLI_COMMAND(set_interface_lldp_cmd, static) = {
302 .path = "set interface lldp",
303 .short_help = "set interface lldp <interface> | sw_if_index <idx>"
304 " [port-desc <string>] [mgmt-ip4 <string>]"
305 " [mgmt-ip6 <string>] [mgmt-oid <string>] [disable]",
306 .function = lldp_intf_cmd,
309 VLIB_CLI_COMMAND(set_lldp_cmd, static) = {
311 .short_help = "set lldp [system-name <string>] [tx-hold <value>] "
312 "[tx-interval <value>]",
313 .function = lldp_cfg_cmd,
317 lldp_chassis_id_subtype_str (lldp_chassis_id_subtype_t t)
321 #define F(num, val, str) \
324 foreach_chassis_id_subtype (F)
327 return "unknown chassis subtype";
331 lldp_port_id_subtype_str (lldp_port_id_subtype_t t)
335 #define F(num, val, str) \
338 foreach_port_id_subtype (F)
341 return "unknown port subtype";
345 * format port id subtype&value
347 * @param va - 1st argument - unsigned - port id subtype
348 * @param va - 2nd argument - u8* - port id
349 * @param va - 3rd argument - unsigned - port id length
350 * @param va - 4th argument - int - 1 for detailed output, 0 for simple
353 format_lldp_port_id (u8 * s, va_list * va)
355 const lldp_port_id_subtype_t subtype = va_arg (*va, unsigned);
356 const u8 *id = va_arg (*va, u8 *);
357 const unsigned len = va_arg (*va, unsigned);
358 const int detail = va_arg (*va, int);
365 case LLDP_PORT_ID_SUBTYPE_NAME (intf_alias):
367 case LLDP_PORT_ID_SUBTYPE_NAME (port_comp):
369 case LLDP_PORT_ID_SUBTYPE_NAME (local):
371 case LLDP_PORT_ID_SUBTYPE_NAME (intf_name):
374 s = format (s, "%U(%s)", format_ascii_bytes, id, len,
375 lldp_port_id_subtype_str (subtype));
379 s = format (s, "%U", format_ascii_bytes, id, len);
382 case LLDP_PORT_ID_SUBTYPE_NAME (mac_addr):
383 if (ETHER_ADDR_LEN == len)
387 s = format (s, "%U(%s)", format_mac_address, id,
388 lldp_port_id_subtype_str (subtype));
392 s = format (s, "%U", format_mac_address, id);
397 case LLDP_PORT_ID_SUBTYPE_NAME (net_addr):
403 s = format (s, "%U(%s)", format_hex_bytes, id, len,
404 lldp_port_id_subtype_str (subtype));
408 s = format (s, "%U", format_hex_bytes, id, len);
416 * format chassis id subtype&value
418 * @param s format string
419 * @param va - 1st argument - unsigned - chassis id subtype
420 * @param va - 2nd argument - u8* - chassis id
421 * @param va - 3rd argument - unsigned - chassis id length
422 * @param va - 4th argument - int - 1 for detailed output, 0 for simple
425 format_lldp_chassis_id (u8 * s, va_list * va)
427 const lldp_chassis_id_subtype_t subtype =
428 va_arg (*va, lldp_chassis_id_subtype_t);
429 const u8 *id = va_arg (*va, u8 *);
430 const unsigned len = va_arg (*va, unsigned);
431 const int detail = va_arg (*va, int);
438 case LLDP_CHASS_ID_SUBTYPE_NAME (chassis_comp):
440 case LLDP_CHASS_ID_SUBTYPE_NAME (intf_alias):
442 case LLDP_CHASS_ID_SUBTYPE_NAME (port_comp):
444 case LLDP_PORT_ID_SUBTYPE_NAME (local):
446 case LLDP_CHASS_ID_SUBTYPE_NAME (intf_name):
449 s = format (s, "%U(%s)", format_ascii_bytes, id, len,
450 lldp_chassis_id_subtype_str (subtype));
454 s = format (s, "%U", format_ascii_bytes, id, len);
457 case LLDP_CHASS_ID_SUBTYPE_NAME (mac_addr):
458 if (ETHER_ADDR_LEN == len)
462 s = format (s, "%U(%s)", format_mac_address, id,
463 lldp_chassis_id_subtype_str (subtype));
467 s = format (s, "%U", format_mac_address, id);
472 case LLDP_CHASS_ID_SUBTYPE_NAME (net_addr):
477 s = format (s, "%U(%s)", format_hex_bytes, id, len,
478 lldp_chassis_id_subtype_str (subtype));
482 s = format (s, "%U", format_hex_bytes, id, len);
490 * convert a tlv code to human-readable string
493 lldp_tlv_code_str (lldp_tlv_code_t t)
500 foreach_lldp_tlv_type (F)
503 return "unknown lldp tlv";
507 * format a single LLDP TLV
509 * @param s format string
510 * @param va variable list - pointer to lldp_tlv_t is expected
513 format_lldp_tlv (u8 * s, va_list * va)
515 const lldp_tlv_t *tlv = va_arg (*va, lldp_tlv_t *);
520 u16 l = lldp_tlv_get_length (tlv);
521 switch (lldp_tlv_get_code (tlv))
523 case LLDP_TLV_NAME (chassis_id):
524 s = format (s, "%U", format_lldp_chassis_id,
525 ((lldp_chassis_id_tlv_t *) tlv)->subtype,
526 ((lldp_chassis_id_tlv_t *) tlv)->id,
527 l - STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype), 1);
529 case LLDP_TLV_NAME (port_id):
530 s = format (s, "%U", format_lldp_port_id,
531 ((lldp_port_id_tlv_t *) tlv)->subtype,
532 ((lldp_port_id_tlv_t *) tlv)->id,
533 l - STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype), 1);
535 case LLDP_TLV_NAME (ttl):
536 s = format (s, "%d", ntohs (((lldp_ttl_tlv_t *) tlv)->ttl));
538 case LLDP_TLV_NAME (sys_name):
540 case LLDP_TLV_NAME (sys_desc):
541 s = format (s, "%U", format_ascii_bytes, tlv->v, l);
544 s = format (s, "%U", format_hex_bytes, tlv->v, l);
551 format_time_ago (u8 * s, va_list * va)
553 f64 ago = va_arg (*va, double);
554 f64 now = va_arg (*va, double);
557 return format (s, "never");
559 return format (s, "%.1fs ago", now - ago);
563 format_lldp_intfs_detail (u8 * s, vlib_main_t * vm, const lldp_main_t * lm)
565 vnet_main_t *vnm = &vnet_main;
566 const lldp_intf_t *n;
567 const vnet_hw_interface_t *hw;
568 const vnet_sw_interface_t *sw;
569 s = format (s, "LLDP configuration:\n");
572 s = format (s, "Configured system name: %U\n", format_ascii_bytes,
573 lm->sys_name, vec_len (lm->sys_name));
576 s = format (s, "Configured tx-hold: %d\n", (int) lm->msg_tx_hold);
577 s = format (s, "Configured tx-interval: %d\n", (int) lm->msg_tx_interval);
578 s = format (s, "\nLLDP-enabled interface table:\n");
579 f64 now = vlib_time_now (vm);
583 hw = vnet_get_hw_interface(vnm, n->hw_if_index);
584 sw = vnet_get_sw_interface(lm->vnet_main, hw->sw_if_index);
586 s = format(s, "\nLocal Interface name: %v\n"
587 "Local Port Description: %s\n",
588 hw->name, n->port_desc);
591 s = format (s, "Local Management address: %U\n",
592 format_ip4_address, n->mgmt_ip4, vec_len (n->mgmt_ip4));
597 s = format (s, "Local Management address IPV6: %U\n",
598 format_ip6_address, n->mgmt_ip6, vec_len (n->mgmt_ip6));
603 s = format (s, "Local Management address OID: %U\n",
604 format_ascii_bytes, n->mgmt_oid, vec_len (n->mgmt_oid));
607 /* Interface shutdown */
608 if (!(sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
610 s = format(s, "Interface/peer state: interface down\n"
611 "Last packet sent: %U\n",
612 format_time_ago, n->last_sent, now);
614 else if (now < n->last_heard + n->ttl)
617 "Interface/peer state: active\n"
618 "Peer chassis ID: %U\nRemote port ID: %U\n"
619 "Last packet sent: %U\nLast packet received: %U\n",
620 format_lldp_chassis_id, n->chassis_id_subtype,
621 n->chassis_id, vec_len(n->chassis_id), 1,
622 format_lldp_port_id, n->port_id_subtype, n->port_id,
623 vec_len(n->port_id), 1, format_time_ago, n->last_sent,
624 now, format_time_ago, n->last_heard, now);
629 "Interface/peer state: inactive(timeout)\n"
630 "Last known peer chassis ID: %U\n"
631 "Last known peer port ID: %U\nLast packet sent: %U\n"
632 "Last packet received: %U\n",
633 format_lldp_chassis_id, n->chassis_id_subtype,
634 n->chassis_id, vec_len(n->chassis_id), 1,
635 format_lldp_port_id, n->port_id_subtype, n->port_id,
636 vec_len(n->port_id), 1, format_time_ago, n->last_sent,
637 now, format_time_ago, n->last_heard, now);
644 format_lldp_intfs (u8 * s, va_list * va)
646 vlib_main_t *vm = va_arg (*va, vlib_main_t *);
647 const lldp_main_t *lm = va_arg (*va, lldp_main_t *);
648 const int detail = va_arg (*va, int);
649 vnet_main_t *vnm = &vnet_main;
650 const lldp_intf_t *n;
654 return format_lldp_intfs_detail (s, vm, lm);
657 f64 now = vlib_time_now (vm);
658 s = format (s, "%-25s %-25s %-25s %=15s %=15s %=10s\n", "Local interface",
659 "Peer chassis ID", "Remote port ID", "Last heard", "Last sent",
664 const vnet_hw_interface_t *hw =
665 vnet_get_hw_interface(vnm, n->hw_if_index);
666 const vnet_sw_interface_t *sw =
667 vnet_get_sw_interface(lm->vnet_main, hw->sw_if_index);
668 /* Interface shutdown */
669 if (!(sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
671 if (now < n->last_heard + n->ttl)
673 s = format(s, "%-25v %-25U %-25U %=15U %=15U %=10s\n", hw->name,
674 format_lldp_chassis_id, n->chassis_id_subtype,
675 n->chassis_id, vec_len(n->chassis_id), 0,
676 format_lldp_port_id, n->port_id_subtype, n->port_id,
677 vec_len(n->port_id), 0, format_time_ago, n->last_heard,
678 now, format_time_ago, n->last_sent, now, "active");
682 s = format(s, "%-25v %-25s %-25s %=15U %=15U %=10s\n", hw->name,
683 "", "", format_time_ago, n->last_heard, now,
684 format_time_ago, n->last_sent, now, "inactive");
690 static clib_error_t *
691 show_lldp (vlib_main_t * vm, unformat_input_t * input,
692 CLIB_UNUSED (vlib_cli_command_t * lmd))
694 lldp_main_t *lm = &lldp_main;
696 if (unformat (input, "detail"))
698 vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 1);
702 vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 0);
707 VLIB_CLI_COMMAND(show_lldp_command, static) = {
709 .short_help = "show lldp [detail]",
710 .function = show_lldp,
714 * packet trace format function, very similar to
715 * lldp_packet_scan except that we call the per TLV format
716 * functions instead of the per TLV processing functions
719 lldp_input_format_trace (u8 * s, va_list * args)
721 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
722 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
723 const lldp_input_trace_t *t = va_arg (*args, lldp_input_trace_t *);
725 const lldp_tlv_t *tlv;
727 while (((cur + lldp_tlv_get_length ((lldp_tlv_t *) cur)) <
730 tlv = (lldp_tlv_t *) cur;
733 s = format (s, "TLV #%d(%s): %U\n", lldp_tlv_get_code (tlv),
734 lldp_tlv_code_str (lldp_tlv_get_code (tlv)),
735 format_lldp_tlv, tlv);
739 s = format (s, " TLV #%d(%s): %U\n", lldp_tlv_get_code (tlv),
740 lldp_tlv_code_str (lldp_tlv_get_code (tlv)),
741 format_lldp_tlv, tlv);
743 cur += STRUCT_SIZE_OF (lldp_tlv_t, head) + lldp_tlv_get_length (tlv);
750 * fd.io coding-style-patch-verification: ON
753 * eval: (c-set-style "gnu")