Line data Source code
1 : /*
2 : * node.c
3 : *
4 : * Copyright (c) 2015 Cisco 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/vnet.h>
20 : #include <vppinfra/error.h>
21 : #include <srv6-ad-flow/ad-flow.h>
22 :
23 : /****************************** Packet tracing ******************************/
24 :
25 : typedef struct
26 : {
27 : u32 localsid_index;
28 : } srv6_ad_flow_localsid_trace_t;
29 :
30 : typedef struct
31 : {
32 : u8 error;
33 : ip6_address_t src, dst;
34 : } srv6_ad_flow_rewrite_trace_t;
35 :
36 : static u8 *
37 0 : format_srv6_ad_flow_localsid_trace (u8 *s, va_list *args)
38 : {
39 0 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
40 0 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
41 0 : srv6_ad_flow_localsid_trace_t *t =
42 : va_arg (*args, srv6_ad_flow_localsid_trace_t *);
43 :
44 0 : return format (s, "SRv6-AD-Flow-localsid: localsid_index %d",
45 : t->localsid_index);
46 : }
47 :
48 : static u8 *
49 6 : format_srv6_ad_flow_rewrite_trace (u8 *s, va_list *args)
50 : {
51 6 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
52 6 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
53 6 : srv6_ad_flow_rewrite_trace_t *t =
54 : va_arg (*args, srv6_ad_flow_rewrite_trace_t *);
55 :
56 6 : if (PREDICT_FALSE (t->error != 0))
57 : {
58 0 : return format (s, "SRv6-AD-Flow-rewrite: cache is empty");
59 : }
60 :
61 6 : return format (s, "SRv6-AD-Flow-rewrite: src %U dst %U", format_ip6_address,
62 : &t->src, format_ip6_address, &t->dst);
63 : }
64 :
65 : /**************************** Nodes registration *****************************/
66 :
67 : vlib_node_registration_t srv6_ad4_flow_rewrite_node;
68 : vlib_node_registration_t srv6_ad6_flow_rewrite_node;
69 :
70 : /****************************** Packet counters ******************************/
71 :
72 : #define foreach_srv6_ad_flow_rewrite_counter \
73 : _ (PROCESSED, "srv6-ad-flow rewritten packets") \
74 : _ (NO_RW, "(Error) No header for rewriting.")
75 :
76 : typedef enum
77 : {
78 : #define _(sym, str) SRV6_AD_FLOW_REWRITE_COUNTER_##sym,
79 : foreach_srv6_ad_flow_rewrite_counter
80 : #undef _
81 : SRV6_AD_FLOW_REWRITE_N_COUNTERS,
82 : } srv6_ad_flow_rewrite_counters;
83 :
84 : static char *srv6_ad_flow_rewrite_counter_strings[] = {
85 : #define _(sym, string) string,
86 : foreach_srv6_ad_flow_rewrite_counter
87 : #undef _
88 : };
89 :
90 : /******************************** Next nodes *********************************/
91 :
92 : typedef enum
93 : {
94 : SRV6_AD_FLOW_LOCALSID_NEXT_ERROR,
95 : SRV6_AD_FLOW_LOCALSID_NEXT_REWRITE4,
96 : SRV6_AD_FLOW_LOCALSID_NEXT_REWRITE6,
97 : SRV6_AD_FLOW_LOCALSID_NEXT_BYPASS,
98 : SRV6_AD_FLOW_LOCALSID_NEXT_PUNT,
99 : SRV6_AD_FLOW_LOCALSID_N_NEXT,
100 : } srv6_ad_flow_localsid_next_t;
101 :
102 : typedef enum
103 : {
104 : SRV6_AD_FLOW_REWRITE_NEXT_ERROR,
105 : SRV6_AD_FLOW_REWRITE_NEXT_LOOKUP,
106 : SRV6_AD_FLOW_REWRITE_N_NEXT,
107 : } srv6_ad_flow_rewrite_next_t;
108 :
109 : /***************************** Inline functions ******************************/
110 :
111 : static_always_inline int
112 2 : ad_flow_lru_insert (srv6_ad_flow_localsid_t *ls, srv6_ad_flow_entry_t *e,
113 : f64 now)
114 : {
115 : dlist_elt_t *lru_list_elt;
116 2 : pool_get (ls->lru_pool, lru_list_elt);
117 2 : e->lru_index = lru_list_elt - ls->lru_pool;
118 2 : clib_dlist_addtail (ls->lru_pool, ls->lru_head_index, e->lru_index);
119 2 : lru_list_elt->value = e - ls->cache;
120 2 : e->last_lru_update = now;
121 2 : return 1;
122 : }
123 :
124 : always_inline void
125 6 : ad_flow_entry_update_lru (srv6_ad_flow_localsid_t *ls, srv6_ad_flow_entry_t *e)
126 : {
127 : /* don't update too often - timeout is in magnitude of seconds anyway */
128 6 : if (e->last_heard > e->last_lru_update + 1)
129 : {
130 0 : clib_dlist_remove (ls->lru_pool, e->lru_index);
131 0 : clib_dlist_addtail (ls->lru_pool, ls->lru_head_index, e->lru_index);
132 0 : e->last_lru_update = e->last_heard;
133 : }
134 6 : }
135 :
136 : always_inline void
137 0 : ad_flow_entry_delete (srv6_ad_flow_localsid_t *ls, srv6_ad_flow_entry_t *e,
138 : int lru_delete)
139 : {
140 : clib_bihash_kv_40_8_t kv;
141 :
142 0 : if (ls->inner_type == AD_TYPE_IP4)
143 : {
144 0 : kv.key[0] = ((u64) e->key.s_addr.ip4.as_u32 << 32) |
145 0 : (u64) e->key.d_addr.ip4.as_u32;
146 0 : kv.key[1] = ((u64) e->key.s_port << 16) | ((u64) e->key.d_port);
147 0 : kv.key[2] = 0;
148 0 : kv.key[3] = 0;
149 0 : kv.key[4] = 0;
150 : }
151 : else
152 : {
153 0 : kv.key[0] = e->key.s_addr.ip6.as_u64[0];
154 0 : kv.key[1] = e->key.s_addr.ip6.as_u64[1];
155 0 : kv.key[2] = e->key.d_addr.ip6.as_u64[0];
156 0 : kv.key[3] = e->key.d_addr.ip6.as_u64[1];
157 0 : kv.key[4] = ((u64) e->key.s_port << 16) | ((u64) e->key.d_port);
158 : }
159 :
160 0 : clib_bihash_add_del_40_8 (&ls->ftable, &kv, 0);
161 :
162 0 : vec_free (e->rw_data);
163 :
164 0 : if (lru_delete)
165 : {
166 0 : clib_dlist_remove (ls->lru_pool, e->lru_index);
167 : }
168 0 : pool_put_index (ls->lru_pool, e->lru_index);
169 0 : pool_put (ls->cache, e);
170 0 : }
171 :
172 : static_always_inline int
173 2 : ad_flow_lru_free_one (srv6_ad_flow_localsid_t *ls, f64 now)
174 : {
175 2 : srv6_ad_flow_entry_t *e = NULL;
176 : dlist_elt_t *oldest_elt;
177 : f64 entry_timeout_time;
178 : u32 oldest_index;
179 2 : oldest_index = clib_dlist_remove_head (ls->lru_pool, ls->lru_head_index);
180 2 : if (~0 != oldest_index)
181 : {
182 0 : oldest_elt = pool_elt_at_index (ls->lru_pool, oldest_index);
183 0 : e = pool_elt_at_index (ls->cache, oldest_elt->value);
184 :
185 0 : entry_timeout_time = e->last_heard + (f64) SRV6_AD_CACHE_TIMEOUT;
186 0 : if (now >= entry_timeout_time)
187 : {
188 0 : ad_flow_entry_delete (ls, e, 0);
189 0 : return 1;
190 : }
191 : else
192 : {
193 0 : clib_dlist_addhead (ls->lru_pool, ls->lru_head_index, oldest_index);
194 : }
195 : }
196 2 : return 0;
197 : }
198 :
199 : static_always_inline srv6_ad_flow_entry_t *
200 2 : ad_flow_entry_alloc (srv6_ad_flow_localsid_t *ls, f64 now)
201 : {
202 : srv6_ad_flow_entry_t *e;
203 :
204 2 : ad_flow_lru_free_one (ls, now);
205 :
206 2 : pool_get (ls->cache, e);
207 2 : clib_memset (e, 0, sizeof *e);
208 :
209 2 : ad_flow_lru_insert (ls, e, now);
210 :
211 2 : return e;
212 : }
213 :
214 : always_inline u32
215 10 : ad_flow_value_get_session_index (clib_bihash_kv_40_8_t *value)
216 : {
217 10 : return value->value & ~(u32) 0;
218 : }
219 :
220 : int
221 0 : ad_flow_is_idle_entry_cb (clib_bihash_kv_40_8_t *kv, void *arg)
222 : {
223 0 : srv6_ad_is_idle_entry_ctx_t *ctx = arg;
224 : srv6_ad_flow_entry_t *e;
225 : u64 entry_timeout_time;
226 0 : srv6_ad_flow_localsid_t *ls = ctx->ls;
227 :
228 0 : e = pool_elt_at_index (ls->cache, ad_flow_value_get_session_index (kv));
229 0 : entry_timeout_time = e->last_heard + (f64) SRV6_AD_CACHE_TIMEOUT;
230 0 : if (ctx->now >= entry_timeout_time)
231 : {
232 0 : ad_flow_entry_delete (ls, e, 1);
233 0 : return 1;
234 : }
235 0 : return 0;
236 : }
237 :
238 : /****************************** Local SID node *******************************/
239 :
240 : /**
241 : * @brief Function doing SRH processing for AD behavior
242 : */
243 : static_always_inline int
244 12 : end_ad_flow_walk_expect_first_hdr (vlib_main_t *vm, vlib_buffer_t *b,
245 : ip6_ext_header_t *first_hdr,
246 : u8 first_hdr_type, u8 expected_hdr_type,
247 : u32 *encap_length, u8 **found_hdr)
248 : {
249 12 : if (PREDICT_TRUE (first_hdr_type == expected_hdr_type))
250 : {
251 12 : *found_hdr = (void *) first_hdr;
252 : }
253 : else
254 : {
255 0 : u8 ext_hdr_type = first_hdr_type;
256 0 : ip6_ext_header_t *ext_hdr = first_hdr;
257 :
258 0 : if (!ip6_ext_hdr (ext_hdr_type))
259 : {
260 0 : *found_hdr = NULL;
261 0 : return -1;
262 : }
263 :
264 0 : u32 ext_hdr_length = ip6_ext_header_len (ext_hdr);
265 0 : if (!vlib_object_within_buffer_data (vm, b, ext_hdr, ext_hdr_length))
266 : {
267 0 : *found_hdr = NULL;
268 0 : return -2;
269 : }
270 0 : *encap_length += ext_hdr_length;
271 0 : ext_hdr_type = ext_hdr->next_hdr;
272 :
273 0 : while (ext_hdr_type != expected_hdr_type && ip6_ext_hdr (ext_hdr_type))
274 : {
275 0 : ext_hdr = ip6_ext_next_header (ext_hdr);
276 0 : ext_hdr_length = ip6_ext_header_len (ext_hdr);
277 0 : if (!vlib_object_within_buffer_data (vm, b, ext_hdr, ext_hdr_length))
278 : {
279 0 : *found_hdr = NULL;
280 0 : return -2;
281 : }
282 0 : *encap_length += ext_hdr_length;
283 0 : ext_hdr_type = ext_hdr->next_hdr;
284 : }
285 :
286 0 : if (ext_hdr_type != expected_hdr_type)
287 : {
288 0 : *found_hdr = NULL;
289 0 : return -1;
290 : }
291 :
292 0 : *found_hdr = ip6_ext_next_header (ext_hdr);
293 : }
294 :
295 12 : return 0;
296 : }
297 :
298 : /**
299 : * @brief Function doing SRH processing for per-flow AD behavior (IPv6 inner
300 : * traffic)
301 : */
302 : static_always_inline void
303 3 : end_ad_flow_processing_v6 (vlib_main_t *vm, vlib_buffer_t *b, ip6_header_t *ip,
304 : srv6_ad_flow_localsid_t *ls_mem, u32 *next,
305 : vlib_combined_counter_main_t **cnt, u32 *cnt_idx,
306 : f64 now)
307 : {
308 3 : ip6_sr_main_t *srm = &sr_main;
309 3 : srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
310 : ip6_address_t *new_dst;
311 3 : u32 encap_length = sizeof (ip6_header_t);
312 : ip6_sr_header_t *srh;
313 3 : clib_bihash_40_8_t *h = &ls_mem->ftable;
314 3 : ip6_header_t *ulh = NULL;
315 3 : u16 src_port = 0, dst_port = 0;
316 3 : srv6_ad_flow_entry_t *e = NULL;
317 : clib_bihash_kv_40_8_t kv, value;
318 : srv6_ad_is_idle_entry_ctx_t ctx;
319 :
320 : /* Find SRH in the extension header chain */
321 3 : end_ad_flow_walk_expect_first_hdr (vm, b, (void *) (ip + 1), ip->protocol,
322 : IP_PROTOCOL_IPV6_ROUTE, &encap_length,
323 : (u8 **) &srh);
324 :
325 : /* Punt the packet if no SRH or SRH with SL = 0 */
326 3 : if (PREDICT_FALSE (srh == NULL || srh->type != ROUTING_HEADER_TYPE_SR ||
327 : srh->segments_left == 0))
328 : {
329 0 : *next = SRV6_AD_FLOW_LOCALSID_NEXT_PUNT;
330 0 : *cnt = &(sm->sid_punt_counters);
331 0 : *cnt_idx = ls_mem->index;
332 0 : return;
333 : }
334 :
335 : /* Decrement Segments Left and update Destination Address */
336 3 : srh->segments_left -= 1;
337 3 : new_dst = (ip6_address_t *) (srh->segments) + srh->segments_left;
338 3 : ip->dst_address.as_u64[0] = new_dst->as_u64[0];
339 3 : ip->dst_address.as_u64[1] = new_dst->as_u64[1];
340 :
341 : /* Compute the total encapsulation size and determine ULH type */
342 3 : encap_length += ip6_ext_header_len ((ip6_ext_header_t *) srh);
343 :
344 : /* Find the inner IPv6 header (ULH) */
345 3 : int ret = end_ad_flow_walk_expect_first_hdr (
346 3 : vm, b, ip6_ext_next_header ((ip6_ext_header_t *) srh), srh->protocol,
347 : IP_PROTOCOL_IPV6, &encap_length, (u8 **) &ulh);
348 :
349 3 : if (PREDICT_FALSE (ulh == NULL))
350 : {
351 0 : if (ret == -1) /* Bypass the NF if ULH is not of expected type */
352 : {
353 0 : *next = SRV6_AD_FLOW_LOCALSID_NEXT_BYPASS;
354 0 : *cnt = &(sm->sid_bypass_counters);
355 0 : *cnt_idx = ls_mem->index;
356 : }
357 : else
358 : {
359 0 : *next = SRV6_AD_FLOW_LOCALSID_NEXT_ERROR;
360 0 : *cnt = &(srm->sr_ls_invalid_counters);
361 : }
362 0 : return;
363 : }
364 :
365 : /* Compute flow hash on ULH */
366 3 : if (PREDICT_TRUE (ulh->protocol == IP_PROTOCOL_UDP ||
367 : ulh->protocol == IP_PROTOCOL_TCP))
368 : {
369 3 : udp_header_t *ulh_l4_hdr = (udp_header_t *) (ulh + 1);
370 3 : src_port = ulh_l4_hdr->src_port;
371 3 : dst_port = ulh_l4_hdr->dst_port;
372 : }
373 :
374 3 : kv.key[0] = ulh->src_address.as_u64[0];
375 3 : kv.key[1] = ulh->src_address.as_u64[1];
376 3 : kv.key[2] = ulh->dst_address.as_u64[0];
377 3 : kv.key[3] = ulh->dst_address.as_u64[1];
378 3 : kv.key[4] = ((u64) src_port << 16) | ((u64) dst_port);
379 :
380 : /* Lookup flow in hashtable */
381 3 : if (!clib_bihash_search_40_8 (h, &kv, &value))
382 : {
383 2 : e = pool_elt_at_index (ls_mem->cache,
384 : ad_flow_value_get_session_index (&value));
385 : }
386 :
387 3 : if (!e)
388 : {
389 1 : if (pool_elts (ls_mem->cache) >= ls_mem->cache_size)
390 : {
391 0 : if (!ad_flow_lru_free_one (ls_mem, now))
392 : {
393 0 : *next = SRV6_AD_FLOW_LOCALSID_NEXT_ERROR;
394 0 : *cnt = &(sm->sid_cache_full_counters);
395 0 : *cnt_idx = ls_mem->index;
396 0 : return;
397 : }
398 : }
399 :
400 1 : e = ad_flow_entry_alloc (ls_mem, now);
401 1 : ASSERT (e);
402 1 : e->key.s_addr.ip6.as_u64[0] = ulh->src_address.as_u64[0];
403 1 : e->key.s_addr.ip6.as_u64[1] = ulh->src_address.as_u64[1];
404 1 : e->key.d_addr.ip6.as_u64[0] = ulh->dst_address.as_u64[0];
405 1 : e->key.d_addr.ip6.as_u64[1] = ulh->dst_address.as_u64[1];
406 1 : e->key.s_port = src_port;
407 1 : e->key.d_port = dst_port;
408 1 : e->key.proto = ulh->protocol;
409 :
410 1 : kv.value = (u64) (e - ls_mem->cache);
411 :
412 1 : ctx.now = now;
413 1 : ctx.ls = ls_mem;
414 1 : clib_bihash_add_or_overwrite_stale_40_8 (h, &kv,
415 : ad_flow_is_idle_entry_cb, &ctx);
416 : }
417 3 : e->last_heard = now;
418 :
419 : /* Cache encapsulation headers */
420 3 : if (PREDICT_FALSE (encap_length > e->rw_len))
421 : {
422 1 : vec_validate (e->rw_data, encap_length - 1);
423 : }
424 3 : clib_memcpy_fast (e->rw_data, ip, encap_length);
425 3 : e->rw_len = encap_length;
426 :
427 : /* Update LRU */
428 3 : ad_flow_entry_update_lru (ls_mem, e);
429 :
430 : /* Decapsulate the packet */
431 3 : vlib_buffer_advance (b, encap_length);
432 :
433 : /* Set next node */
434 3 : *next = SRV6_AD_FLOW_LOCALSID_NEXT_REWRITE6;
435 :
436 : /* Set Xconnect adjacency to VNF */
437 3 : vnet_buffer (b)->ip.adj_index[VLIB_TX] = ls_mem->nh_adj;
438 : }
439 :
440 : static_always_inline void
441 3 : end_ad_flow_processing_v4 (vlib_main_t *vm, vlib_buffer_t *b, ip6_header_t *ip,
442 : srv6_ad_flow_localsid_t *ls_mem, u32 *next,
443 : vlib_combined_counter_main_t **cnt, u32 *cnt_idx,
444 : f64 now)
445 : {
446 3 : ip6_sr_main_t *srm = &sr_main;
447 3 : srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
448 : ip6_address_t *new_dst;
449 3 : u32 encap_length = sizeof (ip6_header_t);
450 : ip6_sr_header_t *srh;
451 3 : clib_bihash_40_8_t *h = &ls_mem->ftable;
452 3 : ip4_header_t *ulh = NULL;
453 3 : u16 src_port = 0, dst_port = 0;
454 3 : srv6_ad_flow_entry_t *e = NULL;
455 : clib_bihash_kv_40_8_t kv, value;
456 : srv6_ad_is_idle_entry_ctx_t ctx;
457 :
458 : /* Find SRH in the extension header chain */
459 3 : end_ad_flow_walk_expect_first_hdr (vm, b, (void *) (ip + 1), ip->protocol,
460 : IP_PROTOCOL_IPV6_ROUTE, &encap_length,
461 : (u8 **) &srh);
462 :
463 : /* Punt the packet if no SRH or SRH with SL = 0 */
464 3 : if (PREDICT_FALSE (srh == NULL || srh->type != ROUTING_HEADER_TYPE_SR ||
465 : srh->segments_left == 0))
466 : {
467 0 : *next = SRV6_AD_FLOW_LOCALSID_NEXT_PUNT;
468 0 : *cnt = &(sm->sid_punt_counters);
469 0 : *cnt_idx = ls_mem->index;
470 0 : return;
471 : }
472 :
473 : /* Decrement Segments Left and update Destination Address */
474 3 : srh->segments_left -= 1;
475 3 : new_dst = (ip6_address_t *) (srh->segments) + srh->segments_left;
476 3 : ip->dst_address.as_u64[0] = new_dst->as_u64[0];
477 3 : ip->dst_address.as_u64[1] = new_dst->as_u64[1];
478 :
479 : /* Add SRH length to the total encapsulation size */
480 3 : encap_length += ip6_ext_header_len ((ip6_ext_header_t *) srh);
481 :
482 : /* Find the inner IPv6 header (ULH) */
483 3 : int ret = end_ad_flow_walk_expect_first_hdr (
484 3 : vm, b, ip6_ext_next_header ((ip6_ext_header_t *) srh), srh->protocol,
485 : IP_PROTOCOL_IP_IN_IP, &encap_length, (u8 **) &ulh);
486 :
487 3 : if (PREDICT_FALSE (ulh == NULL))
488 : {
489 0 : if (ret == -1) /* Bypass the NF if ULH is not of expected type */
490 : {
491 0 : *next = SRV6_AD_FLOW_LOCALSID_NEXT_BYPASS;
492 0 : *cnt = &(sm->sid_bypass_counters);
493 0 : *cnt_idx = ls_mem->index;
494 : }
495 : else
496 : {
497 0 : *next = SRV6_AD_FLOW_LOCALSID_NEXT_ERROR;
498 0 : *cnt = &(srm->sr_ls_invalid_counters);
499 : }
500 0 : return;
501 : }
502 :
503 : /* Compute flow hash on ULH */
504 3 : if (PREDICT_TRUE (ulh->protocol == IP_PROTOCOL_UDP ||
505 : ulh->protocol == IP_PROTOCOL_TCP))
506 : {
507 3 : udp_header_t *ulh_l4_hdr = (udp_header_t *) (ulh + 1);
508 3 : src_port = ulh_l4_hdr->src_port;
509 3 : dst_port = ulh_l4_hdr->dst_port;
510 : }
511 :
512 3 : kv.key[0] = *((u64 *) &ulh->address_pair);
513 3 : kv.key[1] = ((u64) src_port << 16) | ((u64) dst_port);
514 3 : kv.key[2] = 0;
515 3 : kv.key[3] = 0;
516 3 : kv.key[4] = 0;
517 :
518 : /* Lookup flow in hashtable */
519 3 : if (!clib_bihash_search_40_8 (h, &kv, &value))
520 : {
521 2 : e = pool_elt_at_index (ls_mem->cache,
522 : ad_flow_value_get_session_index (&value));
523 : }
524 :
525 3 : if (!e)
526 : {
527 1 : if (pool_elts (ls_mem->cache) >= ls_mem->cache_size)
528 : {
529 0 : if (!ad_flow_lru_free_one (ls_mem, now))
530 : {
531 0 : *next = SRV6_AD_FLOW_LOCALSID_NEXT_ERROR;
532 0 : *cnt = &(sm->sid_cache_full_counters);
533 0 : *cnt_idx = ls_mem->index;
534 0 : return;
535 : }
536 : }
537 :
538 1 : e = ad_flow_entry_alloc (ls_mem, now);
539 1 : ASSERT (e);
540 1 : e->key.s_addr.ip4 = ulh->src_address;
541 1 : e->key.d_addr.ip4 = ulh->dst_address;
542 1 : e->key.s_port = src_port;
543 1 : e->key.d_port = dst_port;
544 1 : e->key.proto = ulh->protocol;
545 :
546 1 : kv.value = (u64) (e - ls_mem->cache);
547 :
548 1 : ctx.now = now;
549 1 : ctx.ls = ls_mem;
550 1 : clib_bihash_add_or_overwrite_stale_40_8 (h, &kv,
551 : ad_flow_is_idle_entry_cb, &ctx);
552 : }
553 3 : e->last_heard = now;
554 :
555 : /* Cache encapsulation headers */
556 3 : if (PREDICT_FALSE (encap_length > e->rw_len))
557 : {
558 1 : vec_validate (e->rw_data, encap_length - 1);
559 : }
560 3 : clib_memcpy_fast (e->rw_data, ip, encap_length);
561 3 : e->rw_len = encap_length;
562 :
563 : /* Update LRU */
564 3 : ad_flow_entry_update_lru (ls_mem, e);
565 :
566 : /* Decapsulate the packet */
567 3 : vlib_buffer_advance (b, encap_length);
568 :
569 : /* Set next node */
570 3 : *next = SRV6_AD_FLOW_LOCALSID_NEXT_REWRITE4;
571 :
572 : /* Set Xconnect adjacency to VNF */
573 3 : vnet_buffer (b)->ip.adj_index[VLIB_TX] = ls_mem->nh_adj;
574 : }
575 :
576 : /**
577 : * @brief SRv6 AD Localsid graph node
578 : */
579 : static uword
580 2 : srv6_ad_flow_localsid_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
581 : vlib_frame_t *frame)
582 : {
583 2 : ip6_sr_main_t *srm = &sr_main;
584 2 : f64 now = vlib_time_now (vm);
585 : u32 n_left_from, next_index, *from, *to_next, n_left_to_next;
586 2 : u32 thread_index = vm->thread_index;
587 :
588 2 : from = vlib_frame_vector_args (frame);
589 2 : n_left_from = frame->n_vectors;
590 2 : next_index = node->cached_next_index;
591 :
592 4 : while (n_left_from > 0)
593 : {
594 2 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
595 :
596 : /* TODO: Dual/quad loop */
597 :
598 8 : while (n_left_from > 0 && n_left_to_next > 0)
599 : {
600 : u32 bi0;
601 : vlib_buffer_t *b0;
602 6 : ip6_header_t *ip0 = 0;
603 : ip6_sr_localsid_t *ls0;
604 : srv6_ad_flow_localsid_t *ls_mem0;
605 : u32 next0;
606 6 : vlib_combined_counter_main_t *cnt0 = &(srm->sr_ls_valid_counters);
607 : u32 cnt_idx0;
608 :
609 6 : bi0 = from[0];
610 6 : to_next[0] = bi0;
611 6 : from += 1;
612 6 : to_next += 1;
613 6 : n_left_from -= 1;
614 6 : n_left_to_next -= 1;
615 :
616 6 : b0 = vlib_get_buffer (vm, bi0);
617 6 : ip0 = vlib_buffer_get_current (b0);
618 :
619 : /* Retrieve local SID context based on IP DA (adj) */
620 6 : ls0 = pool_elt_at_index (srm->localsids,
621 : vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
622 :
623 6 : cnt_idx0 = ls0 - srm->localsids;
624 :
625 : /* Retrieve local SID's plugin memory */
626 6 : ls_mem0 = ls0->plugin_mem;
627 :
628 : /* SRH processing */
629 6 : if (ls_mem0->inner_type == AD_TYPE_IP6)
630 3 : end_ad_flow_processing_v6 (vm, b0, ip0, ls_mem0, &next0, &cnt0,
631 : &cnt_idx0, now);
632 : else
633 3 : end_ad_flow_processing_v4 (vm, b0, ip0, ls_mem0, &next0, &cnt0,
634 : &cnt_idx0, now);
635 :
636 : /* Trace packet (if enabled) */
637 6 : if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
638 : {
639 : srv6_ad_flow_localsid_trace_t *tr =
640 6 : vlib_add_trace (vm, node, b0, sizeof *tr);
641 6 : tr->localsid_index = ls_mem0->index;
642 : }
643 :
644 : /* Increment the appropriate per-SID counter */
645 6 : vlib_increment_combined_counter (
646 : cnt0, thread_index, cnt_idx0, 1,
647 : vlib_buffer_length_in_chain (vm, b0));
648 :
649 6 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
650 : n_left_to_next, bi0, next0);
651 : }
652 2 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
653 : }
654 :
655 2 : return frame->n_vectors;
656 : }
657 :
658 37519 : VLIB_REGISTER_NODE (srv6_ad_flow_localsid_node) = {
659 : .function = srv6_ad_flow_localsid_fn,
660 : .name = "srv6-ad-flow-localsid",
661 : .vector_size = sizeof (u32),
662 : .format_trace = format_srv6_ad_flow_localsid_trace,
663 : .type = VLIB_NODE_TYPE_INTERNAL,
664 : .n_next_nodes = SRV6_AD_FLOW_LOCALSID_N_NEXT,
665 : .next_nodes = {
666 : [SRV6_AD_FLOW_LOCALSID_NEXT_PUNT] = "ip6-local",
667 : [SRV6_AD_FLOW_LOCALSID_NEXT_BYPASS] = "ip6-lookup",
668 : [SRV6_AD_FLOW_LOCALSID_NEXT_REWRITE4] = "ip4-rewrite",
669 : [SRV6_AD_FLOW_LOCALSID_NEXT_REWRITE6] = "ip6-rewrite",
670 : [SRV6_AD_FLOW_LOCALSID_NEXT_ERROR] = "error-drop",
671 : },
672 : };
673 :
674 : /****************************** Rewriting node *******************************/
675 :
676 : /**
677 : * @brief Graph node for applying a SR policy into an IPv6 packet.
678 : * Encapsulation
679 : */
680 : static uword
681 1 : srv6_ad4_flow_rewrite_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
682 : vlib_frame_t *frame)
683 : {
684 1 : ip6_sr_main_t *srm = &sr_main;
685 1 : srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
686 : u32 n_left_from, next_index, *from, *to_next;
687 1 : u32 cnt_packets = 0;
688 :
689 1 : from = vlib_frame_vector_args (frame);
690 1 : n_left_from = frame->n_vectors;
691 1 : next_index = node->cached_next_index;
692 :
693 2 : while (n_left_from > 0)
694 : {
695 : u32 n_left_to_next;
696 :
697 1 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
698 :
699 : /* TODO: Dual/quad loop */
700 :
701 4 : while (n_left_from > 0 && n_left_to_next > 0)
702 : {
703 : u32 bi0;
704 : vlib_buffer_t *b0;
705 3 : ip4_header_t *ip0_encap = 0;
706 3 : ip6_header_t *ip0 = 0;
707 : ip6_sr_localsid_t *ls0;
708 : srv6_ad_flow_localsid_t *ls0_mem;
709 : srv6_ad_flow_entry_t *s0;
710 3 : u32 next0 = SRV6_AD_FLOW_REWRITE_NEXT_LOOKUP;
711 3 : u16 new_l0 = 0;
712 :
713 3 : bi0 = from[0];
714 3 : to_next[0] = bi0;
715 3 : from += 1;
716 3 : to_next += 1;
717 3 : n_left_from -= 1;
718 3 : n_left_to_next -= 1;
719 :
720 3 : b0 = vlib_get_buffer (vm, bi0);
721 3 : ip0_encap = vlib_buffer_get_current (b0);
722 3 : ls0 = pool_elt_at_index (
723 : srm->localsids,
724 : sm->sw_iface_localsid4[vnet_buffer (b0)->sw_if_index[VLIB_RX]]);
725 3 : ls0_mem = ls0->plugin_mem;
726 :
727 3 : if (PREDICT_FALSE (ls0_mem == NULL))
728 : {
729 0 : next0 = SRV6_AD_FLOW_REWRITE_NEXT_ERROR;
730 0 : b0->error = node->errors[SRV6_AD_FLOW_REWRITE_COUNTER_NO_RW];
731 : }
732 : else
733 : {
734 : clib_bihash_kv_40_8_t kv0, value0;
735 :
736 : /* Compute flow hash */
737 3 : u64 ports = 0;
738 3 : if (PREDICT_TRUE (ip0_encap->protocol == IP_PROTOCOL_UDP ||
739 : ip0_encap->protocol == IP_PROTOCOL_TCP))
740 : {
741 3 : udp_header_t *udp0 = (udp_header_t *) (ip0_encap + 1);
742 3 : ports =
743 3 : ((u64) udp0->src_port << 16) | ((u64) udp0->dst_port);
744 : }
745 :
746 3 : kv0.key[0] = *((u64 *) &ip0_encap->address_pair);
747 3 : kv0.key[1] = ports;
748 3 : kv0.key[2] = 0;
749 3 : kv0.key[3] = 0;
750 3 : kv0.key[4] = 0;
751 :
752 : /* Lookup flow in hashtable */
753 3 : if (clib_bihash_search_40_8 (&ls0_mem->ftable, &kv0, &value0) <
754 : 0)
755 : {
756 : /* not found */
757 0 : next0 = SRV6_AD_FLOW_REWRITE_NEXT_ERROR;
758 0 : b0->error = node->errors[SRV6_AD_FLOW_REWRITE_COUNTER_NO_RW];
759 : }
760 : else
761 : {
762 : /* found */
763 3 : s0 = pool_elt_at_index (
764 : ls0_mem->cache, ad_flow_value_get_session_index (&value0));
765 3 : ASSERT (s0);
766 3 : ASSERT (VLIB_BUFFER_PRE_DATA_SIZE >=
767 : (s0->rw_len + b0->current_data));
768 :
769 3 : clib_memcpy_fast (((u8 *) ip0_encap) - s0->rw_len,
770 3 : s0->rw_data, s0->rw_len);
771 3 : vlib_buffer_advance (b0, -(word) s0->rw_len);
772 :
773 3 : ip0 = vlib_buffer_get_current (b0);
774 :
775 : /* Update inner IPv4 TTL and checksum */
776 : u32 checksum0;
777 3 : ip0_encap->ttl -= 1;
778 3 : checksum0 =
779 3 : ip0_encap->checksum + clib_host_to_net_u16 (0x0100);
780 3 : checksum0 += checksum0 >= 0xffff;
781 3 : ip0_encap->checksum = checksum0;
782 :
783 : /* Update outer IPv6 length (in case it has changed) */
784 6 : new_l0 = s0->rw_len - sizeof (ip6_header_t) +
785 3 : clib_net_to_host_u16 (ip0_encap->length);
786 3 : ip0->payload_length = clib_host_to_net_u16 (new_l0);
787 : }
788 : }
789 :
790 3 : if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
791 3 : PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
792 : {
793 : srv6_ad_flow_rewrite_trace_t *tr =
794 3 : vlib_add_trace (vm, node, b0, sizeof *tr);
795 3 : tr->error = 0;
796 :
797 3 : if (next0 == SRV6_AD_FLOW_REWRITE_NEXT_ERROR)
798 : {
799 0 : tr->error = 1;
800 : }
801 : else
802 : {
803 3 : clib_memcpy_fast (tr->src.as_u8, ip0->src_address.as_u8,
804 : sizeof tr->src.as_u8);
805 3 : clib_memcpy_fast (tr->dst.as_u8, ip0->dst_address.as_u8,
806 : sizeof tr->dst.as_u8);
807 : }
808 : }
809 :
810 : /* Increment per-SID AD rewrite counters */
811 3 : vlib_increment_combined_counter (
812 : ((next0 == SRV6_AD_FLOW_REWRITE_NEXT_ERROR) ?
813 : &(sm->rw_invalid_counters) :
814 : &(sm->rw_valid_counters)),
815 : vm->thread_index, ls0_mem->index, 1,
816 : vlib_buffer_length_in_chain (vm, b0));
817 :
818 3 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
819 : n_left_to_next, bi0, next0);
820 :
821 3 : cnt_packets++;
822 : }
823 :
824 1 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
825 : }
826 :
827 : /* Update counters */
828 1 : vlib_node_increment_counter (vm, srv6_ad4_flow_rewrite_node.index,
829 : SRV6_AD_FLOW_REWRITE_COUNTER_PROCESSED,
830 : cnt_packets);
831 :
832 1 : return frame->n_vectors;
833 : }
834 :
835 37519 : VLIB_REGISTER_NODE (srv6_ad4_flow_rewrite_node) = {
836 : .function = srv6_ad4_flow_rewrite_fn,
837 : .name = "srv6-ad4-flow-rewrite",
838 : .vector_size = sizeof (u32),
839 : .format_trace = format_srv6_ad_flow_rewrite_trace,
840 : .type = VLIB_NODE_TYPE_INTERNAL,
841 : .n_errors = SRV6_AD_FLOW_REWRITE_N_COUNTERS,
842 : .error_strings = srv6_ad_flow_rewrite_counter_strings,
843 : .n_next_nodes = SRV6_AD_FLOW_REWRITE_N_NEXT,
844 : .next_nodes = {
845 : [SRV6_AD_FLOW_REWRITE_NEXT_LOOKUP] = "ip6-lookup",
846 : [SRV6_AD_FLOW_REWRITE_NEXT_ERROR] = "error-drop",
847 : },
848 : };
849 :
850 : /**
851 : * @brief Graph node for applying a SR policy into an IPv6 packet.
852 : * Encapsulation
853 : */
854 : static uword
855 1 : srv6_ad6_flow_rewrite_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
856 : vlib_frame_t *frame)
857 : {
858 1 : ip6_sr_main_t *srm = &sr_main;
859 1 : srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
860 : u32 n_left_from, next_index, *from, *to_next;
861 1 : u32 cnt_packets = 0;
862 :
863 1 : from = vlib_frame_vector_args (frame);
864 1 : n_left_from = frame->n_vectors;
865 1 : next_index = node->cached_next_index;
866 :
867 2 : while (n_left_from > 0)
868 : {
869 : u32 n_left_to_next;
870 :
871 1 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
872 :
873 : /* TODO: Dual/quad loop */
874 :
875 4 : while (n_left_from > 0 && n_left_to_next > 0)
876 : {
877 : u32 bi0;
878 : vlib_buffer_t *b0;
879 3 : ip6_header_t *ip0 = 0, *ip0_encap = 0;
880 : ip6_sr_localsid_t *ls0;
881 : srv6_ad_flow_localsid_t *ls0_mem;
882 : srv6_ad_flow_entry_t *s0;
883 3 : u32 next0 = SRV6_AD_FLOW_REWRITE_NEXT_LOOKUP;
884 3 : u16 new_l0 = 0;
885 :
886 3 : bi0 = from[0];
887 3 : to_next[0] = bi0;
888 3 : from += 1;
889 3 : to_next += 1;
890 3 : n_left_from -= 1;
891 3 : n_left_to_next -= 1;
892 :
893 3 : b0 = vlib_get_buffer (vm, bi0);
894 3 : ip0_encap = vlib_buffer_get_current (b0);
895 3 : ls0 = pool_elt_at_index (
896 : srm->localsids,
897 : sm->sw_iface_localsid6[vnet_buffer (b0)->sw_if_index[VLIB_RX]]);
898 3 : ls0_mem = ls0->plugin_mem;
899 :
900 3 : if (PREDICT_FALSE (ls0_mem == NULL))
901 : {
902 0 : next0 = SRV6_AD_FLOW_REWRITE_NEXT_ERROR;
903 0 : b0->error = node->errors[SRV6_AD_FLOW_REWRITE_COUNTER_NO_RW];
904 : }
905 : else
906 : {
907 : /* ############################################# */
908 : clib_bihash_kv_40_8_t kv0, value0;
909 :
910 : /* Compute flow hash */
911 3 : u64 ports = 0;
912 3 : if (PREDICT_TRUE (ip0_encap->protocol == IP_PROTOCOL_UDP ||
913 : ip0_encap->protocol == IP_PROTOCOL_TCP))
914 : {
915 3 : udp_header_t *udp0 = (udp_header_t *) (ip0_encap + 1);
916 3 : ports =
917 3 : ((u64) udp0->src_port << 16) | ((u64) udp0->dst_port);
918 : }
919 :
920 3 : kv0.key[0] = ip0_encap->src_address.as_u64[0];
921 3 : kv0.key[1] = ip0_encap->src_address.as_u64[1];
922 3 : kv0.key[2] = ip0_encap->dst_address.as_u64[0];
923 3 : kv0.key[3] = ip0_encap->dst_address.as_u64[1];
924 3 : kv0.key[4] = ports;
925 :
926 : /* Lookup flow in hashtable */
927 3 : if (clib_bihash_search_40_8 (&ls0_mem->ftable, &kv0, &value0))
928 : {
929 : /* not found */
930 0 : next0 = SRV6_AD_FLOW_REWRITE_NEXT_ERROR;
931 0 : b0->error = node->errors[SRV6_AD_FLOW_REWRITE_COUNTER_NO_RW];
932 : }
933 : else
934 : {
935 : /* found */
936 3 : s0 = pool_elt_at_index (
937 : ls0_mem->cache, ad_flow_value_get_session_index (&value0));
938 3 : ASSERT (s0);
939 :
940 3 : ASSERT (VLIB_BUFFER_PRE_DATA_SIZE >=
941 : (s0->rw_len + b0->current_data));
942 :
943 3 : clib_memcpy_fast (((u8 *) ip0_encap) - s0->rw_len,
944 3 : s0->rw_data, s0->rw_len);
945 3 : vlib_buffer_advance (b0, -(word) s0->rw_len);
946 :
947 3 : ip0 = vlib_buffer_get_current (b0);
948 :
949 : /* Update inner IPv6 hop limit */
950 3 : ip0_encap->hop_limit -= 1;
951 :
952 : /* Update outer IPv6 length (in case it has changed) */
953 6 : new_l0 = s0->rw_len +
954 3 : clib_net_to_host_u16 (ip0_encap->payload_length);
955 3 : ip0->payload_length = clib_host_to_net_u16 (new_l0);
956 : }
957 : }
958 :
959 3 : if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
960 3 : PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
961 : {
962 : srv6_ad_flow_rewrite_trace_t *tr =
963 3 : vlib_add_trace (vm, node, b0, sizeof *tr);
964 3 : tr->error = 0;
965 :
966 3 : if (next0 == SRV6_AD_FLOW_REWRITE_NEXT_ERROR)
967 : {
968 0 : tr->error = 1;
969 : }
970 : else
971 : {
972 3 : clib_memcpy_fast (tr->src.as_u8, ip0->src_address.as_u8,
973 : sizeof tr->src.as_u8);
974 3 : clib_memcpy_fast (tr->dst.as_u8, ip0->dst_address.as_u8,
975 : sizeof tr->dst.as_u8);
976 : }
977 : }
978 :
979 : /* Increment per-SID AD rewrite counters */
980 3 : vlib_increment_combined_counter (
981 : ((next0 == SRV6_AD_FLOW_REWRITE_NEXT_ERROR) ?
982 : &(sm->rw_invalid_counters) :
983 : &(sm->rw_valid_counters)),
984 : vm->thread_index, ls0_mem->index, 1,
985 : vlib_buffer_length_in_chain (vm, b0));
986 :
987 3 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
988 : n_left_to_next, bi0, next0);
989 :
990 3 : cnt_packets++;
991 : }
992 :
993 1 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
994 : }
995 :
996 : /* Update counters */
997 1 : vlib_node_increment_counter (vm, srv6_ad6_flow_rewrite_node.index,
998 : SRV6_AD_FLOW_REWRITE_COUNTER_PROCESSED,
999 : cnt_packets);
1000 :
1001 1 : return frame->n_vectors;
1002 : }
1003 :
1004 37519 : VLIB_REGISTER_NODE (srv6_ad6_flow_rewrite_node) = {
1005 : .function = srv6_ad6_flow_rewrite_fn,
1006 : .name = "srv6-ad6-flow-rewrite",
1007 : .vector_size = sizeof (u32),
1008 : .format_trace = format_srv6_ad_flow_rewrite_trace,
1009 : .type = VLIB_NODE_TYPE_INTERNAL,
1010 : .n_errors = SRV6_AD_FLOW_REWRITE_N_COUNTERS,
1011 : .error_strings = srv6_ad_flow_rewrite_counter_strings,
1012 : .n_next_nodes = SRV6_AD_FLOW_REWRITE_N_NEXT,
1013 : .next_nodes = {
1014 : [SRV6_AD_FLOW_REWRITE_NEXT_LOOKUP] = "ip6-lookup",
1015 : [SRV6_AD_FLOW_REWRITE_NEXT_ERROR] = "error-drop",
1016 : },
1017 : };
1018 :
1019 : /*
1020 : * fd.io coding-style-patch-verification: ON
1021 : *
1022 : * Local Variables:
1023 : * eval: (c-set-style "gnu")
1024 : * End:
1025 : */
|