summaryrefslogtreecommitdiffstats
path: root/src/plugins/nat/nat66_out2in.c
blob: d404d9f71eb3b6f67c7d0406da18a44dbe6791a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/*
 * Copyright (c) 2018 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @file
 * @brief NAT66 outside to inside network translation
 */

#include <nat/nat66.h>
#include <vnet/ip/ip6_to_ip4.h>
#include <vnet/fib/fib_table.h>

typedef struct
{
  u32 sw_if_index;
  u32 next_index;
} nat66_out2in_trace_t;

static u8 *
format_nat66_out2in_trace (u8 * s, va_list * args)
{
  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
  nat66_out2in_trace_t *t = va_arg (*args, nat66_out2in_trace_t *);

  s =
    format (s, "NAT66-out2in: sw_if_index %d, next index %d", t->sw_if_index,
	    t->next_index);

  return s;
}

#define foreach_nat66_out2in_error                       \
_(OUT2IN_PACKETS, "good out2in packets processed")       \
_(NO_TRANSLATION, "no translation")                      \
_(UNKNOWN, "unknown")

typedef enum
{
#define _(sym,str) NAT66_OUT2IN_ERROR_##sym,
  foreach_nat66_out2in_error
#undef _
    NAT66_OUT2IN_N_ERROR,
} nat66_out2in_error_t;

static char *nat66_out2in_error_strings[] = {
#define _(sym,string) string,
  foreach_nat66_out2in_error
#undef _
};

typedef enum
{
  NAT66_OUT2IN_NEXT_IP6_LOOKUP,
  NAT66_OUT2IN_NEXT_DROP,
  NAT66_OUT2IN_N_NEXT,
} nat66_out2in_next_t;

