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.h>
23 #include <vnet/lldp/lldp_node.h>
25 #ifndef ETHER_ADDR_LEN
26 #include <net/ethernet.h>
30 lldp_cfg_err_to_clib_err (lldp_cfg_err_t e)
37 case lldp_not_supported:
38 return clib_error_return (0, "not supported");
39 case lldp_invalid_arg:
40 return clib_error_return (0, "invalid argument");
46 lldp_cfg_intf_set (u32 hw_if_index, u8 ** port_desc, int enable)
48 lldp_main_t *lm = &lldp_main;
49 vnet_main_t *vnm = lm->vnet_main;
50 ethernet_main_t *em = ðernet_main;
51 const vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
52 const ethernet_interface_t *eif = ethernet_get_interface (em, hw_if_index);
56 return lldp_not_supported;
61 lldp_intf_t *n = lldp_get_intf (lm, hw_if_index);
67 n = lldp_create_intf (lm, hw_if_index);
69 if (port_desc && *port_desc)
71 n->port_desc = *port_desc;
75 const vnet_sw_interface_t *sw =
76 vnet_get_sw_interface (lm->vnet_main, hi->sw_if_index);
77 if (sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
79 lldp_schedule_intf (lm, n);
84 lldp_intf_t *n = lldp_get_intf (lm, hi->sw_if_index);
85 lldp_delete_intf (lm, n);
92 lldp_intf_cmd (vlib_main_t * vm, unformat_input_t * input,
93 vlib_cli_command_t * cmd)
95 lldp_main_t *lm = &lldp_main;
96 vnet_main_t *vnm = lm->vnet_main;
97 u32 sw_if_index = (u32) ~ 0;
101 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
103 if (unformat (input, "sw_if_index %d", &sw_if_index))
106 (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
108 else if (unformat (input, "disable"))
110 else if (unformat (input, "port-desc %s", &port_desc))
116 if (sw_if_index == (u32) ~ 0)
117 return clib_error_return (0, "Interface name is invalid!");
119 return lldp_cfg_err_to_clib_err (lldp_cfg_intf_set (sw_if_index,
120 &port_desc, enable));
124 lldp_cfg_set (u8 ** host, int hold_time, int tx_interval)
126 lldp_main_t *lm = &lldp_main;
130 vec_free (lm->sys_name);
131 lm->sys_name = *host;
136 if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD)
138 return lldp_invalid_arg;
140 if (lm->msg_tx_hold != hold_time)
142 lm->msg_tx_hold = hold_time;
148 if (tx_interval < LLDP_MIN_TX_INTERVAL ||
149 tx_interval > LLDP_MAX_TX_INTERVAL)
151 return lldp_invalid_arg;
153 if (lm->msg_tx_interval != tx_interval)
156 lm->msg_tx_interval = tx_interval;
161 vlib_process_signal_event (lm->vlib_main, lm->lldp_process_node_index,
162 LLDP_EVENT_RESCHEDULE, 0);
167 static clib_error_t *
168 lldp_cfg_cmd (vlib_main_t * vm, unformat_input_t * input,
169 vlib_cli_command_t * cmd)
174 clib_error_t *ret = NULL;
176 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
178 if (unformat (input, "system-name %s", &host))
181 else if (unformat (input, "tx-hold %d", &hold_time))
183 if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD)
186 clib_error_return (0,
187 "invalid tx-hold `%d' (out of range <%d,%d>)",
188 hold_time, LLDP_MIN_TX_HOLD,
193 else if (unformat (input, "tx-interval %d", &tx_interval))
195 if (tx_interval < LLDP_MIN_TX_INTERVAL ||
196 tx_interval > LLDP_MAX_TX_INTERVAL)
199 clib_error_return (0,
200 "invalid tx-interval `%d' (out of range <%d,%d>)",
201 tx_interval, LLDP_MIN_TX_INTERVAL,
202 LLDP_MAX_TX_INTERVAL);
212 lldp_cfg_err_to_clib_err (lldp_cfg_set (&host, hold_time, tx_interval));
219 VLIB_CLI_COMMAND(set_interface_lldp_cmd, static) = {
220 .path = "set interface lldp",
221 .short_help = "set interface lldp <interface> | sw_if_index <idx>"
222 " [port-desc <string>] [disable]",
223 .function = lldp_intf_cmd,
226 VLIB_CLI_COMMAND(set_lldp_cmd, static) = {
228 .short_help = "set lldp [system-name <string>] [tx-hold <value>] "
229 "[tx-interval <value>]",
230 .function = lldp_cfg_cmd,
235 lldp_chassis_id_subtype_str (lldp_chassis_id_subtype_t t)
239 #define F(num, val, str) \
242 foreach_chassis_id_subtype (F)
245 return "unknown chassis subtype";
249 lldp_port_id_subtype_str (lldp_port_id_subtype_t t)
253 #define F(num, val, str) \
256 foreach_port_id_subtype (F)
259 return "unknown port subtype";
263 * format port id subtype&value
265 * @param va - 1st argument - unsigned - port id subtype
266 * @param va - 2nd argument - u8* - port id
267 * @param va - 3rd argument - unsigned - port id length
268 * @param va - 4th argument - int - 1 for detailed output, 0 for simple
271 format_lldp_port_id (u8 * s, va_list * va)
273 const lldp_port_id_subtype_t subtype = va_arg (*va, unsigned);
274 const u8 *id = va_arg (*va, u8 *);
275 const unsigned len = va_arg (*va, unsigned);
276 const int detail = va_arg (*va, int);
283 case LLDP_PORT_ID_SUBTYPE_NAME (intf_alias):
285 case LLDP_PORT_ID_SUBTYPE_NAME (port_comp):
287 case LLDP_PORT_ID_SUBTYPE_NAME (local):
289 case LLDP_PORT_ID_SUBTYPE_NAME (intf_name):
292 s = format (s, "%U(%s)", format_ascii_bytes, id, len,
293 lldp_port_id_subtype_str (subtype));
297 s = format (s, "%U", format_ascii_bytes, id, len);
300 case LLDP_PORT_ID_SUBTYPE_NAME (mac_addr):
301 if (ETHER_ADDR_LEN == len)
305 s = format (s, "%U(%s)", format_mac_address, id,
306 lldp_port_id_subtype_str (subtype));
310 s = format (s, "%U", format_mac_address, id);
315 case LLDP_PORT_ID_SUBTYPE_NAME (net_addr):
321 s = format (s, "%U(%s)", format_hex_bytes, id, len,
322 lldp_port_id_subtype_str (subtype));
326 s = format (s, "%U", format_hex_bytes, id, len);
334 * format chassis id subtype&value
336 * @param s format string
337 * @param va - 1st argument - unsigned - chassis id subtype
338 * @param va - 2nd argument - u8* - chassis id
339 * @param va - 3rd argument - unsigned - chassis id length
340 * @param va - 4th argument - int - 1 for detailed output, 0 for simple
343 format_lldp_chassis_id (u8 * s, va_list * va)
345 const lldp_chassis_id_subtype_t subtype =
346 va_arg (*va, lldp_chassis_id_subtype_t);
347 const u8 *id = va_arg (*va, u8 *);
348 const unsigned len = va_arg (*va, unsigned);
349 const int detail = va_arg (*va, int);
356 case LLDP_CHASS_ID_SUBTYPE_NAME (chassis_comp):
358 case LLDP_CHASS_ID_SUBTYPE_NAME (intf_alias):
360 case LLDP_CHASS_ID_SUBTYPE_NAME (port_comp):
362 case LLDP_PORT_ID_SUBTYPE_NAME (local):
364 case LLDP_CHASS_ID_SUBTYPE_NAME (intf_name):
367 s = format (s, "%U(%s)", format_ascii_bytes, id, len,
368 lldp_chassis_id_subtype_str (subtype));
372 s = format (s, "%U", format_ascii_bytes, id, len);
375 case LLDP_CHASS_ID_SUBTYPE_NAME (mac_addr):
376 if (ETHER_ADDR_LEN == len)
380 s = format (s, "%U(%s)", format_mac_address, id,
381 lldp_chassis_id_subtype_str (subtype));
385 s = format (s, "%U", format_mac_address, id);
390 case LLDP_CHASS_ID_SUBTYPE_NAME (net_addr):
395 s = format (s, "%U(%s)", format_hex_bytes, id, len,
396 lldp_chassis_id_subtype_str (subtype));
400 s = format (s, "%U", format_hex_bytes, id, len);
408 * convert a tlv code to human-readable string
411 lldp_tlv_code_str (lldp_tlv_code_t t)
418 foreach_lldp_tlv_type (F)
421 return "unknown lldp tlv";
425 * format a single LLDP TLV
427 * @param s format string
428 * @param va variable list - pointer to lldp_tlv_t is expected
431 format_lldp_tlv (u8 * s, va_list * va)
433 const lldp_tlv_t *tlv = va_arg (*va, lldp_tlv_t *);
438 u16 l = lldp_tlv_get_length (tlv);
439 switch (lldp_tlv_get_code (tlv))
441 case LLDP_TLV_NAME (chassis_id):
442 s = format (s, "%U", format_lldp_chassis_id,
443 ((lldp_chassis_id_tlv_t *) tlv)->subtype,
444 ((lldp_chassis_id_tlv_t *) tlv)->id,
445 l - STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype), 1);
447 case LLDP_TLV_NAME (port_id):
448 s = format (s, "%U", format_lldp_port_id,
449 ((lldp_port_id_tlv_t *) tlv)->subtype,
450 ((lldp_port_id_tlv_t *) tlv)->id,
451 l - STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype), 1);
453 case LLDP_TLV_NAME (ttl):
454 s = format (s, "%d", ntohs (((lldp_ttl_tlv_t *) tlv)->ttl));
456 case LLDP_TLV_NAME (sys_name):
458 case LLDP_TLV_NAME (sys_desc):
459 s = format (s, "%U", format_ascii_bytes, tlv->v, l);
462 s = format (s, "%U", format_hex_bytes, tlv->v, l);
469 format_time_ago (u8 * s, va_list * va)
471 f64 ago = va_arg (*va, double);
472 f64 now = va_arg (*va, double);
475 return format (s, "never");
477 return format (s, "%.1fs ago", now - ago);
481 format_lldp_intfs_detail (u8 * s, vlib_main_t * vm, const lldp_main_t * lm)
483 vnet_main_t *vnm = &vnet_main;
484 const lldp_intf_t *n;
485 const vnet_hw_interface_t *hw;
486 const vnet_sw_interface_t *sw;
487 s = format (s, "LLDP configuration:\n");
490 s = format (s, "Configured system name: %U\n", format_ascii_bytes,
491 lm->sys_name, vec_len (lm->sys_name));
493 s = format (s, "Configured tx-hold: %d\n", (int) lm->msg_tx_hold);
494 s = format (s, "Configured tx-interval: %d\n", (int) lm->msg_tx_interval);
495 s = format (s, "\nLLDP-enabled interface table:\n");
496 f64 now = vlib_time_now (vm);
501 hw = vnet_get_hw_interface(vnm, n->hw_if_index);
502 sw = vnet_get_sw_interface(lm->vnet_main, hw->sw_if_index);
503 /* Interface shutdown */
504 if (!(sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
506 s = format(s, "\nInterface name: %s\nInterface/peer state: "
507 "interface down\nLast packet sent: %U\n",
508 hw->name, format_time_ago, n->last_sent, now);
510 else if (now < n->last_heard + n->ttl)
513 "\nInterface name: %s\nPort Desc: %s\nInterface/peer "
514 "state: active\nPeer chassis ID: %U\nRemote port ID:"
515 " %U\nLast packet sent: %U\nLast packet received: %U\n",
516 hw->name, n->port_desc, format_lldp_chassis_id,
517 n->chassis_id_subtype, n->chassis_id,
518 vec_len(n->chassis_id), 1,
519 format_lldp_port_id, n->port_id_subtype, n->port_id,
520 vec_len(n->port_id), 1, format_time_ago, n->last_sent,
521 now, format_time_ago, n->last_heard, now);
526 "\nInterface name: %s\nPort Desc: %s\nInterface/peer "
527 "state: inactive(timeout)\nLast known peer chassis ID:"
528 "%U\nLast known peer port ID: %U\nLast packet sent: "
529 "%U\nLast packet received: %U\n",
530 hw->name, n->port_desc, format_lldp_chassis_id,
531 n->chassis_id_subtype, n->chassis_id,
532 vec_len(n->chassis_id), 1,
533 format_lldp_port_id, n->port_id_subtype, n->port_id,
534 vec_len(n->port_id), 1, format_time_ago, n->last_sent,
535 now, format_time_ago, n->last_heard, now);
543 format_lldp_intfs (u8 * s, va_list * va)
545 vlib_main_t *vm = va_arg (*va, vlib_main_t *);
546 const lldp_main_t *lm = va_arg (*va, lldp_main_t *);
547 const int detail = va_arg (*va, int);
548 vnet_main_t *vnm = &vnet_main;
549 const lldp_intf_t *n;
553 return format_lldp_intfs_detail (s, vm, lm);
556 f64 now = vlib_time_now (vm);
557 s = format (s, "%-25s %-25s %-25s %=15s %=15s %=10s\n", "Local interface",
558 "Peer chassis ID", "Remote port ID", "Last heard", "Last sent",
564 const vnet_hw_interface_t *hw =
565 vnet_get_hw_interface(vnm, n->hw_if_index);
566 const vnet_sw_interface_t *sw =
567 vnet_get_sw_interface(lm->vnet_main, hw->sw_if_index);
568 /* Interface shutdown */
569 if (!(sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
571 if (now < n->last_heard + n->ttl)
573 s = format(s, "%-25s %-25U %-25U %=15U %=15U %=10s\n", hw->name,
574 format_lldp_chassis_id, n->chassis_id_subtype,
575 n->chassis_id, vec_len(n->chassis_id), 0,
576 format_lldp_port_id, n->port_id_subtype, n->port_id,
577 vec_len(n->port_id), 0, format_time_ago, n->last_heard,
578 now, format_time_ago, n->last_sent, now, "active");
582 s = format(s, "%-25s %-25s %-25s %=15U %=15U %=10s\n", hw->name,
583 "", "", format_time_ago, n->last_heard, now,
584 format_time_ago, n->last_sent, now, "inactive");
591 static clib_error_t *
592 show_lldp (vlib_main_t * vm, unformat_input_t * input,
593 CLIB_UNUSED (vlib_cli_command_t * lmd))
595 lldp_main_t *lm = &lldp_main;
597 if (unformat (input, "detail"))
599 vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 1);
603 vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 0);
609 VLIB_CLI_COMMAND(show_lldp_command, static) = {
611 .short_help = "show lldp [detail]",
612 .function = show_lldp,
617 * packet trace format function, very similar to
618 * lldp_packet_scan except that we call the per TLV format
619 * functions instead of the per TLV processing functions
622 lldp_input_format_trace (u8 * s, va_list * args)
624 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
625 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
626 const lldp_input_trace_t *t = va_arg (*args, lldp_input_trace_t *);
628 const lldp_tlv_t *tlv;
630 while (((cur + lldp_tlv_get_length ((lldp_tlv_t *) cur)) <
633 tlv = (lldp_tlv_t *) cur;
636 s = format (s, "TLV #%d(%s): %U\n", lldp_tlv_get_code (tlv),
637 lldp_tlv_code_str (lldp_tlv_get_code (tlv)),
638 format_lldp_tlv, tlv);
642 s = format (s, " TLV #%d(%s): %U\n", lldp_tlv_get_code (tlv),
643 lldp_tlv_code_str (lldp_tlv_get_code (tlv)),
644 format_lldp_tlv, tlv);
646 cur += STRUCT_SIZE_OF (lldp_tlv_t, head) + lldp_tlv_get_length (tlv);
653 * fd.io coding-style-patch-verification: ON
656 * eval: (c-set-style "gnu")