Line data Source code
1 : /*
2 : * nsh_output.c: NSH Adj rewrite
3 : *
4 : * Copyright (c) 2017-2019 Intel and/or its affiliates.
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at:
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #include <vlib/vlib.h>
19 : #include <vnet/ip/ip.h>
20 : #include <nsh/nsh.h>
21 :
22 : typedef struct {
23 : /* Adjacency taken. */
24 : u32 adj_index;
25 : u32 flow_hash;
26 :
27 : /* Packet data, possibly *after* rewrite. */
28 : u8 packet_data[64 - 1*sizeof(u32)];
29 : } nsh_output_trace_t;
30 :
31 : #define foreach_nsh_output_next \
32 : _(DROP, "error-drop") \
33 : _(INTERFACE, "interface-output" )
34 :
35 : typedef enum {
36 : #define _(s,n) NSH_OUTPUT_NEXT_##s,
37 : foreach_nsh_output_next
38 : #undef _
39 : NSH_OUTPUT_N_NEXT,
40 : } nsh_output_next_t;
41 :
42 : static u8 *
43 0 : format_nsh_output_trace (u8 * s, va_list * args)
44 : {
45 0 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
46 0 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
47 0 : nsh_output_trace_t * t = va_arg (*args, nsh_output_trace_t *);
48 0 : uword indent = format_get_indent (s);
49 :
50 0 : s = format (s, "adj-idx %d : %U flow hash: 0x%08x",
51 : t->adj_index,
52 : format_ip_adjacency, t->adj_index, FORMAT_IP_ADJACENCY_NONE,
53 : t->flow_hash);
54 0 : s = format (s, "\n%U%U",
55 : format_white_space, indent,
56 : format_ip_adjacency_packet_data,
57 0 : t->adj_index, t->packet_data, sizeof (t->packet_data));
58 0 : return s;
59 : }
60 :
61 : static inline uword
62 0 : nsh_output_inline (vlib_main_t * vm,
63 : vlib_node_runtime_t * node,
64 : vlib_frame_t * from_frame,
65 : int is_midchain)
66 : {
67 : u32 n_left_from, next_index, * from, * to_next, thread_index;
68 : vlib_node_runtime_t * error_node;
69 : u32 n_left_to_next;
70 : nsh_main_t *nm;
71 :
72 0 : thread_index = vlib_get_thread_index();
73 0 : error_node = vlib_node_get_runtime (vm, nsh_eth_output_node.index);
74 0 : from = vlib_frame_vector_args (from_frame);
75 0 : n_left_from = from_frame->n_vectors;
76 0 : next_index = node->cached_next_index;
77 0 : nm = &nsh_main;
78 :
79 0 : while (n_left_from > 0)
80 : {
81 0 : vlib_get_next_frame (vm, node, next_index,
82 : to_next, n_left_to_next);
83 :
84 0 : while (n_left_from >= 4 && n_left_to_next >= 2)
85 : {
86 : ip_adjacency_t * adj0;
87 : nsh_base_header_t *hdr0;
88 : ethernet_header_t * eth_hdr0;
89 : vlib_buffer_t * p0;
90 : u32 pi0, adj_index0, next0, error0;
91 :
92 : ip_adjacency_t * adj1;
93 : nsh_base_header_t *hdr1;
94 : ethernet_header_t * eth_hdr1;
95 : vlib_buffer_t * p1;
96 : u32 pi1, adj_index1, next1, error1;
97 : int pkt_len0, pkt_len1;
98 : word rw_len0, rw_len1;
99 :
100 : /* Prefetch next iteration. */
101 : {
102 : vlib_buffer_t * p2, * p3;
103 :
104 0 : p2 = vlib_get_buffer (vm, from[2]);
105 0 : p3 = vlib_get_buffer (vm, from[3]);
106 :
107 0 : vlib_prefetch_buffer_header (p2, STORE);
108 0 : vlib_prefetch_buffer_header (p3, STORE);
109 :
110 0 : CLIB_PREFETCH (p2->data, sizeof (hdr0[0]), STORE);
111 0 : CLIB_PREFETCH (p3->data, sizeof (hdr1[0]), STORE);
112 : }
113 :
114 0 : pi0 = to_next[0] = from[0];
115 0 : pi1 = to_next[1] = from[1];
116 :
117 0 : from += 2;
118 0 : n_left_from -= 2;
119 0 : to_next += 2;
120 0 : n_left_to_next -= 2;
121 :
122 0 : p0 = vlib_get_buffer (vm, pi0);
123 0 : p1 = vlib_get_buffer (vm, pi1);
124 :
125 0 : adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
126 0 : adj_index1 = vnet_buffer (p1)->ip.adj_index[VLIB_TX];
127 :
128 0 : adj0 = adj_get(adj_index0);
129 0 : adj1 = adj_get(adj_index1);
130 0 : hdr0 = vlib_buffer_get_current (p0);
131 0 : hdr1 = vlib_buffer_get_current (p1);
132 :
133 : /* Guess we are only writing on simple Ethernet header. */
134 0 : vnet_rewrite_two_headers (adj0[0], adj1[0], hdr0, hdr1,
135 : sizeof (ethernet_header_t));
136 :
137 0 : eth_hdr0 = (ethernet_header_t*)((u8 *)hdr0-sizeof(ethernet_header_t));
138 0 : eth_hdr0->type = clib_host_to_net_u16(ETHERNET_TYPE_NSH);
139 0 : eth_hdr1 = (ethernet_header_t*)((u8 *)hdr1-sizeof(ethernet_header_t));
140 0 : eth_hdr1->type = clib_host_to_net_u16(ETHERNET_TYPE_NSH);
141 :
142 : /* Update packet buffer attributes/set output interface. */
143 0 : rw_len0 = adj0[0].rewrite_header.data_bytes;
144 0 : rw_len1 = adj1[0].rewrite_header.data_bytes;
145 0 : pkt_len0 = vlib_buffer_length_in_chain (vm, p0);
146 0 : pkt_len1 = vlib_buffer_length_in_chain (vm, p1);
147 :
148 : /* Bump the adj counters for packet and bytes */
149 0 : if (adj_index0 == adj_index1)
150 : {
151 0 : vlib_increment_combined_counter (&adjacency_counters, thread_index, adj_index0, 2,
152 0 : pkt_len0 + rw_len0 + pkt_len1 + rw_len1);
153 : }
154 : else
155 : {
156 0 : vlib_increment_combined_counter (&adjacency_counters, thread_index, adj_index0, 1,
157 0 : pkt_len0 + rw_len0);
158 0 : vlib_increment_combined_counter (&adjacency_counters, thread_index, adj_index1, 1,
159 0 : pkt_len1 + rw_len1);
160 : }
161 : /* Check MTU of outgoing interface. */
162 0 : if (PREDICT_TRUE(pkt_len0 <=
163 : adj0[0].rewrite_header.max_l3_packet_bytes))
164 : {
165 0 : vlib_buffer_advance (p0, -rw_len0);
166 :
167 0 : vnet_buffer (p0)->sw_if_index[VLIB_TX] =
168 0 : adj0[0].rewrite_header.sw_if_index;
169 0 : next0 = NSH_OUTPUT_NEXT_INTERFACE;
170 0 : error0 = IP4_ERROR_NONE;
171 :
172 0 : if (PREDICT_FALSE(adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
173 0 : vnet_feature_arc_start (nm->output_feature_arc_index,
174 : adj0[0].rewrite_header.sw_if_index,
175 : &next0, p0);
176 : }
177 : else
178 : {
179 0 : error0 = IP4_ERROR_MTU_EXCEEDED;
180 0 : next0 = NSH_OUTPUT_NEXT_DROP;
181 : }
182 0 : if (PREDICT_TRUE(pkt_len1 <=
183 : adj1[0].rewrite_header.max_l3_packet_bytes))
184 : {
185 0 : vlib_buffer_advance (p1, -rw_len1);
186 :
187 0 : vnet_buffer (p1)->sw_if_index[VLIB_TX] =
188 0 : adj1[0].rewrite_header.sw_if_index;
189 0 : next1 = NSH_OUTPUT_NEXT_INTERFACE;
190 0 : error1 = IP4_ERROR_NONE;
191 :
192 0 : if (PREDICT_FALSE(adj1[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
193 0 : vnet_feature_arc_start (nm->output_feature_arc_index,
194 : adj1[0].rewrite_header.sw_if_index,
195 : &next1, p1);
196 : }
197 : else
198 : {
199 0 : error1 = IP4_ERROR_MTU_EXCEEDED;
200 0 : next1 = NSH_OUTPUT_NEXT_DROP;
201 : }
202 0 : if (is_midchain)
203 : {
204 0 : adj0->sub_type.midchain.fixup_func
205 : (vm, adj0, p0, adj0->sub_type.midchain.fixup_data);
206 0 : adj1->sub_type.midchain.fixup_func
207 : (vm, adj1, p1, adj1->sub_type.midchain.fixup_data);
208 : }
209 :
210 0 : p0->error = error_node->errors[error0];
211 0 : p1->error = error_node->errors[error1];
212 :
213 0 : if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED))
214 : {
215 0 : nsh_output_trace_t *tr = vlib_add_trace (vm, node,
216 : p0, sizeof (*tr));
217 0 : tr->adj_index = vnet_buffer(p0)->ip.adj_index[VLIB_TX];
218 0 : tr->flow_hash = vnet_buffer(p0)->ip.flow_hash;
219 : }
220 0 : if (PREDICT_FALSE(p1->flags & VLIB_BUFFER_IS_TRACED))
221 : {
222 0 : nsh_output_trace_t *tr = vlib_add_trace (vm, node,
223 : p1, sizeof (*tr));
224 0 : tr->adj_index = vnet_buffer(p1)->ip.adj_index[VLIB_TX];
225 0 : tr->flow_hash = vnet_buffer(p1)->ip.flow_hash;
226 : }
227 :
228 0 : vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
229 : to_next, n_left_to_next,
230 : pi0, pi1, next0, next1);
231 : }
232 :
233 0 : while (n_left_from > 0 && n_left_to_next > 0)
234 : {
235 : ip_adjacency_t * adj0;
236 : nsh_base_header_t *hdr0;
237 : ethernet_header_t * eth_hdr0;
238 : vlib_buffer_t * p0;
239 : u32 pi0, adj_index0, next0, error0;
240 : int pkt_len0;
241 : word rw_len0;
242 :
243 0 : pi0 = to_next[0] = from[0];
244 :
245 0 : p0 = vlib_get_buffer (vm, pi0);
246 :
247 0 : adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
248 :
249 0 : adj0 = adj_get(adj_index0);
250 0 : hdr0 = vlib_buffer_get_current (p0);
251 :
252 : /* Guess we are only writing on simple Ethernet header. */
253 0 : vnet_rewrite_one_header (adj0[0], hdr0,
254 : sizeof (ethernet_header_t));
255 :
256 0 : eth_hdr0 = (ethernet_header_t*)((u8 *)hdr0-sizeof(ethernet_header_t));
257 0 : eth_hdr0->type = clib_host_to_net_u16(ETHERNET_TYPE_NSH);
258 :
259 : /* Update packet buffer attributes/set output interface. */
260 0 : rw_len0 = adj0[0].rewrite_header.data_bytes;
261 0 : pkt_len0 = vlib_buffer_length_in_chain (vm, p0);
262 :
263 0 : vlib_increment_combined_counter (&adjacency_counters, thread_index, adj_index0, 1,
264 0 : pkt_len0 + rw_len0);
265 :
266 : /* Check MTU of outgoing interface. */
267 0 : if (PREDICT_TRUE(pkt_len0 <= adj0[0].rewrite_header.max_l3_packet_bytes))
268 : {
269 0 : vlib_buffer_advance (p0, -rw_len0);
270 :
271 0 : vnet_buffer (p0)->sw_if_index[VLIB_TX] =
272 0 : adj0[0].rewrite_header.sw_if_index;
273 0 : next0 = NSH_OUTPUT_NEXT_INTERFACE;
274 0 : error0 = IP4_ERROR_NONE;
275 :
276 0 : if (PREDICT_FALSE(adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
277 0 : vnet_feature_arc_start (nm->output_feature_arc_index,
278 : adj0[0].rewrite_header.sw_if_index,
279 : &next0, p0);
280 : }
281 : else
282 : {
283 0 : error0 = IP4_ERROR_MTU_EXCEEDED;
284 0 : next0 = NSH_OUTPUT_NEXT_DROP;
285 : }
286 0 : if (is_midchain)
287 : {
288 0 : adj0->sub_type.midchain.fixup_func
289 : (vm, adj0, p0, adj0->sub_type.midchain.fixup_data);
290 : }
291 :
292 0 : p0->error = error_node->errors[error0];
293 :
294 0 : from += 1;
295 0 : n_left_from -= 1;
296 0 : to_next += 1;
297 0 : n_left_to_next -= 1;
298 :
299 0 : if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED))
300 : {
301 0 : nsh_output_trace_t *tr = vlib_add_trace (vm, node,
302 : p0, sizeof (*tr));
303 0 : tr->adj_index = vnet_buffer(p0)->ip.adj_index[VLIB_TX];
304 0 : tr->flow_hash = vnet_buffer(p0)->ip.flow_hash;
305 : }
306 :
307 0 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
308 : to_next, n_left_to_next,
309 : pi0, next0);
310 : }
311 :
312 0 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
313 : }
314 :
315 0 : return from_frame->n_vectors;
316 : }
317 :
318 : typedef enum nsh_midchain_next_t_
319 : {
320 : NSH_MIDCHAIN_NEXT_DROP,
321 : } nsh_midchain_next_t;
322 :
323 2300 : VLIB_NODE_FN (nsh_eth_output_node) (vlib_main_t * vm,
324 : vlib_node_runtime_t * node,
325 : vlib_frame_t * from_frame)
326 : {
327 0 : return (nsh_output_inline(vm, node, from_frame, /* is_midchain */ 0));
328 : }
329 :
330 51864 : VLIB_REGISTER_NODE (nsh_eth_output_node) = {
331 : .name = "nsh-eth-output",
332 : /* Takes a vector of packets. */
333 : .vector_size = sizeof (u32),
334 : .n_next_nodes = NSH_OUTPUT_N_NEXT,
335 : .next_nodes = {
336 : #define _(s,n) [NSH_OUTPUT_NEXT_##s] = n,
337 : foreach_nsh_output_next
338 : #undef _
339 : },
340 :
341 : .format_trace = format_nsh_output_trace,
342 : };
343 :
344 2300 : VLIB_NODE_FN (nsh_midchain_node) (vlib_main_t * vm,
345 : vlib_node_runtime_t * node,
346 : vlib_frame_t * from_frame)
347 : {
348 0 : return (nsh_output_inline(vm, node, from_frame, /* is_midchain */ 1));
349 : }
350 :
351 51864 : VLIB_REGISTER_NODE (nsh_midchain_node) = {
352 : .name = "nsh-midchain",
353 : .vector_size = sizeof (u32),
354 : .format_trace = format_nsh_output_trace,
355 : .n_next_nodes = 1,
356 : .next_nodes = {
357 : [NSH_MIDCHAIN_NEXT_DROP] = "error-drop",
358 : },
359 : };
360 :
361 : /* Built-in nsh tx feature path definition */
362 27647 : VNET_FEATURE_INIT (nsh_interface_output, static) = {
363 : .arc_name = "nsh-eth-output",
364 : .node_name = "interface-output",
365 : .runs_before = 0, /* not before any other features */
366 : };
367 :
368 : /* Built-in ip4 tx feature path definition */
369 : /* *INDENT-OFF* */
370 1151 : VNET_FEATURE_ARC_INIT (nsh_eth_output, static) =
371 : {
372 : .arc_name = "nsh-eth-output",
373 : .start_nodes = VNET_FEATURES ("nsh-midchain"),
374 : };
375 :
376 27647 : VNET_FEATURE_INIT (nsh_eth_tx_drop, static) =
377 : {
378 : .arc_name = "nsh-eth-output",
379 : .node_name = "error-drop",
380 : .runs_before = 0, /* not before any other features */
381 : };
382 : /* *INDENT-ON* */
383 : /**
384 : * @brief Next index values from the NSH incomplete adj node
385 : */
386 : #define foreach_nsh_adj_incomplete_next \
387 : _(DROP, "error-drop") \
388 : _(IP4, "ip4-arp") \
389 : _(IP6, "ip6-discover-neighbor")
390 :
391 : typedef enum {
392 : #define _(s,n) NSH_ADJ_INCOMPLETE_NEXT_##s,
393 : foreach_nsh_adj_incomplete_next
394 : #undef _
395 : NSH_ADJ_INCOMPLETE_N_NEXT,
396 : } nsh_adj_incomplete_next_t;
397 :
398 : /**
399 : * @brief A struct to hold tracing information for the NSH label imposition
400 : * node.
401 : */
402 : typedef struct nsh_adj_incomplete_trace_t_
403 : {
404 : u32 next;
405 : } nsh_adj_incomplete_trace_t;
406 :
407 :
408 : /**
409 : * @brief Graph node for incomplete NSH adjacency.
410 : * This node will push traffic to either the v4-arp or v6-nd node
411 : * based on the next-hop proto of the adj.
412 : * We pay a cost for this 'routing' node, but an incomplete adj is the
413 : * exception case.
414 : */
415 2300 : VLIB_NODE_FN (nsh_adj_incomplete_node) (vlib_main_t * vm,
416 : vlib_node_runtime_t * node,
417 : vlib_frame_t * from_frame)
418 : {
419 : u32 n_left_from, next_index, * from, * to_next;
420 :
421 0 : from = vlib_frame_vector_args (from_frame);
422 0 : n_left_from = from_frame->n_vectors;
423 0 : next_index = node->cached_next_index;
424 :
425 0 : while (n_left_from > 0)
426 : {
427 : u32 n_left_to_next;
428 :
429 0 : vlib_get_next_frame (vm, node, next_index,
430 : to_next, n_left_to_next);
431 :
432 0 : while (n_left_from > 0 && n_left_to_next > 0)
433 : {
434 : u32 pi0, next0, adj_index0;
435 : ip_adjacency_t * adj0;
436 : vlib_buffer_t * p0;
437 :
438 0 : pi0 = to_next[0] = from[0];
439 0 : p0 = vlib_get_buffer (vm, pi0);
440 0 : from += 1;
441 0 : n_left_from -= 1;
442 0 : to_next += 1;
443 0 : n_left_to_next -= 1;
444 :
445 0 : adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
446 :
447 0 : adj0 = adj_get(adj_index0);
448 :
449 0 : if (PREDICT_TRUE(FIB_PROTOCOL_IP4 == adj0->ia_nh_proto))
450 : {
451 0 : next0 = NSH_ADJ_INCOMPLETE_NEXT_IP4;
452 : }
453 : else
454 : {
455 0 : next0 = NSH_ADJ_INCOMPLETE_NEXT_IP6;
456 : }
457 :
458 0 : if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED))
459 : {
460 : nsh_adj_incomplete_trace_t *tr =
461 0 : vlib_add_trace (vm, node, p0, sizeof (*tr));
462 0 : tr->next = next0;
463 : }
464 :
465 0 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
466 : to_next, n_left_to_next,
467 : pi0, next0);
468 : }
469 :
470 0 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
471 : }
472 :
473 0 : return from_frame->n_vectors;
474 : }
475 :
476 : static u8 *
477 0 : format_nsh_adj_incomplete_trace (u8 * s, va_list * args)
478 : {
479 0 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
480 0 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
481 : nsh_adj_incomplete_trace_t * t;
482 : uword indent;
483 :
484 0 : t = va_arg (*args, nsh_adj_incomplete_trace_t *);
485 0 : indent = format_get_indent (s);
486 :
487 0 : s = format (s, "%Unext:%d",
488 : format_white_space, indent,
489 : t->next);
490 0 : return (s);
491 : }
492 :
493 51864 : VLIB_REGISTER_NODE (nsh_adj_incomplete_node) = {
494 : .name = "nsh-adj-incomplete",
495 : .format_trace = format_nsh_adj_incomplete_trace,
496 : /* Takes a vector of packets. */
497 : .vector_size = sizeof (u32),
498 : .n_next_nodes = NSH_ADJ_INCOMPLETE_N_NEXT,
499 : .next_nodes = {
500 : #define _(s,n) [NSH_ADJ_INCOMPLETE_NEXT_##s] = n,
501 : foreach_nsh_adj_incomplete_next
502 : #undef _
503 : },
504 : };
505 :
|