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