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