2 * Copyright (c) 2021 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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <vlib/vlib.h>
18 #include <vnet/vnet.h>
19 #include <vnet/ethernet/ethernet.h>
20 #include <vnet/feature/feature.h>
21 #include <vnet/ip/ip6_packet.h>
22 #include <vnet/ip-neighbor/ip6_neighbor.h>
23 #include <vnet/ip-neighbor/ip_neighbor.h>
24 #include <vnet/ip-neighbor/ip_neighbor_dp.h>
25 #include <vnet/ip6-nd/ip6_nd_inline.h>
26 #include <vnet/fib/ip6_fib.h>
27 #include <vnet/ip/ip6_ll_table.h>
29 #include <vppinfra/error.h>
32 ip6_nd_proxy_enable_disable (u32 sw_if_index, u8 enable)
37 vnet_feature_enable_disable ("ip6-unicast", "ip6-unicast-nd-proxy",
38 sw_if_index, 1, NULL, 0);
39 vnet_feature_enable_disable ("ip6-multicast", "ip6-multicast-nd-proxy",
40 sw_if_index, 1, NULL, 0);
44 vnet_feature_enable_disable ("ip6-unicast", "ip6-unicast-nd-proxy",
45 sw_if_index, 0, NULL, 0);
46 vnet_feature_enable_disable ("ip6-multicast", "ip6-multicast-nd-proxy",
47 sw_if_index, 0, NULL, 0);
53 set_int_ip6_nd_proxy_command_fn (vlib_main_t *vm, unformat_input_t *input,
54 vlib_cli_command_t *cmd)
56 vnet_main_t *vnm = vnet_get_main ();
62 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
64 if (unformat (input, "%U", unformat_vnet_sw_interface, vnm,
67 else if (unformat (input, "enable"))
69 else if (unformat (input, "disable"))
75 if (~0 == sw_if_index)
76 return clib_error_return (0, "unknown input '%U'", format_unformat_error,
79 ip6_nd_proxy_enable_disable (sw_if_index, enable);
84 VLIB_CLI_COMMAND (set_int_ip6_nd_proxy_enable_command, static) = {
85 .path = "set interface ip6-nd proxy",
86 .short_help = "set interface ip6-nd proxy <intfc> [enable|disable]",
87 .function = set_int_ip6_nd_proxy_command_fn,
94 } vnet_ip6_nd_proxy_trace_t;
97 format_ip6_nd_proxy_trace (u8 *s, va_list *args)
99 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
100 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
101 vnet_main_t *vnm = vnet_get_main ();
102 vnet_ip6_nd_proxy_trace_t *t = va_arg (*args, vnet_ip6_nd_proxy_trace_t *);
103 u32 indent = format_get_indent (s);
106 s = format (s, "%U %U multicast ", format_white_space, indent,
107 format_vnet_sw_if_index_name, vnm, t->sw_if_index);
109 s = format (s, "%U %U unicast ", format_white_space, indent,
110 format_vnet_sw_if_index_name, vnm, t->sw_if_index);
115 static_always_inline void
116 ip6_nd_proxy_unicast (vlib_main_t *vm, vlib_node_runtime_t *node,
117 vlib_buffer_t *b0, ip6_header_t *ip6, u32 *next0)
119 if (PREDICT_FALSE (ip6->protocol == IP_PROTOCOL_ICMP6))
121 icmp46_header_t *icmp0;
124 icmp0 = ip6_next_header (ip6);
126 if (type0 == ICMP6_neighbor_solicitation ||
127 type0 == ICMP6_neighbor_advertisement)
129 icmp6_neighbor_solicitation_or_advertisement_header_t *icmp6_nsa;
130 icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
134 icmp6_nsa = (void *) icmp0;
135 icmp6_nd_ell_addr = (void *) (icmp6_nsa + 1);
137 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
139 /* unicast neighbor solicitation */
140 fib_node_index_t fei;
143 fib_index = ip6_fib_table_get_index_for_sw_if_index (sw_if_index0);
147 *next0 = ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP;
151 if (ip6_address_is_link_local_unicast (&ip6->dst_address))
153 fei = ip6_fib_table_lookup_exact_match (
154 ip6_ll_fib_get (sw_if_index0), &ip6->dst_address, 128);
158 fei = ip6_fib_table_lookup_exact_match (
159 fib_index, &ip6->dst_address, 128);
162 if (FIB_NODE_INDEX_INVALID != fei)
164 *next0 = ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY;
165 icmp6_send_neighbor_advertisement (
166 vm, b0, ip6, icmp6_nsa, icmp6_nd_ell_addr, sw_if_index0);
169 if (b0->flags & VLIB_BUFFER_IS_TRACED)
171 vnet_ip6_nd_proxy_trace_t *t;
172 t = vlib_add_trace (vm, node, b0, sizeof (t[0]));
173 t->sw_if_index = sw_if_index0;
180 static_always_inline void
181 ip6_nd_proxy_multicast (vlib_main_t *vm, vlib_node_runtime_t *node,
182 vlib_buffer_t *b0, ip6_header_t *ip6, u32 *next0)
184 if (PREDICT_FALSE (ip6->protocol == IP_PROTOCOL_ICMP6))
186 icmp46_header_t *icmp0;
189 icmp0 = ip6_next_header (ip6);
191 if (type0 == ICMP6_neighbor_solicitation ||
192 type0 == ICMP6_neighbor_advertisement)
194 icmp6_neighbor_solicitation_or_advertisement_header_t *icmp6_nsa;
195 icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
199 icmp6_nsa = (void *) icmp0;
200 icmp6_nd_ell_addr = (void *) (icmp6_nsa + 1);
202 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
203 if (type0 == ICMP6_neighbor_solicitation)
206 (icmp6_nd_ell_addr->header.type ==
207 ICMP6_NEIGHBOR_DISCOVERY_OPTION_source_link_layer_address) &&
208 (!ip6_address_is_unspecified (&ip6->src_address)) &&
209 (!ip6_address_is_link_local_unicast (&ip6->src_address)))
211 ip_neighbor_learn_t learn = { .sw_if_index = sw_if_index0,
214 .ip.ip6 = ip6->src_address,
216 clib_memcpy (&learn.mac, icmp6_nd_ell_addr->ethernet_address,
218 ip_neighbor_learn_dp (&learn);
220 *next0 = ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY;
221 icmp6_send_neighbor_advertisement (
222 vm, b0, ip6, icmp6_nsa, icmp6_nd_ell_addr, sw_if_index0);
225 else // type0 = ICMP6_neighbor_advertisement
227 icmp6_neighbor_solicitation_or_advertisement_header_t
228 *icmp6_nsa = (void *) icmp0;
229 icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
230 *icmp6_nd_ell_addr = (void *) (icmp6_nsa + 1);
232 (icmp6_nd_ell_addr->header.type ==
233 ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address) &&
234 (!ip6_address_is_unspecified (&ip6->src_address)) &&
235 (!ip6_address_is_link_local_unicast (&ip6->src_address)))
237 ip_neighbor_learn_t learn = { .sw_if_index = sw_if_index0,
241 icmp6_nsa->target_address,
243 clib_memcpy (&learn.mac, icmp6_nd_ell_addr->ethernet_address,
245 ip_neighbor_learn_dp (&learn);
247 *next0 = ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP;
251 if (b0->flags & VLIB_BUFFER_IS_TRACED)
253 vnet_ip6_nd_proxy_trace_t *t;
254 t = vlib_add_trace (vm, node, b0, sizeof (t[0]));
255 t->sw_if_index = sw_if_index0;
262 static_always_inline uword
263 ip6_nd_proxy_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
264 vlib_frame_t *frame, u8 is_multicast)
266 u32 n_left_from, *from, *to_next;
267 u32 next_index, n_left_to_next;
268 vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
270 from = vlib_frame_vector_args (frame);
271 n_left_from = frame->n_vectors;
272 next_index = node->cached_next_index;
274 vlib_get_buffers (vm, from, bufs, n_left_from);
276 while (n_left_from > 0)
278 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
280 while (n_left_from > 4 && n_left_to_next > 2)
282 ip6_header_t *ip6_0, *ip6_1;
286 /* Prefetch next iteration. */
288 vlib_prefetch_buffer_header (b[2], LOAD);
289 vlib_prefetch_buffer_header (b[3], LOAD);
291 vlib_prefetch_buffer_data (b[2], LOAD);
292 vlib_prefetch_buffer_data (b[3], LOAD);
296 * speculatively enqueue b0 and b1 to the current next frame
298 to_next[0] = bi0 = from[0];
299 to_next[1] = bi1 = from[1];
303 vnet_feature_next (&next0, b[0]);
304 vnet_feature_next (&next1, b[1]);
306 ip6_0 = vlib_buffer_get_current (b[0]);
307 ip6_1 = vlib_buffer_get_current (b[1]);
311 ip6_nd_proxy_multicast (vm, node, b[0], ip6_0, &next0);
312 ip6_nd_proxy_multicast (vm, node, b[1], ip6_1, &next1);
316 ip6_nd_proxy_unicast (vm, node, b[0], ip6_0, &next0);
317 ip6_nd_proxy_unicast (vm, node, b[1], ip6_1, &next1);
319 vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
320 n_left_to_next, bi0, bi1, next0,
328 while (n_left_from > 0 && n_left_to_next > 0)
333 /* speculatively enqueue b0 to the current next frame */
334 to_next[0] = bi0 = from[0];
338 vnet_feature_next (&next0, b[0]);
339 ip6_0 = vlib_buffer_get_current (b[0]);
342 ip6_nd_proxy_multicast (vm, node, b[0], ip6_0, &next0);
344 ip6_nd_proxy_unicast (vm, node, b[0], ip6_0, &next0);
346 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
347 n_left_to_next, bi0, next0);
352 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
355 return frame->n_vectors;
358 VLIB_NODE_FN (ip6_unicast_nd_proxy_node)
359 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
361 return ip6_nd_proxy_node_inline (vm, node, frame, 0 /* is_multicast */);
364 VLIB_REGISTER_NODE (ip6_unicast_nd_proxy_node) = {
365 .vector_size = sizeof (u32),
366 .format_trace = format_ip6_nd_proxy_trace,
367 .type = VLIB_NODE_TYPE_INTERNAL,
369 .n_next_nodes = ICMP6_NEIGHBOR_SOLICITATION_N_NEXT,
371 [ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP] = "ip6-drop",
372 [ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY] = "interface-output",
374 .name = "ip6-unicast-nd-proxy",
377 VLIB_NODE_FN (ip6_multicast_nd_proxy_node)
378 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
380 return ip6_nd_proxy_node_inline (vm, node, frame, 1 /* is_multicast */);
383 VLIB_REGISTER_NODE (ip6_multicast_nd_proxy_node) = {
384 .vector_size = sizeof (u32),
385 .format_trace = format_ip6_nd_proxy_trace,
386 .type = VLIB_NODE_TYPE_INTERNAL,
388 .n_next_nodes = ICMP6_NEIGHBOR_SOLICITATION_N_NEXT,
390 [ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP] = "ip6-drop",
391 [ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY] = "interface-output",
393 .name = "ip6-multicast-nd-proxy",
396 VNET_FEATURE_INIT (ip6_unicast_nd_proxy_node, static) = {
397 .arc_name = "ip6-unicast",
398 .node_name = "ip6-unicast-nd-proxy",
399 .runs_before = VNET_FEATURES ("ip6-lookup"),
402 VNET_FEATURE_INIT (ip6_multicast_nd_proxy_node, static) = {
403 .arc_name = "ip6-multicast",
404 .node_name = "ip6-multicast-nd-proxy",
405 .runs_before = VNET_FEATURES ("ip6-mfib-forward-lookup"),
409 * fd.io coding-style-patch-verification: ON
412 * eval: (c-set-style "gnu")