Line data Source code
1 : /*
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:
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 : #include <vlib/vlib.h>
17 :
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>
28 :
29 : #include <vppinfra/error.h>
30 :
31 : int
32 2 : ip6_nd_proxy_enable_disable (u32 sw_if_index, u8 enable)
33 : {
34 :
35 2 : if (enable)
36 : {
37 1 : vnet_feature_enable_disable ("ip6-unicast", "ip6-unicast-nd-proxy",
38 : sw_if_index, 1, NULL, 0);
39 1 : vnet_feature_enable_disable ("ip6-multicast", "ip6-multicast-nd-proxy",
40 : sw_if_index, 1, NULL, 0);
41 : }
42 : else
43 : {
44 1 : vnet_feature_enable_disable ("ip6-unicast", "ip6-unicast-nd-proxy",
45 : sw_if_index, 0, NULL, 0);
46 1 : vnet_feature_enable_disable ("ip6-multicast", "ip6-multicast-nd-proxy",
47 : sw_if_index, 0, NULL, 0);
48 : }
49 2 : return 0;
50 : }
51 :
52 : static clib_error_t *
53 0 : set_int_ip6_nd_proxy_command_fn (vlib_main_t *vm, unformat_input_t *input,
54 : vlib_cli_command_t *cmd)
55 : {
56 0 : vnet_main_t *vnm = vnet_get_main ();
57 : u32 sw_if_index;
58 0 : int enable = 0;
59 :
60 0 : sw_if_index = ~0;
61 :
62 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
63 : {
64 0 : if (unformat (input, "%U", unformat_vnet_sw_interface, vnm,
65 : &sw_if_index))
66 : ;
67 0 : else if (unformat (input, "enable"))
68 0 : enable = 1;
69 0 : else if (unformat (input, "disable"))
70 0 : enable = 0;
71 : else
72 0 : break;
73 : }
74 :
75 0 : if (~0 == sw_if_index)
76 0 : return clib_error_return (0, "unknown input '%U'", format_unformat_error,
77 : input);
78 :
79 0 : ip6_nd_proxy_enable_disable (sw_if_index, enable);
80 :
81 0 : return 0;
82 : }
83 :
84 272887 : 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,
88 : };
89 :
90 : typedef struct
91 : {
92 : u8 is_multicast;
93 : u32 sw_if_index;
94 : } vnet_ip6_nd_proxy_trace_t;
95 :
96 : static u8 *
97 3 : format_ip6_nd_proxy_trace (u8 *s, va_list *args)
98 : {
99 3 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
100 3 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
101 3 : vnet_main_t *vnm = vnet_get_main ();
102 3 : vnet_ip6_nd_proxy_trace_t *t = va_arg (*args, vnet_ip6_nd_proxy_trace_t *);
103 3 : u32 indent = format_get_indent (s);
104 :
105 3 : if (t->is_multicast)
106 1 : s = format (s, "%U %U multicast ", format_white_space, indent,
107 : format_vnet_sw_if_index_name, vnm, t->sw_if_index);
108 : else
109 2 : s = format (s, "%U %U unicast ", format_white_space, indent,
110 : format_vnet_sw_if_index_name, vnm, t->sw_if_index);
111 :
112 3 : return s;
113 : }
114 :
115 : static_always_inline void
116 3 : ip6_nd_proxy_unicast (vlib_main_t *vm, vlib_node_runtime_t *node,
117 : vlib_buffer_t *b0, ip6_header_t *ip6, u32 *next0)
118 : {
119 3 : if (PREDICT_FALSE (ip6->protocol == IP_PROTOCOL_ICMP6))
120 : {
121 : icmp46_header_t *icmp0;
122 : icmp6_type_t type0;
123 :
124 3 : icmp0 = ip6_next_header (ip6);
125 3 : type0 = icmp0->type;
126 3 : if (type0 == ICMP6_neighbor_solicitation ||
127 : type0 == ICMP6_neighbor_advertisement)
128 : {
129 : icmp6_neighbor_solicitation_or_advertisement_header_t *icmp6_nsa;
130 : icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
131 : *icmp6_nd_ell_addr;
132 : u32 sw_if_index0;
133 :
134 2 : icmp6_nsa = (void *) icmp0;
135 2 : icmp6_nd_ell_addr = (void *) (icmp6_nsa + 1);
136 :
137 2 : sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
138 :
139 : /* unicast neighbor solicitation */
140 : fib_node_index_t fei;
141 : u32 fib_index;
142 :
143 2 : fib_index = ip6_fib_table_get_index_for_sw_if_index (sw_if_index0);
144 :
145 2 : if (~0 == fib_index)
146 : {
147 0 : *next0 = ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP;
148 : }
149 : else
150 : {
151 2 : if (ip6_address_is_link_local_unicast (&ip6->dst_address))
152 : {
153 0 : fei = ip6_fib_table_lookup_exact_match (
154 0 : ip6_ll_fib_get (sw_if_index0), &ip6->dst_address, 128);
155 : }
156 : else
157 : {
158 2 : fei = ip6_fib_table_lookup_exact_match (
159 2 : fib_index, &ip6->dst_address, 128);
160 : }
161 :
162 2 : if (FIB_NODE_INDEX_INVALID != fei)
163 : {
164 1 : *next0 = ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY;
165 1 : icmp6_send_neighbor_advertisement (
166 : vm, b0, ip6, icmp6_nsa, icmp6_nd_ell_addr, sw_if_index0);
167 : }
168 : }
169 2 : if (b0->flags & VLIB_BUFFER_IS_TRACED)
170 : {
171 : vnet_ip6_nd_proxy_trace_t *t;
172 2 : t = vlib_add_trace (vm, node, b0, sizeof (t[0]));
173 2 : t->sw_if_index = sw_if_index0;
174 2 : t->is_multicast = 0;
175 : }
176 : }
177 : }
178 3 : }
179 :
180 : static_always_inline void
181 1 : ip6_nd_proxy_multicast (vlib_main_t *vm, vlib_node_runtime_t *node,
182 : vlib_buffer_t *b0, ip6_header_t *ip6, u32 *next0)
183 : {
184 1 : if (PREDICT_FALSE (ip6->protocol == IP_PROTOCOL_ICMP6))
185 : {
186 : icmp46_header_t *icmp0;
187 : icmp6_type_t type0;
188 :
189 1 : icmp0 = ip6_next_header (ip6);
190 1 : type0 = icmp0->type;
191 1 : if (type0 == ICMP6_neighbor_solicitation ||
192 : type0 == ICMP6_neighbor_advertisement)
193 : {
194 : icmp6_neighbor_solicitation_or_advertisement_header_t *icmp6_nsa;
195 : icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
196 : *icmp6_nd_ell_addr;
197 : u32 sw_if_index0;
198 :
199 1 : icmp6_nsa = (void *) icmp0;
200 1 : icmp6_nd_ell_addr = (void *) (icmp6_nsa + 1);
201 :
202 1 : sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
203 1 : if (type0 == ICMP6_neighbor_solicitation)
204 : {
205 1 : if (
206 1 : (icmp6_nd_ell_addr->header.type ==
207 1 : ICMP6_NEIGHBOR_DISCOVERY_OPTION_source_link_layer_address) &&
208 2 : (!ip6_address_is_unspecified (&ip6->src_address)) &&
209 1 : (!ip6_address_is_link_local_unicast (&ip6->src_address)))
210 : {
211 1 : ip_neighbor_learn_t learn = { .sw_if_index = sw_if_index0,
212 : .ip = {
213 : .version = AF_IP6,
214 : .ip.ip6 = ip6->src_address,
215 : } };
216 1 : clib_memcpy (&learn.mac, icmp6_nd_ell_addr->ethernet_address,
217 : sizeof (learn.mac));
218 1 : ip_neighbor_learn_dp (&learn);
219 :
220 1 : *next0 = ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY;
221 1 : icmp6_send_neighbor_advertisement (
222 : vm, b0, ip6, icmp6_nsa, icmp6_nd_ell_addr, sw_if_index0);
223 : }
224 : }
225 : else // type0 = ICMP6_neighbor_advertisement
226 : {
227 : icmp6_neighbor_solicitation_or_advertisement_header_t
228 0 : *icmp6_nsa = (void *) icmp0;
229 : icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
230 0 : *icmp6_nd_ell_addr = (void *) (icmp6_nsa + 1);
231 0 : if (
232 0 : (icmp6_nd_ell_addr->header.type ==
233 0 : ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address) &&
234 0 : (!ip6_address_is_unspecified (&ip6->src_address)) &&
235 0 : (!ip6_address_is_link_local_unicast (&ip6->src_address)))
236 : {
237 0 : ip_neighbor_learn_t learn = { .sw_if_index = sw_if_index0,
238 : .ip = {
239 : .version = AF_IP6,
240 : .ip.ip6 =
241 : icmp6_nsa->target_address,
242 : } };
243 0 : clib_memcpy (&learn.mac, icmp6_nd_ell_addr->ethernet_address,
244 : sizeof (learn.mac));
245 0 : ip_neighbor_learn_dp (&learn);
246 :
247 0 : *next0 = ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP;
248 : }
249 : }
250 :
251 1 : if (b0->flags & VLIB_BUFFER_IS_TRACED)
252 : {
253 : vnet_ip6_nd_proxy_trace_t *t;
254 1 : t = vlib_add_trace (vm, node, b0, sizeof (t[0]));
255 1 : t->sw_if_index = sw_if_index0;
256 1 : t->is_multicast = 1;
257 : }
258 : }
259 : }
260 1 : }
261 :
262 : static_always_inline uword
263 4 : ip6_nd_proxy_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
264 : vlib_frame_t *frame, u8 is_multicast)
265 : {
266 : u32 n_left_from, *from, *to_next;
267 : u32 next_index, n_left_to_next;
268 4 : vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
269 :
270 4 : from = vlib_frame_vector_args (frame);
271 4 : n_left_from = frame->n_vectors;
272 4 : next_index = node->cached_next_index;
273 :
274 4 : vlib_get_buffers (vm, from, bufs, n_left_from);
275 :
276 8 : while (n_left_from > 0)
277 : {
278 4 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
279 :
280 4 : while (n_left_from > 4 && n_left_to_next > 2)
281 : {
282 : ip6_header_t *ip6_0, *ip6_1;
283 : u32 next0, next1;
284 : u32 bi0, bi1;
285 :
286 : /* Prefetch next iteration. */
287 : {
288 0 : vlib_prefetch_buffer_header (b[2], LOAD);
289 0 : vlib_prefetch_buffer_header (b[3], LOAD);
290 :
291 0 : vlib_prefetch_buffer_data (b[2], LOAD);
292 0 : vlib_prefetch_buffer_data (b[3], LOAD);
293 : }
294 :
295 : /*
296 : * speculatively enqueue b0 and b1 to the current next frame
297 : */
298 0 : to_next[0] = bi0 = from[0];
299 0 : to_next[1] = bi1 = from[1];
300 0 : to_next += 2;
301 0 : n_left_to_next -= 2;
302 :
303 0 : vnet_feature_next (&next0, b[0]);
304 0 : vnet_feature_next (&next1, b[1]);
305 :
306 0 : ip6_0 = vlib_buffer_get_current (b[0]);
307 0 : ip6_1 = vlib_buffer_get_current (b[1]);
308 :
309 0 : if (is_multicast)
310 : {
311 0 : ip6_nd_proxy_multicast (vm, node, b[0], ip6_0, &next0);
312 0 : ip6_nd_proxy_multicast (vm, node, b[1], ip6_1, &next1);
313 : }
314 : else
315 : {
316 0 : ip6_nd_proxy_unicast (vm, node, b[0], ip6_0, &next0);
317 0 : ip6_nd_proxy_unicast (vm, node, b[1], ip6_1, &next1);
318 : }
319 0 : vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
320 : n_left_to_next, bi0, bi1, next0,
321 : next1);
322 :
323 0 : b += 2;
324 0 : from += 2;
325 0 : n_left_from -= 2;
326 : }
327 :
328 8 : while (n_left_from > 0 && n_left_to_next > 0)
329 : {
330 : ip6_header_t *ip6_0;
331 : u32 next0, bi0;
332 :
333 : /* speculatively enqueue b0 to the current next frame */
334 4 : to_next[0] = bi0 = from[0];
335 4 : to_next += 1;
336 4 : n_left_to_next -= 1;
337 :
338 4 : vnet_feature_next (&next0, b[0]);
339 4 : ip6_0 = vlib_buffer_get_current (b[0]);
340 :
341 4 : if (is_multicast)
342 1 : ip6_nd_proxy_multicast (vm, node, b[0], ip6_0, &next0);
343 : else
344 3 : ip6_nd_proxy_unicast (vm, node, b[0], ip6_0, &next0);
345 :
346 4 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
347 : n_left_to_next, bi0, next0);
348 4 : b += 1;
349 4 : from += 1;
350 4 : n_left_from -= 1;
351 : }
352 4 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
353 : }
354 :
355 4 : return frame->n_vectors;
356 : }
357 :
358 562 : VLIB_NODE_FN (ip6_unicast_nd_proxy_node)
359 : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
360 : {
361 3 : return ip6_nd_proxy_node_inline (vm, node, frame, 0 /* is_multicast */);
362 : }
363 :
364 178120 : 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,
368 : .n_errors = 0,
369 : .n_next_nodes = ICMP6_NEIGHBOR_SOLICITATION_N_NEXT,
370 : .next_nodes = {
371 : [ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP] = "ip6-drop",
372 : [ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY] = "interface-output",
373 : },
374 : .name = "ip6-unicast-nd-proxy",
375 : };
376 :
377 560 : VLIB_NODE_FN (ip6_multicast_nd_proxy_node)
378 : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
379 : {
380 1 : return ip6_nd_proxy_node_inline (vm, node, frame, 1 /* is_multicast */);
381 : }
382 :
383 178120 : 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,
387 : .n_errors = 0,
388 : .n_next_nodes = ICMP6_NEIGHBOR_SOLICITATION_N_NEXT,
389 : .next_nodes = {
390 : [ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP] = "ip6-drop",
391 : [ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY] = "interface-output",
392 : },
393 : .name = "ip6-multicast-nd-proxy",
394 : };
395 :
396 70583 : 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"),
400 : };
401 :
402 70583 : 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"),
406 : };
407 :
408 : /*
409 : * fd.io coding-style-patch-verification: ON
410 : *
411 : * Local Variables:
412 : * eval: (c-set-style "gnu")
413 : * End:
414 : */
|