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