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