misc: move to new pool_foreach macros
[vpp.git] / src / plugins / 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/ethernet/ethernet.h>
22 #include <vnet/ip/ip.h>
23 #include <lldp/lldp.h>
24 #include <lldp/lldp_node.h>
25
26 #ifndef ETHER_ADDR_LEN
27 #include <net/ethernet.h>
28 #endif
29
30 static clib_error_t *
31 lldp_cfg_err_to_clib_err (lldp_cfg_err_t e)
32 {
33
34   switch (e)
35     {
36     case lldp_ok:
37       return 0;
38     case lldp_not_supported:
39       return clib_error_return (0, "not supported");
40     case lldp_invalid_arg:
41       return clib_error_return (0, "invalid argument");
42     case lldp_internal_error:
43       return clib_error_return (0, "internal error");
44     }
45   return 0;
46 }
47
48 lldp_cfg_err_t
49 lldp_cfg_intf_set (u32 hw_if_index, u8 ** port_desc, u8 ** mgmt_ip4,
50                    u8 ** mgmt_ip6, u8 ** mgmt_oid, int enable)
51 {
52   clib_error_t *error = 0;
53   lldp_main_t *lm = &lldp_main;
54   vnet_main_t *vnm = lm->vnet_main;
55   ethernet_main_t *em = &ethernet_main;
56   const vnet_hw_interface_t *hi;
57   const ethernet_interface_t *eif;
58
59   if (pool_is_free_index (vnm->interface_main.hw_interfaces, hw_if_index))
60     {
61       return lldp_invalid_arg;
62     }
63
64   hi = vnet_get_hw_interface (vnm, hw_if_index);
65   eif = ethernet_get_interface (em, hw_if_index);
66   if (!eif)
67     {
68       return lldp_not_supported;
69     }
70
71   if (enable)
72     {
73       lldp_intf_t *n = lldp_get_intf (lm, hw_if_index);
74       if (n)
75         {
76           /* already enabled */
77           return lldp_ok;
78         }
79       n = lldp_create_intf (lm, hw_if_index);
80
81       if (port_desc && *port_desc)
82         {
83           n->port_desc = *port_desc;
84           *port_desc = NULL;
85         }
86
87       if (mgmt_ip4 && *mgmt_ip4)
88         {
89           n->mgmt_ip4 = *mgmt_ip4;
90           *mgmt_ip4 = NULL;
91         }
92
93       if (mgmt_ip6 && *mgmt_ip6)
94         {
95           n->mgmt_ip6 = *mgmt_ip6;
96           *mgmt_ip6 = NULL;
97         }
98
99       if (mgmt_oid && *mgmt_oid)
100         {
101           n->mgmt_oid = *mgmt_oid;
102           *mgmt_oid = NULL;
103         }
104
105       /* Add MAC address to an interface's filter */
106       if (hi->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_MAC_FILTER)
107         {
108           error =
109             vnet_hw_interface_add_del_mac_address (lm->vnet_main,
110                                                    hw_if_index,
111                                                    lldp_mac_addr,
112                                                    1 /* is_add */ );
113           if (error)
114             {
115               clib_error_free (error);
116               lldp_delete_intf (lm, n);
117               return lldp_internal_error;
118             }
119         }
120
121       const vnet_sw_interface_t *sw =
122         vnet_get_sw_interface (lm->vnet_main, hi->sw_if_index);
123       if (sw->flags & (VNET_SW_INTERFACE_FLAG_ADMIN_UP))
124         {
125           lldp_schedule_intf (lm, n);
126         }
127     }
128   else
129     {
130       lldp_intf_t *n = lldp_get_intf (lm, hi->sw_if_index);
131       lldp_delete_intf (lm, n);
132       /* Remove MAC address from the interface's filter */
133       if ((n) && (hi->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_MAC_FILTER))
134         {
135           error =
136             vnet_hw_interface_add_del_mac_address (lm->vnet_main,
137                                                    hw_if_index,
138                                                    lldp_mac_addr,
139                                                    0 /* is_add */ );
140           if (error)
141             {
142               clib_error_free (error);
143             }
144         }
145     }
146
147   return lldp_ok;
148 }
149
150 static clib_error_t *
151 lldp_intf_cmd (vlib_main_t * vm, unformat_input_t * input,
152                vlib_cli_command_t * cmd)
153 {
154   lldp_main_t *lm = &lldp_main;
155   vnet_main_t *vnm = lm->vnet_main;
156   u32 sw_if_index = (u32) ~ 0;
157   int enable = 1;
158   u8 *port_desc = NULL;
159   u8 *mgmt_ip4 = NULL, *mgmt_ip6 = NULL, *mgmt_oid = NULL;
160   ip4_address_t ip4_addr;
161   ip6_address_t ip6_addr;
162
163   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
164     {
165       if (unformat (input, "sw_if_index %d", &sw_if_index))
166         ;
167       if (unformat
168           (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
169         ;
170       else if (unformat (input, "disable"))
171         enable = 0;
172       else if (unformat (input, "port-desc %s", &port_desc))
173         ;
174       else
175         if (unformat (input, "mgmt-ip4 %U", unformat_ip4_address, &ip4_addr))
176         {
177           vec_validate (mgmt_ip4, sizeof (ip4_address_t) - 1);
178           clib_memcpy (mgmt_ip4, &ip4_addr, vec_len (mgmt_ip4));
179         }
180       else
181         if (unformat (input, "mgmt-ip6 %U", unformat_ip6_address, &ip6_addr))
182         {
183           vec_validate (mgmt_ip6, sizeof (ip6_address_t) - 1);
184           clib_memcpy (mgmt_ip6, &ip6_addr, vec_len (mgmt_ip6));
185         }
186       else if (unformat (input, "mgmt-oid %s", &mgmt_oid))
187         ;
188       else
189         break;
190     }
191
192   if (sw_if_index == (u32) ~ 0)
193     return clib_error_return (0, "Interface name is invalid!");
194
195   return lldp_cfg_err_to_clib_err (lldp_cfg_intf_set (sw_if_index,
196                                                       &port_desc, &mgmt_ip4,
197                                                       &mgmt_ip6, &mgmt_oid,
198                                                       enable));
199 }
200
201 lldp_cfg_err_t
202 lldp_cfg_set (u8 ** host, int hold_time, int tx_interval)
203 {
204   lldp_main_t *lm = &lldp_main;
205   int reschedule = 0;
206
207   if (host && *host)
208     {
209       vec_free (lm->sys_name);
210       lm->sys_name = *host;
211       *host = NULL;
212     }
213
214   if (hold_time)
215     {
216       if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD)
217         {
218           return lldp_invalid_arg;
219         }
220       if (lm->msg_tx_hold != hold_time)
221         {
222           lm->msg_tx_hold = hold_time;
223           reschedule = 1;
224         }
225     }
226
227   if (tx_interval)
228     {
229       if (tx_interval < LLDP_MIN_TX_INTERVAL ||
230           tx_interval > LLDP_MAX_TX_INTERVAL)
231         {
232           return lldp_invalid_arg;
233         }
234       if (lm->msg_tx_interval != tx_interval)
235         {
236           reschedule = 1;
237           lm->msg_tx_interval = tx_interval;
238         }
239     }
240
241   if (reschedule)
242     {
243       vlib_process_signal_event (lm->vlib_main, lm->lldp_process_node_index,
244                                  LLDP_EVENT_RESCHEDULE, 0);
245     }
246
247   return lldp_ok;
248 }
249
250 static clib_error_t *
251 lldp_cfg_cmd (vlib_main_t * vm, unformat_input_t * input,
252               vlib_cli_command_t * cmd)
253 {
254   int hold_time = 0;
255   int tx_interval = 0;
256   u8 *host = NULL;
257   clib_error_t *ret = NULL;
258
259   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
260     {
261       if (unformat (input, "system-name %s", &host))
262         {
263         }
264       else if (unformat (input, "tx-hold %d", &hold_time))
265         {
266           if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD)
267             {
268               ret =
269                 clib_error_return (0,
270                                    "invalid tx-hold `%d' (out of range <%d,%d>)",
271                                    hold_time, LLDP_MIN_TX_HOLD,
272                                    LLDP_MAX_TX_HOLD);
273               goto out;
274             }
275         }
276       else if (unformat (input, "tx-interval %d", &tx_interval))
277         {
278           if (tx_interval < LLDP_MIN_TX_INTERVAL ||
279               tx_interval > LLDP_MAX_TX_INTERVAL)
280             {
281               ret =
282                 clib_error_return (0,
283                                    "invalid tx-interval `%d' (out of range <%d,%d>)",
284                                    tx_interval, LLDP_MIN_TX_INTERVAL,
285                                    LLDP_MAX_TX_INTERVAL);
286               goto out;
287             }
288         }
289       else
290         {
291           break;
292         }
293     }
294   ret =
295     lldp_cfg_err_to_clib_err (lldp_cfg_set (&host, hold_time, tx_interval));
296 out:
297   vec_free (host);
298   return ret;
299 }
300
301 /* *INDENT-OFF* */
302 VLIB_CLI_COMMAND(set_interface_lldp_cmd, static) = {
303   .path = "set interface lldp",
304   .short_help = "set interface lldp <interface> | sw_if_index <idx>"
305                 " [port-desc <string>] [mgmt-ip4 <string>]"
306                 " [mgmt-ip6 <string>] [mgmt-oid <string>] [disable]",
307   .function = lldp_intf_cmd,
308 };
309
310 VLIB_CLI_COMMAND(set_lldp_cmd, static) = {
311   .path = "set lldp",
312   .short_help = "set lldp [system-name <string>] [tx-hold <value>] "
313                 "[tx-interval <value>]",
314   .function = lldp_cfg_cmd,
315 };
316 /* *INDENT-ON* */
317
318 static const char *
319 lldp_chassis_id_subtype_str (lldp_chassis_id_subtype_t t)
320 {
321   switch (t)
322     {
323 #define F(num, val, str) \
324   case num:              \
325     return str;
326       foreach_chassis_id_subtype (F)
327 #undef F
328     }
329   return "unknown chassis subtype";
330 }
331
332 static const char *
333 lldp_port_id_subtype_str (lldp_port_id_subtype_t t)
334 {
335   switch (t)
336     {
337 #define F(num, val, str) \
338   case num:              \
339     return str;
340       foreach_port_id_subtype (F)
341 #undef F
342     }
343   return "unknown port subtype";
344 }
345
346 /*
347  * format port id subtype&value
348  *
349  * @param va - 1st argument - unsigned - port id subtype
350  * @param va - 2nd argument - u8* - port id
351  * @param va - 3rd argument - unsigned - port id length
352  * @param va - 4th argument - int - 1 for detailed output, 0 for simple
353  */
354 u8 *
355 format_lldp_port_id (u8 * s, va_list * va)
356 {
357   const lldp_port_id_subtype_t subtype = va_arg (*va, unsigned);
358   const u8 *id = va_arg (*va, u8 *);
359   const unsigned len = va_arg (*va, unsigned);
360   const int detail = va_arg (*va, int);
361   if (!id)
362     {
363       return s;
364     }
365   switch (subtype)
366     {
367     case LLDP_PORT_ID_SUBTYPE_NAME (intf_alias):
368       /* fallthrough */
369     case LLDP_PORT_ID_SUBTYPE_NAME (port_comp):
370       /* fallthrough */
371     case LLDP_PORT_ID_SUBTYPE_NAME (local):
372       /* fallthrough */
373     case LLDP_PORT_ID_SUBTYPE_NAME (intf_name):
374       if (detail)
375         {
376           s = format (s, "%U(%s)", format_ascii_bytes, id, len,
377                       lldp_port_id_subtype_str (subtype));
378         }
379       else
380         {
381           s = format (s, "%U", format_ascii_bytes, id, len);
382         }
383       break;
384     case LLDP_PORT_ID_SUBTYPE_NAME (mac_addr):
385       if (ETHER_ADDR_LEN == len)
386         {
387           if (detail)
388             {
389               s = format (s, "%U(%s)", format_mac_address, id,
390                           lldp_port_id_subtype_str (subtype));
391             }
392           else
393             {
394               s = format (s, "%U", format_mac_address, id);
395             }
396           break;
397         }
398       /* fallthrough */
399     case LLDP_PORT_ID_SUBTYPE_NAME (net_addr):
400       /* TODO */
401       /* fallthrough */
402     default:
403       if (detail)
404         {
405           s = format (s, "%U(%s)", format_hex_bytes, id, len,
406                       lldp_port_id_subtype_str (subtype));
407         }
408       else
409         {
410           s = format (s, "%U", format_hex_bytes, id, len);
411         }
412       break;
413     }
414   return s;
415 }
416
417 /*
418  * format chassis id subtype&value
419  *
420  * @param s format string
421  * @param va - 1st argument - unsigned - chassis id subtype
422  * @param va - 2nd argument - u8* - chassis id
423  * @param va - 3rd argument - unsigned - chassis id length
424  * @param va - 4th argument - int - 1 for detailed output, 0 for simple
425  */
426 u8 *
427 format_lldp_chassis_id (u8 * s, va_list * va)
428 {
429   const lldp_chassis_id_subtype_t subtype =
430     va_arg (*va, lldp_chassis_id_subtype_t);
431   const u8 *id = va_arg (*va, u8 *);
432   const unsigned len = va_arg (*va, unsigned);
433   const int detail = va_arg (*va, int);
434   if (!id)
435     {
436       return s;
437     }
438   switch (subtype)
439     {
440     case LLDP_CHASS_ID_SUBTYPE_NAME (chassis_comp):
441       /* fallthrough */
442     case LLDP_CHASS_ID_SUBTYPE_NAME (intf_alias):
443       /* fallthrough */
444     case LLDP_CHASS_ID_SUBTYPE_NAME (port_comp):
445       /* fallthrough */
446     case LLDP_PORT_ID_SUBTYPE_NAME (local):
447       /* fallthrough */
448     case LLDP_CHASS_ID_SUBTYPE_NAME (intf_name):
449       if (detail)
450         {
451           s = format (s, "%U(%s)", format_ascii_bytes, id, len,
452                       lldp_chassis_id_subtype_str (subtype));
453         }
454       else
455         {
456           s = format (s, "%U", format_ascii_bytes, id, len);
457         }
458       break;
459     case LLDP_CHASS_ID_SUBTYPE_NAME (mac_addr):
460       if (ETHER_ADDR_LEN == len)
461         {
462           if (detail)
463             {
464               s = format (s, "%U(%s)", format_mac_address, id,
465                           lldp_chassis_id_subtype_str (subtype));
466             }
467           else
468             {
469               s = format (s, "%U", format_mac_address, id);
470             }
471           break;
472         }
473       /* fallthrough */
474     case LLDP_CHASS_ID_SUBTYPE_NAME (net_addr):
475       /* TODO */
476     default:
477       if (detail)
478         {
479           s = format (s, "%U(%s)", format_hex_bytes, id, len,
480                       lldp_chassis_id_subtype_str (subtype));
481         }
482       else
483         {
484           s = format (s, "%U", format_hex_bytes, id, len);
485         }
486       break;
487     }
488   return s;
489 }
490
491 /*
492  * convert a tlv code to human-readable string
493  */
494 static const char *
495 lldp_tlv_code_str (lldp_tlv_code_t t)
496 {
497   switch (t)
498     {
499 #define F(n, t, s) \
500   case n:          \
501     return s;
502       foreach_lldp_tlv_type (F)
503 #undef F
504     }
505   return "unknown lldp tlv";
506 }
507
508 /*
509  * format a single LLDP TLV
510  *
511  * @param s format string
512  * @param va variable list - pointer to lldp_tlv_t is expected
513  */
514 u8 *
515 format_lldp_tlv (u8 * s, va_list * va)
516 {
517   const lldp_tlv_t *tlv = va_arg (*va, lldp_tlv_t *);
518   if (!tlv)
519     {
520       return s;
521     }
522   u16 l = lldp_tlv_get_length (tlv);
523   switch (lldp_tlv_get_code (tlv))
524     {
525     case LLDP_TLV_NAME (chassis_id):
526       s = format (s, "%U", format_lldp_chassis_id,
527                   ((lldp_chassis_id_tlv_t *) tlv)->subtype,
528                   ((lldp_chassis_id_tlv_t *) tlv)->id,
529                   l - STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype), 1);
530       break;
531     case LLDP_TLV_NAME (port_id):
532       s = format (s, "%U", format_lldp_port_id,
533                   ((lldp_port_id_tlv_t *) tlv)->subtype,
534                   ((lldp_port_id_tlv_t *) tlv)->id,
535                   l - STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype), 1);
536       break;
537     case LLDP_TLV_NAME (ttl):
538       s = format (s, "%d", ntohs (((lldp_ttl_tlv_t *) tlv)->ttl));
539       break;
540     case LLDP_TLV_NAME (sys_name):
541       /* fallthrough */
542     case LLDP_TLV_NAME (sys_desc):
543       s = format (s, "%U", format_ascii_bytes, tlv->v, l);
544       break;
545     default:
546       s = format (s, "%U", format_hex_bytes, tlv->v, l);
547     }
548
549   return s;
550 }
551
552 static u8 *
553 format_time_ago (u8 * s, va_list * va)
554 {
555   f64 ago = va_arg (*va, double);
556   f64 now = va_arg (*va, double);
557   if (ago < 0.01)
558     {
559       return format (s, "never");
560     }
561   return format (s, "%.1fs ago", now - ago);
562 }
563
564 static u8 *
565 format_lldp_intfs_detail (u8 * s, vlib_main_t * vm, const lldp_main_t * lm)
566 {
567   vnet_main_t *vnm = &vnet_main;
568   const lldp_intf_t *n;
569   const vnet_hw_interface_t *hw;
570   const vnet_sw_interface_t *sw;
571   s = format (s, "LLDP configuration:\n");
572   if (lm->sys_name)
573     {
574       s = format (s, "Configured system name: %U\n", format_ascii_bytes,
575                   lm->sys_name, vec_len (lm->sys_name));
576     }
577
578   s = format (s, "Configured tx-hold: %d\n", (int) lm->msg_tx_hold);
579   s = format (s, "Configured tx-interval: %d\n", (int) lm->msg_tx_interval);
580   s = format (s, "\nLLDP-enabled interface table:\n");
581   f64 now = vlib_time_now (vm);
582
583   /* *INDENT-OFF* */
584   pool_foreach (
585       n, lm->intfs)  {
586         hw = vnet_get_hw_interface(vnm, n->hw_if_index);
587         sw = vnet_get_sw_interface(lm->vnet_main, hw->sw_if_index);
588
589         s = format(s, "\nLocal Interface name: %v\n"
590                       "Local Port Description: %s\n",
591                        hw->name, n->port_desc);
592         if (n->mgmt_ip4)
593           {
594             s = format (s, "Local Management address: %U\n",
595                         format_ip4_address, n->mgmt_ip4, vec_len (n->mgmt_ip4));
596           }
597
598         if (n->mgmt_ip6)
599           {
600             s = format (s, "Local Management address IPV6: %U\n",
601                         format_ip6_address, n->mgmt_ip6, vec_len (n->mgmt_ip6));
602           }
603
604         if (n->mgmt_oid)
605           {
606             s = format (s, "Local Management address OID: %U\n",
607                         format_ascii_bytes, n->mgmt_oid, vec_len (n->mgmt_oid));
608           }
609
610         /* Interface shutdown */
611         if (!(sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
612           {
613             s = format(s, "Interface/peer state: interface down\n"
614                        "Last packet sent: %U\n",
615                        format_time_ago, n->last_sent, now);
616           }
617         else if (now < n->last_heard + n->ttl)
618           {
619             s = format(s,
620                        "Interface/peer state: active\n"
621                        "Peer chassis ID: %U\nRemote port ID: %U\n"
622                        "Last packet sent: %U\nLast packet received: %U\n",
623                        format_lldp_chassis_id, n->chassis_id_subtype,
624                        n->chassis_id, vec_len(n->chassis_id), 1,
625                        format_lldp_port_id, n->port_id_subtype, n->port_id,
626                        vec_len(n->port_id), 1, format_time_ago, n->last_sent,
627                        now, format_time_ago, n->last_heard, now);
628           }
629         else
630           {
631             s = format(s,
632                        "Interface/peer state: inactive(timeout)\n"
633                        "Last known peer chassis ID: %U\n"
634                        "Last known peer port ID: %U\nLast packet sent: %U\n"
635                        "Last packet received: %U\n",
636                        format_lldp_chassis_id, n->chassis_id_subtype,
637                        n->chassis_id, vec_len(n->chassis_id), 1,
638                        format_lldp_port_id, n->port_id_subtype, n->port_id,
639                        vec_len(n->port_id), 1, format_time_ago, n->last_sent,
640                        now, format_time_ago, n->last_heard, now);
641           }
642       }
643   /* *INDENT-ON* */
644   return s;
645 }
646
647 static u8 *
648 format_lldp_intfs (u8 * s, va_list * va)
649 {
650   vlib_main_t *vm = va_arg (*va, vlib_main_t *);
651   const lldp_main_t *lm = va_arg (*va, lldp_main_t *);
652   const int detail = va_arg (*va, int);
653   vnet_main_t *vnm = &vnet_main;
654   const lldp_intf_t *n;
655
656   if (detail)
657     {
658       return format_lldp_intfs_detail (s, vm, lm);
659     }
660
661   f64 now = vlib_time_now (vm);
662   s = format (s, "%-25s %-25s %-25s %=15s %=15s %=10s\n", "Local interface",
663               "Peer chassis ID", "Remote port ID", "Last heard", "Last sent",
664               "Status");
665
666   /* *INDENT-OFF* */
667   pool_foreach (
668       n, lm->intfs)  {
669         const vnet_hw_interface_t *hw =
670             vnet_get_hw_interface(vnm, n->hw_if_index);
671         const vnet_sw_interface_t *sw =
672             vnet_get_sw_interface(lm->vnet_main, hw->sw_if_index);
673         /* Interface shutdown */
674         if (!(sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
675           continue;
676         if (now < n->last_heard + n->ttl)
677           {
678             s = format(s, "%-25v %-25U %-25U %=15U %=15U %=10s\n", hw->name,
679                        format_lldp_chassis_id, n->chassis_id_subtype,
680                        n->chassis_id, vec_len(n->chassis_id), 0,
681                        format_lldp_port_id, n->port_id_subtype, n->port_id,
682                        vec_len(n->port_id), 0, format_time_ago, n->last_heard,
683                        now, format_time_ago, n->last_sent, now, "active");
684           }
685         else
686           {
687             s = format(s, "%-25v %-25s %-25s %=15U %=15U %=10s\n", hw->name,
688                        "", "", format_time_ago, n->last_heard, now,
689                        format_time_ago, n->last_sent, now, "inactive");
690           }
691       }
692   /* *INDENT-ON* */
693   return s;
694 }
695
696 static clib_error_t *
697 show_lldp (vlib_main_t * vm, unformat_input_t * input,
698            CLIB_UNUSED (vlib_cli_command_t * lmd))
699 {
700   lldp_main_t *lm = &lldp_main;
701
702   if (unformat (input, "detail"))
703     {
704       vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 1);
705     }
706   else
707     {
708       vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 0);
709     }
710   return 0;
711 }
712
713 /* *INDENT-OFF* */
714 VLIB_CLI_COMMAND(show_lldp_command, static) = {
715   .path = "show lldp",
716   .short_help = "show lldp [detail]",
717   .function = show_lldp,
718 };
719 /* *INDENT-ON* */
720
721 /*
722  * packet trace format function, very similar to
723  * lldp_packet_scan except that we call the per TLV format
724  * functions instead of the per TLV processing functions
725  */
726 u8 *
727 lldp_input_format_trace (u8 * s, va_list * args)
728 {
729   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
730   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
731   const lldp_input_trace_t *t = va_arg (*args, lldp_input_trace_t *);
732   const u8 *cur;
733   const lldp_tlv_t *tlv;
734   cur = t->data;
735   while (((cur + lldp_tlv_get_length ((lldp_tlv_t *) cur)) <
736           t->data + t->len))
737     {
738       tlv = (lldp_tlv_t *) cur;
739       if (cur == t->data)
740         {
741           s = format (s, "TLV #%d(%s): %U\n", lldp_tlv_get_code (tlv),
742                       lldp_tlv_code_str (lldp_tlv_get_code (tlv)),
743                       format_lldp_tlv, tlv);
744         }
745       else
746         {
747           s = format (s, "  TLV #%d(%s): %U\n", lldp_tlv_get_code (tlv),
748                       lldp_tlv_code_str (lldp_tlv_get_code (tlv)),
749                       format_lldp_tlv, tlv);
750         }
751       cur += STRUCT_SIZE_OF (lldp_tlv_t, head) + lldp_tlv_get_length (tlv);
752     }
753
754   return s;
755 }
756
757 /*
758  * fd.io coding-style-patch-verification: ON
759  *
760  * Local Variables:
761  * eval: (c-set-style "gnu")
762  * End:
763  */