Add API support for LLDP config/interface set
[vpp.git] / src / vnet / lldp / lldp_cli.c
1 /*
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.h>
23 #include <vnet/lldp/lldp_node.h>
24
25 #ifndef ETHER_ADDR_LEN
26 #include <net/ethernet.h>
27 #endif
28
29 static clib_error_t *
30 lldp_cfg_err_to_clib_err (lldp_cfg_err_t e)
31 {
32
33   switch (e)
34     {
35     case lldp_ok:
36       return 0;
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");
41     }
42   return 0;
43 }
44
45 lldp_cfg_err_t
46 lldp_cfg_intf_set (u32 hw_if_index, u8 ** port_desc, int enable)
47 {
48   lldp_main_t *lm = &lldp_main;
49   vnet_main_t *vnm = lm->vnet_main;
50   ethernet_main_t *em = &ethernet_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);
53
54   if (!eif)
55     {
56       return lldp_not_supported;
57     }
58
59   if (enable)
60     {
61       lldp_intf_t *n = lldp_get_intf (lm, hw_if_index);
62       if (n)
63         {
64           /* already enabled */
65           return lldp_ok;
66         }
67       n = lldp_create_intf (lm, hw_if_index);
68
69       if (port_desc && *port_desc)
70         {
71           n->port_desc = *port_desc;
72           *port_desc = NULL;
73         }
74
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)
78         {
79           lldp_schedule_intf (lm, n);
80         }
81     }
82   else
83     {
84       lldp_intf_t *n = lldp_get_intf (lm, hi->sw_if_index);
85       lldp_delete_intf (lm, n);
86     }
87
88   return lldp_ok;
89 }
90
91 static clib_error_t *
92 lldp_intf_cmd (vlib_main_t * vm, unformat_input_t * input,
93                vlib_cli_command_t * cmd)
94 {
95   lldp_main_t *lm = &lldp_main;
96   vnet_main_t *vnm = lm->vnet_main;
97   u32 sw_if_index = (u32) ~ 0;
98   int enable = 1;
99   u8 *port_desc = NULL;
100
101   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
102     {
103       if (unformat (input, "sw_if_index %d", &sw_if_index))
104         ;
105       if (unformat
106           (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
107         ;
108       else if (unformat (input, "disable"))
109         enable = 0;
110       else if (unformat (input, "port-desc %s", &port_desc))
111         ;
112       else
113         break;
114     }
115
116   if (sw_if_index == (u32) ~ 0)
117     return clib_error_return (0, "Interface name is invalid!");
118
119   return lldp_cfg_err_to_clib_err (lldp_cfg_intf_set (sw_if_index,
120                                                       &port_desc, enable));
121 }
122
123 lldp_cfg_err_t
124 lldp_cfg_set (u8 ** host, int hold_time, int tx_interval)
125 {
126   lldp_main_t *lm = &lldp_main;
127   int reschedule = 0;
128   if (host && *host)
129     {
130       vec_free (lm->sys_name);
131       lm->sys_name = *host;
132       *host = NULL;
133     }
134   if (hold_time)
135     {
136       if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD)
137         {
138           return lldp_invalid_arg;
139         }
140       if (lm->msg_tx_hold != hold_time)
141         {
142           lm->msg_tx_hold = hold_time;
143           reschedule = 1;
144         }
145     }
146   if (tx_interval)
147     {
148       if (tx_interval < LLDP_MIN_TX_INTERVAL ||
149           tx_interval > LLDP_MAX_TX_INTERVAL)
150         {
151           return lldp_invalid_arg;
152         }
153       if (lm->msg_tx_interval != tx_interval)
154         {
155           reschedule = 1;
156           lm->msg_tx_interval = tx_interval;
157         }
158     }
159   if (reschedule)
160     {
161       vlib_process_signal_event (lm->vlib_main, lm->lldp_process_node_index,
162                                  LLDP_EVENT_RESCHEDULE, 0);
163     }
164   return lldp_ok;
165 }
166
167 static clib_error_t *
168 lldp_cfg_cmd (vlib_main_t * vm, unformat_input_t * input,
169               vlib_cli_command_t * cmd)
170 {
171   int hold_time = 0;
172   int tx_interval = 0;
173   u8 *host = NULL;
174   clib_error_t *ret = NULL;
175
176   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
177     {
178       if (unformat (input, "system-name %s", &host))
179         {
180         }
181       else if (unformat (input, "tx-hold %d", &hold_time))
182         {
183           if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD)
184             {
185               ret =
186                 clib_error_return (0,
187                                    "invalid tx-hold `%d' (out of range <%d,%d>)",
188                                    hold_time, LLDP_MIN_TX_HOLD,
189                                    LLDP_MAX_TX_HOLD);
190               goto out;
191             }
192         }
193       else if (unformat (input, "tx-interval %d", &tx_interval))
194         {
195           if (tx_interval < LLDP_MIN_TX_INTERVAL ||
196               tx_interval > LLDP_MAX_TX_INTERVAL)
197             {
198               ret =
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);
203               goto out;
204             }
205         }
206       else
207         {
208           break;
209         }
210     }
211   ret =
212     lldp_cfg_err_to_clib_err (lldp_cfg_set (&host, hold_time, tx_interval));
213 out:
214   vec_free (host);
215   return ret;
216 }
217
218 /* *INDENT-OFF* */
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,
224 };
225
226 VLIB_CLI_COMMAND(set_lldp_cmd, static) = {
227   .path = "set lldp",
228   .short_help = "set lldp [system-name <string>] [tx-hold <value>] "
229                 "[tx-interval <value>]",
230   .function = lldp_cfg_cmd,
231 };
232 /* *INDENT-ON* */
233
234 static const char *
235 lldp_chassis_id_subtype_str (lldp_chassis_id_subtype_t t)
236 {
237   switch (t)
238     {
239 #define F(num, val, str) \
240   case num:              \
241     return str;
242       foreach_chassis_id_subtype (F)
243 #undef F
244     }
245   return "unknown chassis subtype";
246 }
247
248 static const char *
249 lldp_port_id_subtype_str (lldp_port_id_subtype_t t)
250 {
251   switch (t)
252     {
253 #define F(num, val, str) \
254   case num:              \
255     return str;
256       foreach_port_id_subtype (F)
257 #undef F
258     }
259   return "unknown port subtype";
260 }
261
262 /*
263  * format port id subtype&value
264  *
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
269  */
270 u8 *
271 format_lldp_port_id (u8 * s, va_list * va)
272 {
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);
277   if (!id)
278     {
279       return s;
280     }
281   switch (subtype)
282     {
283     case LLDP_PORT_ID_SUBTYPE_NAME (intf_alias):
284       /* fallthrough */
285     case LLDP_PORT_ID_SUBTYPE_NAME (port_comp):
286       /* fallthrough */
287     case LLDP_PORT_ID_SUBTYPE_NAME (local):
288       /* fallthrough */
289     case LLDP_PORT_ID_SUBTYPE_NAME (intf_name):
290       if (detail)
291         {
292           s = format (s, "%U(%s)", format_ascii_bytes, id, len,
293                       lldp_port_id_subtype_str (subtype));
294         }
295       else
296         {
297           s = format (s, "%U", format_ascii_bytes, id, len);
298         }
299       break;
300     case LLDP_PORT_ID_SUBTYPE_NAME (mac_addr):
301       if (ETHER_ADDR_LEN == len)
302         {
303           if (detail)
304             {
305               s = format (s, "%U(%s)", format_mac_address, id,
306                           lldp_port_id_subtype_str (subtype));
307             }
308           else
309             {
310               s = format (s, "%U", format_mac_address, id);
311             }
312           break;
313         }
314       /* fallthrough */
315     case LLDP_PORT_ID_SUBTYPE_NAME (net_addr):
316       /* TODO */
317       /* fallthrough */
318     default:
319       if (detail)
320         {
321           s = format (s, "%U(%s)", format_hex_bytes, id, len,
322                       lldp_port_id_subtype_str (subtype));
323         }
324       else
325         {
326           s = format (s, "%U", format_hex_bytes, id, len);
327         }
328       break;
329     }
330   return s;
331 }
332
333 /*
334  * format chassis id subtype&value
335  *
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
341  */
342 u8 *
343 format_lldp_chassis_id (u8 * s, va_list * va)
344 {
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);
350   if (!id)
351     {
352       return s;
353     }
354   switch (subtype)
355     {
356     case LLDP_CHASS_ID_SUBTYPE_NAME (chassis_comp):
357       /* fallthrough */
358     case LLDP_CHASS_ID_SUBTYPE_NAME (intf_alias):
359       /* fallthrough */
360     case LLDP_CHASS_ID_SUBTYPE_NAME (port_comp):
361       /* fallthrough */
362     case LLDP_PORT_ID_SUBTYPE_NAME (local):
363       /* fallthrough */
364     case LLDP_CHASS_ID_SUBTYPE_NAME (intf_name):
365       if (detail)
366         {
367           s = format (s, "%U(%s)", format_ascii_bytes, id, len,
368                       lldp_chassis_id_subtype_str (subtype));
369         }
370       else
371         {
372           s = format (s, "%U", format_ascii_bytes, id, len);
373         }
374       break;
375     case LLDP_CHASS_ID_SUBTYPE_NAME (mac_addr):
376       if (ETHER_ADDR_LEN == len)
377         {
378           if (detail)
379             {
380               s = format (s, "%U(%s)", format_mac_address, id,
381                           lldp_chassis_id_subtype_str (subtype));
382             }
383           else
384             {
385               s = format (s, "%U", format_mac_address, id);
386             }
387           break;
388         }
389       /* fallthrough */
390     case LLDP_CHASS_ID_SUBTYPE_NAME (net_addr):
391       /* TODO */
392     default:
393       if (detail)
394         {
395           s = format (s, "%U(%s)", format_hex_bytes, id, len,
396                       lldp_chassis_id_subtype_str (subtype));
397         }
398       else
399         {
400           s = format (s, "%U", format_hex_bytes, id, len);
401         }
402       break;
403     }
404   return s;
405 }
406
407 /*
408  * convert a tlv code to human-readable string
409  */
410 static const char *
411 lldp_tlv_code_str (lldp_tlv_code_t t)
412 {
413   switch (t)
414     {
415 #define F(n, t, s) \
416   case n:          \
417     return s;
418       foreach_lldp_tlv_type (F)
419 #undef F
420     }
421   return "unknown lldp tlv";
422 }
423
424 /*
425  * format a single LLDP TLV
426  *
427  * @param s format string
428  * @param va variable list - pointer to lldp_tlv_t is expected
429  */
430 u8 *
431 format_lldp_tlv (u8 * s, va_list * va)
432 {
433   const lldp_tlv_t *tlv = va_arg (*va, lldp_tlv_t *);
434   if (!tlv)
435     {
436       return s;
437     }
438   u16 l = lldp_tlv_get_length (tlv);
439   switch (lldp_tlv_get_code (tlv))
440     {
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);
446       break;
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);
452       break;
453     case LLDP_TLV_NAME (ttl):
454       s = format (s, "%d", ntohs (((lldp_ttl_tlv_t *) tlv)->ttl));
455       break;
456     case LLDP_TLV_NAME (sys_name):
457       /* fallthrough */
458     case LLDP_TLV_NAME (sys_desc):
459       s = format (s, "%U", format_ascii_bytes, tlv->v, l);
460       break;
461     default:
462       s = format (s, "%U", format_hex_bytes, tlv->v, l);
463     }
464
465   return s;
466 }
467
468 static u8 *
469 format_time_ago (u8 * s, va_list * va)
470 {
471   f64 ago = va_arg (*va, double);
472   f64 now = va_arg (*va, double);
473   if (ago < 0.01)
474     {
475       return format (s, "never");
476     }
477   return format (s, "%.1fs ago", now - ago);
478 }
479
480 static u8 *
481 format_lldp_intfs_detail (u8 * s, vlib_main_t * vm, const lldp_main_t * lm)
482 {
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");
488   if (lm->sys_name)
489     {
490       s = format (s, "Configured system name: %U\n", format_ascii_bytes,
491                   lm->sys_name, vec_len (lm->sys_name));
492     }
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);
497
498   /* *INDENT-OFF* */
499   pool_foreach(
500       n, lm->intfs, ({
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))
505           {
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);
509           }
510         else if (now < n->last_heard + n->ttl)
511           {
512             s = format(s,
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);
522           }
523         else
524           {
525             s = format(s,
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);
536           }
537       }));
538   /* *INDENT-ON* */
539   return s;
540 }
541
542 static u8 *
543 format_lldp_intfs (u8 * s, va_list * va)
544 {
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;
550
551   if (detail)
552     {
553       return format_lldp_intfs_detail (s, vm, lm);
554     }
555
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",
559               "Status");
560
561   /* *INDENT-OFF* */
562   pool_foreach(
563       n, lm->intfs, ({
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))
570           continue;
571         if (now < n->last_heard + n->ttl)
572           {
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");
579           }
580         else
581           {
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");
585           }
586       }));
587   /* *INDENT-ON* */
588   return s;
589 }
590
591 static clib_error_t *
592 show_lldp (vlib_main_t * vm, unformat_input_t * input,
593            CLIB_UNUSED (vlib_cli_command_t * lmd))
594 {
595   lldp_main_t *lm = &lldp_main;
596
597   if (unformat (input, "detail"))
598     {
599       vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 1);
600     }
601   else
602     {
603       vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 0);
604     }
605   return 0;
606 }
607
608 /* *INDENT-OFF* */
609 VLIB_CLI_COMMAND(show_lldp_command, static) = {
610   .path = "show lldp",
611   .short_help = "show lldp [detail]",
612   .function = show_lldp,
613 };
614 /* *INDENT-ON* */
615
616 /*
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
620  */
621 u8 *
622 lldp_input_format_trace (u8 * s, va_list * args)
623 {
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 *);
627   const u8 *cur;
628   const lldp_tlv_t *tlv;
629   cur = t->data;
630   while (((cur + lldp_tlv_get_length ((lldp_tlv_t *) cur)) <
631           t->data + t->len))
632     {
633       tlv = (lldp_tlv_t *) cur;
634       if (cur == t->data)
635         {
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);
639         }
640       else
641         {
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);
645         }
646       cur += STRUCT_SIZE_OF (lldp_tlv_t, head) + lldp_tlv_get_length (tlv);
647     }
648
649   return s;
650 }
651
652 /*
653  * fd.io coding-style-patch-verification: ON
654  *
655  * Local Variables:
656  * eval: (c-set-style "gnu")
657  * End:
658  */