VLIB_NODE_FN (nat66_out2in_node) (vlib_main_t * vm,
				  vlib_node_runtime_t * node,
				  vlib_frame_t * frame)
{
  u32 n_left_from, *from, *to_next;
  nat66_out2in_next_t next_index;
  u32 pkts_processed = 0;
  u32 thread_index = vm->thread_index;
  nat66_main_t *nm = &nat66_main;

  from = vlib_frame_vector_args (frame);
  n_left_from = frame->n_vectors;
  next_index = node->cached_next_index;

  while (n_left_from > 0)
    {
      u32 n_left_to_next;

      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);

      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  u32 bi0;
	  vlib_buffer_t *b0;
	  u32 next0 = NAT66_OUT2IN_NEXT_IP6_LOOKUP;
	  ip6_header_t *ip60;
	  u16 l4_offset0, frag_offset0;
	  u8 l4_protocol0;
	  nat66_static_mapping_t *sm0;
	  u32 sw_if_index0, fib_index0;
	  udp_header_t *udp0;
	  tcp_header_t *tcp0;
	  icmp46_header_t *icmp0;
	  u16 *checksum0 = 0;
	  ip_csum_t csum0;

	  /* speculatively enqueue b0 to the current next frame */
	  bi0 = from[0];
	  to_next[0] = bi0;
	  from += 1;
	  to_next += 1;
	  n_left_from -= 1;
	  n_left_to_next -= 1;

	  b0 = vlib_get_buffer (vm, bi0);
	  ip60 = vlib_buffer_get_current (b0);

	  if (PREDICT_FALSE
	      (ip6_parse
	       (ip60, b0->current_length, &l4_protocol0, &l4_offset0,
		&frag_offset0)))
	    {
	      next0 = NAT66_OUT2IN_NEXT_DROP;
	      b0->error = node->errors[NAT66_OUT2IN_ERROR_UNKNOWN];
	      goto trace0;
	    }

	  sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
	  fib_index0 =
	    fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
						 sw_if_index0);

	  sm0 = nat66_static_mapping_get (&ip60->dst_address, fib_index0, 0);
	  if (PREDICT_FALSE (!sm0))
	    {
	      goto trace0;
	    }

	  if (l4_protocol0 == IP_PROTOCOL_UDP)
	    {
	      udp0 = (udp_header_t *) u8_ptr_add (ip60, l4_offset0);
	      checksum0 = &udp0->checksum;
	    }
	  else if (l4_protocol0 == IP_PROTOCOL_TCP)
	    {
	      tcp0 = (tcp_header_t *) u8_ptr_add (ip60, l4_offset0);
	      checksum0 = &tcp0->checksum;
	    }
	  else if (l4_protocol0 == IP_PROTOCOL_ICMP6)
	    {
	      icmp0 = (icmp46_header_t *) u8_ptr_add (ip60, l4_offset0);
	      checksum0 = &icmp0->checksum;
	    }
	  else
	    goto skip_csum0;

	  csum0 = ip_csum_sub_even (*checksum0, ip60->dst_address.as_u64[0]);
	  csum0 = ip_csum_sub_even (csum0, ip60->dst_address.as_u64[1]);
	  csum0 = ip_csum_add_even (csum0, sm0->l_addr.as_u64[0]);
	  csum0 = ip_csum_add_even (csum0, sm0->l_addr.as_u64[1]);
	  *checksum0 = ip_csum_fold (csum0);

	skip_csum0:
	  ip60->dst_address.as_u64[0] = sm0->l_addr.as_u64[0];
	  ip60->dst_address.as_u64[1] = sm0->l_addr.as_u64[1];
	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = sm0->fib_index;

	  vlib_increment_combined_counter (&nm->session_counters,
					   thread_index, sm0 - nm->sm, 1,
					   vlib_buffer_length_in_chain (vm,
									b0));

	trace0:
	  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
			     && (b0->flags & VLIB_BUFFER_IS_TRACED)))
	    {
	      nat66_out2in_trace_t *t =
		vlib_add_trace (vm, node, b0, sizeof (*t));
	      t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
	      t->next_index = next0;
	    }

	  pkts_processed += next0 != NAT66_OUT2IN_NEXT_DROP;

	  /* verify speculative enqueue, maybe switch current next frame */
	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
					   n_left_to_next, bi0, next0);
	}
      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    }

  vlib_node_increment_counter (vm, nm->out2in_node_index,
			       NAT66_OUT2IN_ERROR_OUT2IN_PACKETS,
			       pkts_processed);
  return frame->n_vectors;
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (nat66_out2in_node) = {
  .name = "nat66-out2in",
  .vector_size = sizeof (u32),
  .format_trace = format_nat66_out2in_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,
  .n_errors = ARRAY_LEN (nat66_out2in_error_strings),
  .error_strings = nat66_out2in_error_strings,
  .n_next_nodes = NAT66_OUT2IN_N_NEXT,
  /* edit / add dispositions here */
  .next_nodes = {
    [NAT66_OUT2IN_NEXT_DROP] = "error-drop",
    [NAT66_OUT2IN_NEXT_IP6_LOOKUP] = "ip6-lookup",
  },
};
/* *INDENT-ON* */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
span class="o">= vnet_get_sw_interface (lm->vnet_main, hi->sw_if_index); if (sw->flags & (VNET_SW_INTERFACE_FLAG_ADMIN_UP | VNET_SW_INTERFACE_FLAG_BOND_SLAVE)) { lldp_schedule_intf (lm, n); } } else { lldp_intf_t *n = lldp_get_intf (lm, hi->sw_if_index); lldp_delete_intf (lm, n); } return lldp_ok; } static clib_error_t * lldp_intf_cmd (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { lldp_main_t *lm = &lldp_main; vnet_main_t *vnm = lm->vnet_main; u32 sw_if_index = (u32) ~ 0; int enable = 1; u8 *port_desc = NULL; u8 *mgmt_ip4 = NULL, *mgmt_ip6 = NULL, *mgmt_oid = NULL; ip4_address_t ip4_addr; ip6_address_t ip6_addr; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "sw_if_index %d", &sw_if_index)) ; if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index)) ; else if (unformat (input, "disable")) enable = 0; else if (unformat (input, "port-desc %s", &port_desc)) ; else if (unformat (input, "mgmt-ip4 %U", unformat_ip4_address, &ip4_addr)) { vec_validate (mgmt_ip4, sizeof (ip4_address_t) - 1); clib_memcpy (mgmt_ip4, &ip4_addr, vec_len (mgmt_ip4)); } else if (unformat (input, "mgmt-ip6 %U", unformat_ip6_address, &ip6_addr)) { vec_validate (mgmt_ip6, sizeof (ip6_address_t) - 1); clib_memcpy (mgmt_ip6, &ip6_addr, vec_len (mgmt_ip6)); } else if (unformat (input, "mgmt-oid %s", &mgmt_oid)) ; else break; } if (sw_if_index == (u32) ~ 0) return clib_error_return (0, "Interface name is invalid!"); return lldp_cfg_err_to_clib_err (lldp_cfg_intf_set (sw_if_index, &port_desc, &mgmt_ip4, &mgmt_ip6, &mgmt_oid, enable)); } lldp_cfg_err_t lldp_cfg_set (u8 ** host, int hold_time, int tx_interval) { lldp_main_t *lm = &lldp_main; int reschedule = 0; if (host && *host) { vec_free (lm->sys_name); lm->sys_name = *host; *host = NULL; } if (hold_time) { if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD) { return lldp_invalid_arg; } if (lm->msg_tx_hold != hold_time) { lm->msg_tx_hold = hold_time; reschedule = 1; } } if (tx_interval) { if (tx_interval < LLDP_MIN_TX_INTERVAL || tx_interval > LLDP_MAX_TX_INTERVAL) { return lldp_invalid_arg; } if (lm->msg_tx_interval != tx_interval) { reschedule = 1; lm->msg_tx_interval = tx_interval; } } if (reschedule) { vlib_process_signal_event (lm->vlib_main, lm->lldp_process_node_index, LLDP_EVENT_RESCHEDULE, 0); } return lldp_ok; } static clib_error_t * lldp_cfg_cmd (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { int hold_time = 0; int tx_interval = 0; u8 *host = NULL; clib_error_t *ret = NULL; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "system-name %s", &host)) { } else if (unformat (input, "tx-hold %d", &hold_time)) { if (hold_time < LLDP_MIN_TX_HOLD || hold_time > LLDP_MAX_TX_HOLD) { ret = clib_error_return (0, "invalid tx-hold `%d' (out of range <%d,%d>)", hold_time, LLDP_MIN_TX_HOLD, LLDP_MAX_TX_HOLD); goto out; } } else if (unformat (input, "tx-interval %d", &tx_interval)) { if (tx_interval < LLDP_MIN_TX_INTERVAL || tx_interval > LLDP_MAX_TX_INTERVAL) { ret = clib_error_return (0, "invalid tx-interval `%d' (out of range <%d,%d>)", tx_interval, LLDP_MIN_TX_INTERVAL, LLDP_MAX_TX_INTERVAL); goto out; } } else { break; } } ret = lldp_cfg_err_to_clib_err (lldp_cfg_set (&host, hold_time, tx_interval)); out: vec_free (host); return ret; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND(set_interface_lldp_cmd, static) = { .path = "set interface lldp", .short_help = "set interface lldp <interface> | sw_if_index <idx>" " [port-desc <string>] [mgmt-ip4 <string>]" " [mgmt-ip6 <string>] [mgmt-oid <string>] [disable]", .function = lldp_intf_cmd, }; VLIB_CLI_COMMAND(set_lldp_cmd, static) = { .path = "set lldp", .short_help = "set lldp [system-name <string>] [tx-hold <value>] " "[tx-interval <value>]", .function = lldp_cfg_cmd, }; /* *INDENT-ON* */ static const char * lldp_chassis_id_subtype_str (lldp_chassis_id_subtype_t t) { switch (t) { #define F(num, val, str) \ case num: \ return str; foreach_chassis_id_subtype (F) #undef F } return "unknown chassis subtype"; } static const char * lldp_port_id_subtype_str (lldp_port_id_subtype_t t) { switch (t) { #define F(num, val, str) \ case num: \ return str; foreach_port_id_subtype (F) #undef F } return "unknown port subtype"; } /* * format port id subtype&value * * @param va - 1st argument - unsigned - port id subtype * @param va - 2nd argument - u8* - port id * @param va - 3rd argument - unsigned - port id length * @param va - 4th argument - int - 1 for detailed output, 0 for simple */ u8 * format_lldp_port_id (u8 * s, va_list * va) { const lldp_port_id_subtype_t subtype = va_arg (*va, unsigned); const u8 *id = va_arg (*va, u8 *); const unsigned len = va_arg (*va, unsigned); const int detail = va_arg (*va, int); if (!id) { return s; } switch (subtype) { case LLDP_PORT_ID_SUBTYPE_NAME (intf_alias): /* fallthrough */ case LLDP_PORT_ID_SUBTYPE_NAME (port_comp): /* fallthrough */ case LLDP_PORT_ID_SUBTYPE_NAME (local): /* fallthrough */ case LLDP_PORT_ID_SUBTYPE_NAME (intf_name): if (detail) { s = format (s, "%U(%s)", format_ascii_bytes, id, len, lldp_port_id_subtype_str (subtype)); } else { s = format (s, "%U", format_ascii_bytes, id, len); } break; case LLDP_PORT_ID_SUBTYPE_NAME (mac_addr): if (ETHER_ADDR_LEN == len) { if (detail) { s = format (s, "%U(%s)", format_mac_address, id, lldp_port_id_subtype_str (subtype)); } else { s = format (s, "%U", format_mac_address, id); } break; } /* fallthrough */ case LLDP_PORT_ID_SUBTYPE_NAME (net_addr): /* TODO */ /* fallthrough */ default: if (detail) { s = format (s, "%U(%s)", format_hex_bytes, id, len, lldp_port_id_subtype_str (subtype)); } else { s = format (s, "%U", format_hex_bytes, id, len); } break; } return s; } /* * format chassis id subtype&value * * @param s format string * @param va - 1st argument - unsigned - chassis id subtype * @param va - 2nd argument - u8* - chassis id * @param va - 3rd argument - unsigned - chassis id length * @param va - 4th argument - int - 1 for detailed output, 0 for simple */ u8 * format_lldp_chassis_id (u8 * s, va_list * va) { const lldp_chassis_id_subtype_t subtype = va_arg (*va, lldp_chassis_id_subtype_t); const u8 *id = va_arg (*va, u8 *); const unsigned len = va_arg (*va, unsigned); const int detail = va_arg (*va, int); if (!id) { return s; } switch (subtype) { case LLDP_CHASS_ID_SUBTYPE_NAME (chassis_comp): /* fallthrough */ case LLDP_CHASS_ID_SUBTYPE_NAME (intf_alias): /* fallthrough */ case LLDP_CHASS_ID_SUBTYPE_NAME (port_comp): /* fallthrough */ case LLDP_PORT_ID_SUBTYPE_NAME (local): /* fallthrough */ case LLDP_CHASS_ID_SUBTYPE_NAME (intf_name): if (detail) { s = format (s, "%U(%s)", format_ascii_bytes, id, len, lldp_chassis_id_subtype_str (subtype)); } else { s = format (s, "%U", format_ascii_bytes, id, len); } break; case LLDP_CHASS_ID_SUBTYPE_NAME (mac_addr): if (ETHER_ADDR_LEN == len) { if (detail) { s = format (s, "%U(%s)", format_mac_address, id, lldp_chassis_id_subtype_str (subtype)); } else { s = format (s, "%U", format_mac_address, id); } break; } /* fallthrough */ case LLDP_CHASS_ID_SUBTYPE_NAME (net_addr): /* TODO */ default: if (detail) { s = format (s, "%U(%s)", format_hex_bytes, id, len, lldp_chassis_id_subtype_str (subtype)); } else { s = format (s, "%U", format_hex_bytes, id, len); } break; } return s; } /* * convert a tlv code to human-readable string */ static const char * lldp_tlv_code_str (lldp_tlv_code_t t) { switch (t) { #define F(n, t, s) \ case n: \ return s; foreach_lldp_tlv_type (F) #undef F } return "unknown lldp tlv"; } /* * format a single LLDP TLV * * @param s format string * @param va variable list - pointer to lldp_tlv_t is expected */ u8 * format_lldp_tlv (u8 * s, va_list * va) { const lldp_tlv_t *tlv = va_arg (*va, lldp_tlv_t *); if (!tlv) { return s; } u16 l = lldp_tlv_get_length (tlv); switch (lldp_tlv_get_code (tlv)) { case LLDP_TLV_NAME (chassis_id): s = format (s, "%U", format_lldp_chassis_id, ((lldp_chassis_id_tlv_t *) tlv)->subtype, ((lldp_chassis_id_tlv_t *) tlv)->id, l - STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype), 1); break; case LLDP_TLV_NAME (port_id): s = format (s, "%U", format_lldp_port_id, ((lldp_port_id_tlv_t *) tlv)->subtype, ((lldp_port_id_tlv_t *) tlv)->id, l - STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype), 1); break; case LLDP_TLV_NAME (ttl): s = format (s, "%d", ntohs (((lldp_ttl_tlv_t *) tlv)->ttl)); break; case LLDP_TLV_NAME (sys_name): /* fallthrough */ case LLDP_TLV_NAME (sys_desc): s = format (s, "%U", format_ascii_bytes, tlv->v, l); break; default: s = format (s, "%U", format_hex_bytes, tlv->v, l); } return s; } static u8 * format_time_ago (u8 * s, va_list * va) { f64 ago = va_arg (*va, double); f64 now = va_arg (*va, double); if (ago < 0.01) { return format (s, "never"); } return format (s, "%.1fs ago", now - ago); } static u8 * format_lldp_intfs_detail (u8 * s, vlib_main_t * vm, const lldp_main_t * lm) { vnet_main_t *vnm = &vnet_main; const lldp_intf_t *n; const vnet_hw_interface_t *hw; const vnet_sw_interface_t *sw; s = format (s, "LLDP configuration:\n"); if (lm->sys_name) { s = format (s, "Configured system name: %U\n", format_ascii_bytes, lm->sys_name, vec_len (lm->sys_name)); } s = format (s, "Configured tx-hold: %d\n", (int) lm->msg_tx_hold); s = format (s, "Configured tx-interval: %d\n", (int) lm->msg_tx_interval); s = format (s, "\nLLDP-enabled interface table:\n"); f64 now = vlib_time_now (vm); /* *INDENT-OFF* */ pool_foreach( n, lm->intfs, ({ hw = vnet_get_hw_interface(vnm, n->hw_if_index); sw = vnet_get_sw_interface(lm->vnet_main, hw->sw_if_index); s = format(s, "\nLocal Interface name: %s\n" "Local Port Description: %s\n", hw->name, n->port_desc); if (n->mgmt_ip4) { s = format (s, "Local Management address: %U\n", format_ip4_address, n->mgmt_ip4, vec_len (n->mgmt_ip4)); } if (n->mgmt_ip6) { s = format (s, "Local Management address IPV6: %U\n", format_ip6_address, n->mgmt_ip6, vec_len (n->mgmt_ip6)); } if (n->mgmt_oid) { s = format (s, "Local Management address OID: %U\n", format_ascii_bytes, n->mgmt_oid, vec_len (n->mgmt_oid)); } /* Interface shutdown */ if (!(sw->flags & (VNET_SW_INTERFACE_FLAG_ADMIN_UP | VNET_SW_INTERFACE_FLAG_BOND_SLAVE))) { s = format(s, "Interface/peer state: interface down\n" "Last packet sent: %U\n", format_time_ago, n->last_sent, now); } else if (now < n->last_heard + n->ttl) { s = format(s, "Interface/peer state: active\n" "Peer chassis ID: %U\nRemote port ID: %U\n" "Last packet sent: %U\nLast packet received: %U\n", format_lldp_chassis_id, n->chassis_id_subtype, n->chassis_id, vec_len(n->chassis_id), 1, format_lldp_port_id, n->port_id_subtype, n->port_id, vec_len(n->port_id), 1, format_time_ago, n->last_sent, now, format_time_ago, n->last_heard, now); } else { s = format(s, "Interface/peer state: inactive(timeout)\n" "Last known peer chassis ID: %U\n" "Last known peer port ID: %U\nLast packet sent: %U\n" "Last packet received: %U\n", format_lldp_chassis_id, n->chassis_id_subtype, n->chassis_id, vec_len(n->chassis_id), 1, format_lldp_port_id, n->port_id_subtype, n->port_id, vec_len(n->port_id), 1, format_time_ago, n->last_sent, now, format_time_ago, n->last_heard, now); } })); /* *INDENT-ON* */ return s; } static u8 * format_lldp_intfs (u8 * s, va_list * va) { vlib_main_t *vm = va_arg (*va, vlib_main_t *); const lldp_main_t *lm = va_arg (*va, lldp_main_t *); const int detail = va_arg (*va, int); vnet_main_t *vnm = &vnet_main; const lldp_intf_t *n; if (detail) { return format_lldp_intfs_detail (s, vm, lm); } f64 now = vlib_time_now (vm); s = format (s, "%-25s %-25s %-25s %=15s %=15s %=10s\n", "Local interface", "Peer chassis ID", "Remote port ID", "Last heard", "Last sent", "Status"); /* *INDENT-OFF* */ pool_foreach( n, lm->intfs, ({ const vnet_hw_interface_t *hw = vnet_get_hw_interface(vnm, n->hw_if_index); const vnet_sw_interface_t *sw = vnet_get_sw_interface(lm->vnet_main, hw->sw_if_index); /* Interface shutdown */ if (!(sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)) continue; if (now < n->last_heard + n->ttl) { s = format(s, "%-25s %-25U %-25U %=15U %=15U %=10s\n", hw->name, format_lldp_chassis_id, n->chassis_id_subtype, n->chassis_id, vec_len(n->chassis_id), 0, format_lldp_port_id, n->port_id_subtype, n->port_id, vec_len(n->port_id), 0, format_time_ago, n->last_heard, now, format_time_ago, n->last_sent, now, "active"); } else { s = format(s, "%-25s %-25s %-25s %=15U %=15U %=10s\n", hw->name, "", "", format_time_ago, n->last_heard, now, format_time_ago, n->last_sent, now, "inactive"); } })); /* *INDENT-ON* */ return s; } static clib_error_t * show_lldp (vlib_main_t * vm, unformat_input_t * input, CLIB_UNUSED (vlib_cli_command_t * lmd)) { lldp_main_t *lm = &lldp_main; if (unformat (input, "detail")) { vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 1); } else { vlib_cli_output (vm, "%U\n", format_lldp_intfs, vm, lm, 0); } return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND(show_lldp_command, static) = { .path = "show lldp", .short_help = "show lldp [detail]", .function = show_lldp, }; /* *INDENT-ON* */ /* * packet trace format function, very similar to * lldp_packet_scan except that we call the per TLV format * functions instead of the per TLV processing functions */ u8 * lldp_input_format_trace (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); const lldp_input_trace_t *t = va_arg (*args, lldp_input_trace_t *); const u8 *cur; const lldp_tlv_t *tlv; cur = t->data; while (((cur + lldp_tlv_get_length ((lldp_tlv_t *) cur)) < t->data + t->len)) { tlv = (lldp_tlv_t *) cur; if (cur == t->data) { s = format (s, "TLV #%d(%s): %U\n", lldp_tlv_get_code (tlv), lldp_tlv_code_str (lldp_tlv_get_code (tlv)), format_lldp_tlv, tlv); } else { s = format (s, " TLV #%d(%s): %U\n", lldp_tlv_get_code (tlv), lldp_tlv_code_str (lldp_tlv_get_code (tlv)), format_lldp_tlv, tlv); } cur += STRUCT_SIZE_OF (lldp_tlv_t, head) + lldp_tlv_get_length (tlv); } return s; } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */