blob: d6d84bfd1a2fd6c5422848d157259cf9bad1b334 [file] [log] [blame]
Klement Sekerade4582b2016-09-13 12:27:08 +02001/*
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:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
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.
14 */
15
16/**
17 * @file
18 * @brief LLDP CLI handling
19 *
20 */
21#include <vnet/lisp-cp/lisp_types.h>
22#include <vnet/lldp/lldp_node.h>
23
24#ifndef ETHER_ADDR_LEN
25#include <net/ethernet.h>
26#endif
27
28typedef enum lldp_cfg_err
29{
30 lldp_ok,
31 lldp_not_supported,
32 lldp_invalid_arg,
33} lldp_cfg_err_t;
34
35static clib_error_t *
36lldp_cfg_err_to_clib_err (lldp_cfg_err_t e)
37{
38
39 switch (e)
40 {
41 case lldp_ok:
42 return 0;
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");
47 }
48 return 0;
49}
50
51static lldp_cfg_err_t
52lldp_cfg_intf_set (u32 hw_if_index, int enable)
53{
54 lldp_main_t *lm = &lldp_main;
55 vnet_main_t *vnm = lm->vnet_main;
56 ethernet_main_t *em = &ethernet_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);
59
60 if (!eif)
61 {
62 return lldp_not_supported;
63 }
64
65 if (enable)
66 {
67 lldp_intf_t *n = lldp_get_intf (lm, hw_if_index);
68 if (n)
69 {
70 /* already enabled */
71 return 0;
72 }
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)
77 {
78 lldp_schedule_intf (lm, n);
79 }
80 }
81 else
82 {
83 lldp_intf_t *n = lldp_get_intf (lm, hi->sw_if_index);
84 lldp_delete_intf (lm, n);
85 }
86
87 return 0;
88}
89
90static clib_error_t *
91lldp_intf_cmd (vlib_main_t * vm, unformat_input_t * input,
92 vlib_cli_command_t * cmd)
93{
94 lldp_main_t *lm = &lldp_main;
95 vnet_main_t *vnm = lm->vnet_main;
96 u32 hw_if_index;
97 int enable = 0;
98
99 if (unformat (input, "%U %U", unformat_vnet_hw_interface, vnm, &hw_if_index,
100 unformat_vlib_enable_disable, &enable))
101 {
102 return
103 lldp_cfg_err_to_clib_err (lldp_cfg_intf_set (hw_if_index, enable));
104 }
105 else
106 {
107 return clib_error_return (0, "unknown input `%U'",
108 format_unformat_error, input);
109 }
110 return 0;
111}
112
113static lldp_cfg_err_t
114lldp_cfg_set (u8 ** host, int hold_time, int tx_interval)
115{
116 lldp_main_t *lm = &lldp_main;
117 int reschedule = 0;
118 if (host && *host)
119 {
120 vec_free (lm->sys_name);
121 lm->sys_name = *host;
122 *host = NULL;
123 }
124 if (hold_time)
125 {
126 if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD)
127 {
128 return lldp_invalid_arg;
129 }
130 if (lm->msg_tx_hold != hold_time)
131 {
132 lm->msg_tx_hold = hold_time;
133 reschedule = 1;
134 }
135 }
136 if (tx_interval)
137 {
138 if (tx_interval < LLDP_MIN_TX_INTERVAL ||
139 tx_interval > LLDP_MAX_TX_INTERVAL)
140 {
141 return lldp_invalid_arg;
142 }
143 if (lm->msg_tx_interval != tx_interval)
144 {
145 reschedule = 1;
146 lm->msg_tx_interval = tx_interval;
147 }
148 }
149 if (reschedule)
150 {
151 vlib_process_signal_event (lm->vlib_main, lm->lldp_process_node_index,
152 LLDP_EVENT_RESCHEDULE, 0);
153 }
154 return lldp_ok;
155}
156
157static clib_error_t *
158lldp_cfg_cmd (vlib_main_t * vm, unformat_input_t * input,
159 vlib_cli_command_t * cmd)
160{
161 int hold_time = 0;
162 int tx_interval = 0;
163 u8 *host = NULL;
164 clib_error_t *ret = NULL;
165
166 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
167 {
168 if (unformat (input, "system-name %s", &host))
169 {
170 }
171 else if (unformat (input, "tx-hold %d", &hold_time))
172 {
173 if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD)
174 {
175 ret =
176 clib_error_return (0,
177 "invalid tx-hold `%d' (out of range <%d,%d>)",
178 hold_time, LLDP_MIN_TX_HOLD,
179 LLDP_MAX_TX_HOLD);
180 goto out;
181 }
182 }
183 else if (unformat (input, "tx-interval %d", &tx_interval))
184 {
185 if (tx_interval < LLDP_MIN_TX_INTERVAL ||
186 tx_interval > LLDP_MAX_TX_INTERVAL)
187 {
188 ret =
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);
193 goto out;
194 }
195 }
196 else
197 {
Klement Sekera3d62a7f2017-06-28 13:35:30 +0200198 break;
Klement Sekerade4582b2016-09-13 12:27:08 +0200199 }
200 }
201 ret =
202 lldp_cfg_err_to_clib_err (lldp_cfg_set (&host, hold_time, tx_interval));
203out:
204 vec_free (host);
205 return ret;
206}
207
208/* *INDENT-OFF* */
209VLIB_CLI_COMMAND(set_interface_lldp_cmd, static) = {
210 .path = "set interface lldp",
211 .short_help = "set interface lldp <interface> (enable | disable) ",
212 .function = lldp_intf_cmd,
213};
214
215VLIB_CLI_COMMAND(set_lldp_cmd, static) = {
216 .path = "set lldp",
217 .short_help = "set lldp [system-name <string>] [tx-hold <value>] "
218 "[tx-interval <value>]",
219 .function = lldp_cfg_cmd,
220};
221/* *INDENT-ON* */
222
223static const char *
224lldp_chassis_id_subtype_str (lldp_chassis_id_subtype_t t)
225{
226 switch (t)
227 {
228#define F(num, val, str) \
229 case num: \
230 return str;
231 foreach_chassis_id_subtype (F)
232#undef F
233 }
234 return "unknown chassis subtype";
235}
236
237static const char *
238lldp_port_id_subtype_str (lldp_port_id_subtype_t t)
239{
240 switch (t)
241 {
242#define F(num, val, str) \
243 case num: \
244 return str;
245 foreach_port_id_subtype (F)
246#undef F
247 }
248 return "unknown port subtype";
249}
250
251/*
252 * format port id subtype&value
253 *
254 * @param va - 1st argument - unsigned - port id subtype
255 * @param va - 2nd argument - u8* - port id
256 * @param va - 3rd argument - unsigned - port id length
257 * @param va - 4th argument - int - 1 for detailed output, 0 for simple
258 */
259u8 *
260format_lldp_port_id (u8 * s, va_list * va)
261{
262 const lldp_port_id_subtype_t subtype = va_arg (*va, unsigned);
263 const u8 *id = va_arg (*va, u8 *);
264 const unsigned len = va_arg (*va, unsigned);
265 const int detail = va_arg (*va, int);
266 if (!id)
267 {
268 return s;
269 }
270 switch (subtype)
271 {
272 case LLDP_PORT_ID_SUBTYPE_NAME (intf_alias):
273 /* fallthrough */
274 case LLDP_PORT_ID_SUBTYPE_NAME (port_comp):
275 /* fallthrough */
276 case LLDP_PORT_ID_SUBTYPE_NAME (local):
277 /* fallthrough */
278 case LLDP_PORT_ID_SUBTYPE_NAME (intf_name):
279 if (detail)
280 {
281 s = format (s, "%U(%s)", format_ascii_bytes, id, len,
282 lldp_port_id_subtype_str (subtype));
283 }
284 else
285 {
286 s = format (s, "%U", format_ascii_bytes, id, len);
287 }
288 break;
289 case LLDP_PORT_ID_SUBTYPE_NAME (mac_addr):
290 if (ETHER_ADDR_LEN == len)
291 {
292 if (detail)
293 {
294 s = format (s, "%U(%s)", format_mac_address, id,
295 lldp_port_id_subtype_str (subtype));
296 }
297 else
298 {
299 s = format (s, "%U", format_mac_address, id);
300 }
301 break;
302 }
303 /* fallthrough */
304 case LLDP_PORT_ID_SUBTYPE_NAME (net_addr):
305 /* TODO */
306 /* fallthrough */
307 default:
308 if (detail)
309 {
310 s = format (s, "%U(%s)", format_hex_bytes, id, len,
311 lldp_port_id_subtype_str (subtype));
312 }
313 else
314 {
315 s = format (s, "%U", format_hex_bytes, id, len);
316 }
317 break;
318 }
319 return s;
320}
321
322/*
323 * format chassis id subtype&value
324 *
325 * @param s format string
326 * @param va - 1st argument - unsigned - chassis id subtype
327 * @param va - 2nd argument - u8* - chassis id
328 * @param va - 3rd argument - unsigned - chassis id length
329 * @param va - 4th argument - int - 1 for detailed output, 0 for simple
330 */
331u8 *
332format_lldp_chassis_id (u8 * s, va_list * va)
333{
334 const lldp_chassis_id_subtype_t subtype =
335 va_arg (*va, lldp_chassis_id_subtype_t);
336 const u8 *id = va_arg (*va, u8 *);
337 const unsigned len = va_arg (*va, unsigned);
338 const int detail = va_arg (*va, int);
339 if (!id)
340 {
341 return s;
342 }
343 switch (subtype)
344 {
345 case LLDP_CHASS_ID_SUBTYPE_NAME (chassis_comp):
346 /* fallthrough */
347 case LLDP_CHASS_ID_SUBTYPE_NAME (intf_alias):
348 /* fallthrough */
349 case LLDP_CHASS_ID_SUBTYPE_NAME (port_comp):
350 /* fallthrough */
351 case LLDP_PORT_ID_SUBTYPE_NAME (local):
352 /* fallthrough */
353 case LLDP_CHASS_ID_SUBTYPE_NAME (intf_name):
354 if (detail)
355 {
356 s = format (s, "%U(%s)", format_ascii_bytes, id, len,
357 lldp_chassis_id_subtype_str (subtype));
358 }
359 else
360 {
361 s = format (s, "%U", format_ascii_bytes, id, len);
362 }
363 break;
364 case LLDP_CHASS_ID_SUBTYPE_NAME (mac_addr):
365 if (ETHER_ADDR_LEN == len)
366 {
367 if (detail)
368 {
369 s = format (s, "%U(%s)", format_mac_address, id,
370 lldp_chassis_id_subtype_str (subtype));
371 }
372 else
373 {
374 s = format (s, "%U", format_mac_address, id);
375 }
376 break;
377 }
378 /* fallthrough */
379 case LLDP_CHASS_ID_SUBTYPE_NAME (net_addr):
380 /* TODO */
381 default:
382 if (detail)
383 {
384 s = format (s, "%U(%s)", format_hex_bytes, id, len,
385 lldp_chassis_id_subtype_str (subtype));
386 }
387 else
388 {
389 s = format (s, "%U", format_hex_bytes, id, len);
390 }
391 break;
392 }
393 return s;
394}
395
396/*
397 * convert a tlv code to human-readable string
398 */
399static const char *
400lldp_tlv_code_str (lldp_tlv_code_t t)
401{
402 switch (t)
403 {
404#define F(n, t, s) \
405 case n: \
406 return s;
407 foreach_lldp_tlv_type (F)
408#undef F
409 }
410 return "unknown lldp tlv";
411}
412
413/*
414 * format a single LLDP TLV
415 *
416 * @param s format string
417 * @param va variable list - pointer to lldp_tlv_t is expected
418 */
419u8 *
420format_lldp_tlv (u8 * s, va_list * va)
421{
422 const lldp_tlv_t *tlv = va_arg (*va, lldp_tlv_t *);
423 if (!tlv)
424 {
425 return s;
426 }
427 u16 l = lldp_tlv_get_length (tlv);
428 switch (lldp_tlv_get_code (tlv))
429 {
430 case LLDP_TLV_NAME (chassis_id):
431 s = format (s, "%U", format_lldp_chassis_id,
432 ((lldp_chassis_id_tlv_t *) tlv)->subtype,
433 ((lldp_chassis_id_tlv_t *) tlv)->id,
434 l - STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype), 1);
435 break;
436 case LLDP_TLV_NAME (port_id):
437 s = format (s, "%U", format_lldp_port_id,
438 ((lldp_port_id_tlv_t *) tlv)->subtype,
439 ((lldp_port_id_tlv_t *) tlv)->id,
440 l - STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype), 1);
441 break;
442 case LLDP_TLV_NAME (ttl):
443 s = format (s, "%d", ntohs (((lldp_ttl_tlv_t *) tlv)->ttl));
444 break;
445 case LLDP_TLV_NAME (sys_name):
446 /* fallthrough */
447 case LLDP_TLV_NAME (sys_desc):
448 s = format (s, "%U", format_ascii_bytes, tlv->v, l);
449 break;
450 default:
451 s = format (s, "%U", format_hex_bytes, tlv->v, l);
452 }
453
454 return s;
455}
456
457static u8 *
458format_time_ago (u8 * s, va_list * va)
459{
460 f64 ago = va_arg (*va, double);
461 f64 now = va_arg (*va, double);
462 if (ago < 0.01)
463 {
464 return format (s, "never");
465 }
466 return format (s, "%.1fs ago", now - ago);
467}
468
469static u8 *
470format_lldp_intfs_detail (u8 * s, vlib_main_t * vm, const lldp_main_t * lm)
471{
472 vnet_main_t *vnm = &vnet_main;
473 const lldp_intf_t *n;
474 const vnet_hw_interface_t *hw;
475 const vnet_sw_interface_t *sw;
476 s = format (s, "LLDP configuration:\n");
477 if (lm->sys_name)
478 {
479 s = format (s, "Configured system name: %U\n", format_ascii_bytes,
480 lm->sys_name, vec_len (lm->sys_name));
481 }
482 s = format (s, "Configured tx-hold: %d\n", (int) lm->msg_tx_hold);
483 s = format (s, "Configured tx-interval: %d\n", (int) lm->msg_tx_interval);
484 s = format (s, "\nLLDP-enabled interface table:\n");
485 f64 now = vlib_time_now (vm);
486
487 /* *INDENT-OFF* */
488 pool_foreach(
489 n, lm->intfs, ({
490 hw = vnet_get_hw_interface(vnm, n->hw_if_index);
491 sw = vnet_get_sw_interface(lm->vnet_main, hw->sw_if_index);
492 /* Interface shutdown */
493 if (!(sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
494 {
495 s = format(s, "\nInterface name: %s\nInterface/peer state: "
496 "interface down\nLast packet sent: %U\n",
497 hw->name, format_time_ago, n->last_sent, now);
498 }
499 else if (now < n->last_heard + n->ttl)
500 {
501 s = format(s,
502 "\nInterface name: %s\nInterface/peer state: "
503 "active\nPeer chassis ID: %U\nRemote port ID: %U\nLast "
504 "packet sent: %U\nLast packet received: %U\n",
505 hw->name, format_lldp_chassis_id, n->chassis_id_subtype,
506 n->chassis_id, vec_len(n->chassis_id), 1,
507 format_lldp_port_id, n->port_id_subtype, n->port_id,
508 vec_len(n->port_id), 1, format_time_ago, n->last_sent,
509 now, format_time_ago, n->last_heard, now);
510 }
511 else
512 {
513 s = format(s, "\nInterface name: %s\nInterface/peer state: "
514 "inactive(timeout)\nLast known peer chassis ID: "
515 "%U\nLast known peer port ID: %U\nLast packet sent: "
516 "%U\nLast packet received: %U\n",
517 hw->name, format_lldp_chassis_id, n->chassis_id_subtype,
518 n->chassis_id, 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);
522 }
523 }));
524 /* *INDENT-ON* */
525 return s;
526}
527
528static u8 *
529format_lldp_intfs (u8 * s, va_list * va)
530{
531 vlib_main_t *vm = va_arg (*va, vlib_main_t *);
532 const lldp_main_t *lm = va_arg (*va, lldp_main_t *);
533 const int detail = va_arg (*va, int);
534 vnet_main_t *vnm = &vnet_main;
535 const lldp_intf_t *n;
536
537 if (detail)
538 {
539 return format_lldp_intfs_detail (s, vm, lm);
540 }
541
542 f64 now = vlib_time_now (vm);
543 s = format (s, "%-25s %-25s %-25s %=15s %=15s %=10s\n", "Local interface",
544 "Peer chassis ID", "Remote port ID", "Last heard", "Last sent",
545 "Status");
546
547 /* *INDENT-OFF* */
548 pool_foreach(
549 n, lm->intfs, ({
550 const vnet_hw_interface_t *hw =
551 vnet_get_hw_interface(vnm, n->hw_if_index);
552 const vnet_sw_interface_t *sw =
553 vnet_get_sw_interface(lm->vnet_main, hw->sw_if_index);
554 /* Interface shutdown */
555 if (!(sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
556 continue;
557 if (now < n->last_heard + n->ttl)
558 {
559 s = format(s, "%-25s %-25U %-25U %=15U %=15U %=10s\n", hw->name,
560 format_lldp_chassis_id, n->chassis_id_subtype,
561 n->chassis_id, vec_len(n->chassis_id), 0,
562 format_lldp_port_id, n->port_id_subtype, n->port_id,
563 vec_len(n->port_id), 0, format_time_ago, n->last_heard,
564 now, format_time_ago, n->last_sent, now, "active");
565 }
566 else
567 {
568 s = format(s, "%-25s %-25s %-25s %=15U %=15U %=10s\n", hw->name,
569 "", "", format_time_ago, n->last_heard, now,
570 format_time_ago, n->last_sent, now, "inactive");
571 }
572 }));
573 /* *INDENT-ON* */
574 return s;
575}
576
577static clib_error_t *
578show_lldp (vlib_main_t * vm, unformat_input_t * input,
579 CLIB_UNUSED (vlib_cli_command_t * lmd))
580{
581 lldp_main_t *lm = &lldp_main;
582
583 if (unformat (input, "detail"))
584 {
585 vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 1);
586 }
587 else
588 {
589 vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 0);
590 }
591 return 0;
592}
593
594/* *INDENT-OFF* */
595VLIB_CLI_COMMAND(show_lldp_command, static) = {
596 .path = "show lldp",
597 .short_help = "show lldp [detail]",
598 .function = show_lldp,
599};
600/* *INDENT-ON* */
601
602/*
603 * packet trace format function, very similar to
604 * lldp_packet_scan except that we call the per TLV format
605 * functions instead of the per TLV processing functions
606 */
607u8 *
608lldp_input_format_trace (u8 * s, va_list * args)
609{
610 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
611 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
612 const lldp_input_trace_t *t = va_arg (*args, lldp_input_trace_t *);
613 const u8 *cur;
614 const lldp_tlv_t *tlv;
615 cur = t->data;
616 while (((cur + lldp_tlv_get_length ((lldp_tlv_t *) cur)) <
617 t->data + t->len))
618 {
619 tlv = (lldp_tlv_t *) cur;
620 if (cur == t->data)
621 {
622 s = format (s, "TLV #%d(%s): %U\n", lldp_tlv_get_code (tlv),
623 lldp_tlv_code_str (lldp_tlv_get_code (tlv)),
624 format_lldp_tlv, tlv);
625 }
626 else
627 {
628 s = format (s, " TLV #%d(%s): %U\n", lldp_tlv_get_code (tlv),
629 lldp_tlv_code_str (lldp_tlv_get_code (tlv)),
630 format_lldp_tlv, tlv);
631 }
632 cur += STRUCT_SIZE_OF (lldp_tlv_t, head) + lldp_tlv_get_length (tlv);
633 }
634
635 return s;
636}
637
638/*
639 * fd.io coding-style-patch-verification: ON
640 *
641 * Local Variables:
642 * eval: (c-set-style "gnu")
643 * End:
644 */