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