Line data Source code
1 : /*
2 : * Copyright (c) 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 : #include <vnet/adj/adj_nbr.h>
17 : #include <vnet/adj/adj_internal.h>
18 : #include <vnet/adj/adj_l2.h>
19 : #include <vnet/adj/adj_nsh.h>
20 : #include <vnet/adj/adj_midchain.h>
21 : #include <vnet/dpo/drop_dpo.h>
22 : #include <vnet/dpo/load_balance.h>
23 : #include <vnet/fib/fib_walk.h>
24 : #include <vnet/fib/fib_entry.h>
25 : #include <vnet/ip/ip4_inlines.h>
26 : #include <vnet/ip/ip6_inlines.h>
27 :
28 : u8
29 46303 : adj_is_midchain (adj_index_t ai)
30 : {
31 : ip_adjacency_t *adj;
32 :
33 46303 : adj = adj_get(ai);
34 :
35 46303 : switch (adj->lookup_next_index)
36 : {
37 463 : case IP_LOOKUP_NEXT_MIDCHAIN:
38 : case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
39 463 : return (1);
40 45840 : case IP_LOOKUP_NEXT_ARP:
41 : case IP_LOOKUP_NEXT_GLEAN:
42 : case IP_LOOKUP_NEXT_BCAST:
43 : case IP_LOOKUP_NEXT_MCAST:
44 : case IP_LOOKUP_NEXT_DROP:
45 : case IP_LOOKUP_NEXT_PUNT:
46 : case IP_LOOKUP_NEXT_LOCAL:
47 : case IP_LOOKUP_NEXT_REWRITE:
48 : case IP_LOOKUP_NEXT_ICMP_ERROR:
49 : case IP_LOOKUP_N_NEXT:
50 45840 : return (0);
51 : }
52 :
53 0 : return (0);
54 : }
55 :
56 : static inline u32
57 671 : adj_get_midchain_node (vnet_link_t link)
58 : {
59 671 : switch (link) {
60 419 : case VNET_LINK_IP4:
61 419 : return (ip4_midchain_node.index);
62 227 : case VNET_LINK_IP6:
63 227 : return (ip6_midchain_node.index);
64 9 : case VNET_LINK_MPLS:
65 9 : return (mpls_midchain_node.index);
66 16 : case VNET_LINK_ETHERNET:
67 16 : return (adj_l2_midchain_node.index);
68 0 : case VNET_LINK_NSH:
69 0 : return (adj_nsh_midchain_node.index);
70 0 : case VNET_LINK_ARP:
71 0 : break;
72 : }
73 0 : ASSERT(0);
74 0 : return (0);
75 : }
76 :
77 : static u8
78 972 : adj_midchain_get_feature_arc_index (const ip_adjacency_t *adj)
79 : {
80 972 : switch (adj->ia_link)
81 : {
82 573 : case VNET_LINK_IP4:
83 573 : return ip4_main.lookup_main.output_feature_arc_index;
84 360 : case VNET_LINK_IP6:
85 360 : return ip6_main.lookup_main.output_feature_arc_index;
86 11 : case VNET_LINK_MPLS:
87 11 : return mpls_main.output_feature_arc_index;
88 28 : case VNET_LINK_ETHERNET:
89 28 : return ethernet_main.output_feature_arc_index;
90 0 : case VNET_LINK_NSH:
91 : case VNET_LINK_ARP:
92 0 : break;
93 : }
94 0 : ASSERT (0);
95 0 : return (0);
96 : }
97 :
98 : static u32
99 1236 : adj_nbr_midchain_get_tx_node (ip_adjacency_t *adj)
100 : {
101 1236 : return (adj_midchain_tx.index);
102 : }
103 :
104 : static u32
105 972 : adj_nbr_midchain_get_next_node (ip_adjacency_t *adj)
106 : {
107 972 : return (vnet_feature_get_end_node(adj_midchain_get_feature_arc_index(adj),
108 : adj->rewrite_header.sw_if_index));
109 : }
110 :
111 : /**
112 : * adj_midchain_setup
113 : *
114 : * Setup the adj as a mid-chain
115 : */
116 : void
117 477 : adj_midchain_teardown (ip_adjacency_t *adj)
118 : {
119 477 : dpo_reset(&adj->sub_type.midchain.next_dpo);
120 477 : }
121 :
122 : /**
123 : * adj_midchain_setup
124 : *
125 : * Setup the adj as a mid-chain
126 : */
127 : void
128 479 : adj_midchain_setup (adj_index_t adj_index,
129 : adj_midchain_fixup_t fixup,
130 : const void *data,
131 : adj_flags_t flags)
132 : {
133 : ip_adjacency_t *adj;
134 :
135 479 : ASSERT(ADJ_INDEX_INVALID != adj_index);
136 :
137 479 : adj = adj_get(adj_index);
138 :
139 479 : adj->sub_type.midchain.fixup_func = fixup;
140 479 : adj->sub_type.midchain.fixup_data = data;
141 479 : adj->sub_type.midchain.fei = FIB_NODE_INDEX_INVALID;
142 479 : adj->ia_flags |= flags;
143 :
144 479 : if (flags & ADJ_FLAG_MIDCHAIN_FIXUP_IP4O4_HDR)
145 : {
146 54 : adj->rewrite_header.flags |= VNET_REWRITE_FIXUP_IP4_O_4;
147 : }
148 : else
149 : {
150 425 : adj->rewrite_header.flags &= ~VNET_REWRITE_FIXUP_IP4_O_4;
151 : }
152 479 : if (!(flags & ADJ_FLAG_MIDCHAIN_FIXUP_FLOW_HASH))
153 : {
154 479 : adj->rewrite_header.flags &= ~VNET_REWRITE_FIXUP_FLOW_HASH;
155 : }
156 :
157 : /*
158 : * stack the midchain on the drop so it's ready to forward in the adj-midchain-tx.
159 : * The graph arc used/created here is from the midchain-tx node to the
160 : * child's registered node. This is because post adj processing the next
161 : * node are any output features, then the midchain-tx. from there we
162 : * need to get to the stacked child's node.
163 : */
164 479 : dpo_stack_from_node(adj_nbr_midchain_get_tx_node(adj),
165 : &adj->sub_type.midchain.next_dpo,
166 479 : drop_dpo_get(vnet_link_to_dpo_proto(adj->ia_link)));
167 479 : }
168 :
169 : /**
170 : * adj_nbr_midchain_update_rewrite
171 : *
172 : * Update the adjacency's rewrite string. A NULL string implies the
173 : * rewrite is reset (i.e. when ARP/ND entry is gone).
174 : * NB: the adj being updated may be handling traffic in the DP.
175 : */
176 : void
177 671 : adj_nbr_midchain_update_rewrite (adj_index_t adj_index,
178 : adj_midchain_fixup_t fixup,
179 : const void *fixup_data,
180 : adj_flags_t flags,
181 : u8 *rewrite)
182 : {
183 : ip_adjacency_t *adj;
184 :
185 671 : ASSERT(ADJ_INDEX_INVALID != adj_index);
186 :
187 671 : adj = adj_get(adj_index);
188 :
189 : /*
190 : * one time only update. since we don't support changing the tunnel
191 : * src,dst, this is all we need.
192 : */
193 671 : if (adj->lookup_next_index != IP_LOOKUP_NEXT_MIDCHAIN &&
194 479 : adj->lookup_next_index != IP_LOOKUP_NEXT_MCAST_MIDCHAIN)
195 : {
196 479 : adj_midchain_setup(adj_index, fixup, fixup_data, flags);
197 : }
198 :
199 : /*
200 : * update the rewrite with the workers paused.
201 : */
202 1342 : adj_nbr_update_rewrite_internal(adj,
203 : IP_LOOKUP_NEXT_MIDCHAIN,
204 671 : adj_get_midchain_node(adj->ia_link),
205 : adj_nbr_midchain_get_next_node(adj),
206 : rewrite);
207 671 : }
208 :
209 : void
210 369 : adj_nbr_midchain_update_next_node (adj_index_t adj_index,
211 : u32 next_node)
212 : {
213 : ip_adjacency_t *adj;
214 : vlib_main_t * vm;
215 :
216 369 : ASSERT(ADJ_INDEX_INVALID != adj_index);
217 :
218 369 : adj = adj_get(adj_index);
219 369 : vm = vlib_get_main();
220 :
221 369 : vlib_worker_thread_barrier_sync(vm);
222 :
223 369 : adj->rewrite_header.next_index = vlib_node_add_next(vlib_get_main(),
224 369 : adj->ia_node_index,
225 : next_node);
226 :
227 369 : vlib_worker_thread_barrier_release(vm);
228 369 : }
229 :
230 : void
231 301 : adj_nbr_midchain_reset_next_node (adj_index_t adj_index)
232 : {
233 : ip_adjacency_t *adj;
234 : vlib_main_t * vm;
235 :
236 301 : ASSERT(ADJ_INDEX_INVALID != adj_index);
237 :
238 301 : adj = adj_get(adj_index);
239 301 : vm = vlib_get_main();
240 :
241 301 : vlib_worker_thread_barrier_sync(vm);
242 :
243 301 : adj->rewrite_header.next_index =
244 301 : vlib_node_add_next(vlib_get_main(),
245 301 : adj->ia_node_index,
246 301 : adj_nbr_midchain_get_next_node(adj));
247 :
248 301 : vlib_worker_thread_barrier_release(vm);
249 301 : }
250 :
251 : /**
252 : * adj_nbr_midchain_unstack
253 : *
254 : * Unstack the adj. stack it on drop
255 : */
256 : void
257 387 : adj_nbr_midchain_unstack (adj_index_t adj_index)
258 : {
259 : fib_node_index_t *entry_indicies, tmp;
260 : ip_adjacency_t *adj;
261 :
262 387 : ASSERT(ADJ_INDEX_INVALID != adj_index);
263 387 : adj = adj_get (adj_index);
264 :
265 : /*
266 : * check to see if this unstacking breaks a recursion loop
267 : */
268 387 : entry_indicies = NULL;
269 387 : tmp = adj->sub_type.midchain.fei;
270 387 : adj->sub_type.midchain.fei = FIB_NODE_INDEX_INVALID;
271 :
272 387 : if (FIB_NODE_INDEX_INVALID != tmp)
273 : {
274 303 : fib_entry_recursive_loop_detect(tmp, &entry_indicies);
275 303 : vec_free(entry_indicies);
276 : }
277 :
278 : /*
279 : * stack on the drop
280 : */
281 774 : dpo_stack(DPO_ADJACENCY_MIDCHAIN,
282 387 : vnet_link_to_dpo_proto(adj->ia_link),
283 : &adj->sub_type.midchain.next_dpo,
284 387 : drop_dpo_get(vnet_link_to_dpo_proto(adj->ia_link)));
285 387 : CLIB_MEMORY_BARRIER();
286 387 : }
287 :
288 : void
289 697 : adj_nbr_midchain_stack_on_fib_entry (adj_index_t ai,
290 : fib_node_index_t fei,
291 : fib_forward_chain_type_t fct)
292 : {
293 : fib_node_index_t *entry_indicies;
294 697 : dpo_id_t tmp = DPO_INVALID;
295 : ip_adjacency_t *adj;
296 :
297 697 : adj = adj_get (ai);
298 :
299 : /*
300 : * check to see if this stacking will form a recursion loop
301 : */
302 697 : entry_indicies = NULL;
303 697 : adj->sub_type.midchain.fei = fei;
304 :
305 697 : if (fib_entry_recursive_loop_detect(adj->sub_type.midchain.fei, &entry_indicies))
306 : {
307 : /*
308 : * loop formed, stack on the drop.
309 : */
310 1 : dpo_copy(&tmp, drop_dpo_get(fib_forw_chain_type_to_dpo_proto(fct)));
311 : }
312 : else
313 : {
314 696 : fib_entry_contribute_forwarding (fei, fct, &tmp);
315 :
316 696 : if (DPO_LOAD_BALANCE == tmp.dpoi_type)
317 : {
318 : load_balance_t *lb;
319 :
320 650 : lb = load_balance_get (tmp.dpoi_index);
321 :
322 650 : if ((adj->ia_flags & ADJ_FLAG_MIDCHAIN_IP_STACK) ||
323 130 : lb->lb_n_buckets == 1)
324 650 : {
325 : /*
326 : * do that hash now and stack on the choice.
327 : * If the choice is an incomplete adj then we will need a poke when
328 : * it becomes complete. This happens since the adj update walk propagates
329 : * as far a recursive paths.
330 : */
331 : const dpo_id_t *choice;
332 : int hash;
333 :
334 650 : if (FIB_FORW_CHAIN_TYPE_UNICAST_IP4 == fct)
335 : {
336 414 : hash = ip4_compute_flow_hash ((ip4_header_t *) adj_get_rewrite (ai),
337 : lb->lb_hash_config);
338 : }
339 236 : else if (FIB_FORW_CHAIN_TYPE_UNICAST_IP6 == fct)
340 : {
341 236 : hash = ip6_compute_flow_hash ((ip6_header_t *) adj_get_rewrite (ai),
342 : lb->lb_hash_config);
343 : }
344 : else
345 : {
346 0 : hash = 0;
347 0 : ASSERT(0);
348 : }
349 :
350 650 : choice = load_balance_get_bucket_i (lb, hash & lb->lb_n_buckets_minus_1);
351 650 : dpo_copy (&tmp, choice);
352 : }
353 0 : else if (lb->lb_n_buckets > 1)
354 : {
355 : /*
356 : * the client has chosen not to use the stacking to select a
357 : * bucket, and there are more than one buckets. there's no
358 : * value in using the midchain's fixed rewrite string to select
359 : * the path, so force a flow hash on the inner.
360 : */
361 0 : adj->rewrite_header.flags |= VNET_REWRITE_FIXUP_FLOW_HASH;
362 : }
363 :
364 650 : if (adj->ia_flags & ADJ_FLAG_MIDCHAIN_FIXUP_FLOW_HASH)
365 : {
366 : /*
367 : * The client, for reasons unbeknownst to adj, wants to force
368 : * a flow hash on the inner, we will oblige.
369 : */
370 0 : adj->rewrite_header.flags |= VNET_REWRITE_FIXUP_FLOW_HASH;
371 : }
372 : }
373 : }
374 697 : adj_nbr_midchain_stack (ai, &tmp);
375 697 : dpo_reset(&tmp);
376 697 : vec_free(entry_indicies);
377 697 : }
378 :
379 : /**
380 : * adj_nbr_midchain_stack
381 : */
382 : void
383 757 : adj_nbr_midchain_stack (adj_index_t adj_index,
384 : const dpo_id_t *next)
385 : {
386 : ip_adjacency_t *adj;
387 :
388 757 : ASSERT(ADJ_INDEX_INVALID != adj_index);
389 :
390 757 : adj = adj_get(adj_index);
391 :
392 757 : ASSERT((IP_LOOKUP_NEXT_MIDCHAIN == adj->lookup_next_index) ||
393 : (IP_LOOKUP_NEXT_MCAST_MIDCHAIN == adj->lookup_next_index));
394 :
395 757 : dpo_stack_from_node(adj_nbr_midchain_get_tx_node(adj),
396 : &adj->sub_type.midchain.next_dpo,
397 : next);
398 757 : }
399 :
400 : int
401 2130 : adj_ndr_midchain_recursive_loop_detect (adj_index_t ai,
402 : fib_node_index_t **entry_indicies)
403 : {
404 : fib_node_index_t *entry_index, *entries;
405 : ip_adjacency_t * adj;
406 :
407 2130 : adj = adj_get(ai);
408 2130 : entries = *entry_indicies;
409 :
410 2449 : vec_foreach(entry_index, entries)
411 : {
412 320 : if (*entry_index == adj->sub_type.midchain.fei)
413 : {
414 : /*
415 : * The entry this midchain links to is already in the set
416 : * of visited entries, this is a loop
417 : */
418 1 : adj->ia_flags |= ADJ_FLAG_MIDCHAIN_LOOPED;
419 1 : return (1);
420 : }
421 : }
422 :
423 2129 : adj->ia_flags &= ~ADJ_FLAG_MIDCHAIN_LOOPED;
424 2129 : return (0);
425 : }
426 :
427 : u8*
428 42682 : format_adj_midchain (u8* s, va_list *ap)
429 : {
430 42682 : index_t index = va_arg(*ap, index_t);
431 42682 : u32 indent = va_arg(*ap, u32);
432 42682 : ip_adjacency_t * adj = adj_get(index);
433 :
434 42682 : s = format (s, "%U", format_vnet_link, adj->ia_link);
435 42682 : if (adj->rewrite_header.flags & VNET_REWRITE_HAS_FEATURES)
436 6978 : s = format(s, " [features]");
437 42682 : s = format (s, " via %U",
438 : format_ip46_address, &adj->sub_type.nbr.next_hop,
439 42682 : adj_proto_to_46(adj->ia_nh_proto));
440 42682 : s = format (s, " %U",
441 : format_vnet_rewrite,
442 : &adj->rewrite_header, sizeof (adj->rewrite_data), indent);
443 42682 : s = format (s, "\n%Ustacked-on",
444 : format_white_space, indent);
445 :
446 42682 : if (FIB_NODE_INDEX_INVALID != adj->sub_type.midchain.fei)
447 : {
448 34470 : s = format (s, " entry:%d", adj->sub_type.midchain.fei);
449 :
450 : }
451 42682 : s = format (s, ":\n%U%U",
452 : format_white_space, indent+2,
453 : format_dpo_id, &adj->sub_type.midchain.next_dpo, indent+2);
454 :
455 42682 : return (s);
456 : }
457 :
458 : static void
459 5804 : adj_dpo_lock (dpo_id_t *dpo)
460 : {
461 5804 : adj_lock(dpo->dpoi_index);
462 5804 : }
463 : static void
464 5802 : adj_dpo_unlock (dpo_id_t *dpo)
465 : {
466 5802 : adj_unlock(dpo->dpoi_index);
467 5802 : }
468 :
469 : const static dpo_vft_t adj_midchain_dpo_vft = {
470 : .dv_lock = adj_dpo_lock,
471 : .dv_unlock = adj_dpo_unlock,
472 : .dv_format = format_adj_midchain,
473 : .dv_get_urpf = adj_dpo_get_urpf,
474 : .dv_get_mtu = adj_dpo_get_mtu,
475 : };
476 :
477 : /**
478 : * @brief The per-protocol VLIB graph nodes that are assigned to a midchain
479 : * object.
480 : *
481 : * this means that these graph nodes are ones from which a midchain is the
482 : * parent object in the DPO-graph.
483 : */
484 : const static char* const midchain_ip4_nodes[] =
485 : {
486 : "ip4-midchain",
487 : NULL,
488 : };
489 : const static char* const midchain_ip6_nodes[] =
490 : {
491 : "ip6-midchain",
492 : NULL,
493 : };
494 : const static char* const midchain_mpls_nodes[] =
495 : {
496 : "mpls-midchain",
497 : NULL,
498 : };
499 : const static char* const midchain_ethernet_nodes[] =
500 : {
501 : "adj-l2-midchain",
502 : NULL,
503 : };
504 : const static char* const midchain_nsh_nodes[] =
505 : {
506 : "adj-nsh-midchain",
507 : NULL,
508 : };
509 :
510 : const static char* const * const midchain_nodes[DPO_PROTO_NUM] =
511 : {
512 : [DPO_PROTO_IP4] = midchain_ip4_nodes,
513 : [DPO_PROTO_IP6] = midchain_ip6_nodes,
514 : [DPO_PROTO_MPLS] = midchain_mpls_nodes,
515 : [DPO_PROTO_ETHERNET] = midchain_ethernet_nodes,
516 : [DPO_PROTO_NSH] = midchain_nsh_nodes,
517 : };
518 :
519 : void
520 559 : adj_midchain_module_init (void)
521 : {
522 559 : dpo_register(DPO_ADJACENCY_MIDCHAIN, &adj_midchain_dpo_vft, midchain_nodes);
523 559 : }
|