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