LLDP: properly parse lldp cmds from startup config
[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_node.h>
23
24 #ifndef ETHER_ADDR_LEN
25 #include <net/ethernet.h>
26 #endif
27
28 typedef enum lldp_cfg_err
29 {
30   lldp_ok,
31   lldp_not_supported,
32   lldp_invalid_arg,
33 } lldp_cfg_err_t;
34
35 static clib_error_t *
36 lldp_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
51 static lldp_cfg_err_t
52 lldp_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
90 static clib_error_t *
91 lldp_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
113 static lldp_cfg_err_t
114 lldp_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
157 static clib_error_t *
158 lldp_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         {
198           break;
199         }
200     }
201   ret =
202     lldp_cfg_err_to_clib_err (lldp_cfg_set (&host, hold_time, tx_interval));
203 out:
204   vec_free (host);
205   return ret;
206 }
207
208 /* *INDENT-OFF* */
209 VLIB_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
215 VLIB_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
223 static const char *
224 lldp_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
237 static const char *
238 lldp_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  */
259 u8 *
260 format_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  */
331 u8 *
332 format_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  */
399 static const char *
400 lldp_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  */
419 u8 *
420 format_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
457 static u8 *
458 format_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
469 static u8 *
470 format_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
528 static u8 *
529 format_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
577 static clib_error_t *
578 show_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* */
595 VLIB_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  */
607 u8 *
608 lldp_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  */