diff options
Diffstat (limited to 'src/plugins/srv6-ad-flow/node.c')
-rw-r--r-- | src/plugins/srv6-ad-flow/node.c | 1025 |
1 files changed, 1025 insertions, 0 deletions
diff --git a/src/plugins/srv6-ad-flow/node.c b/src/plugins/srv6-ad-flow/node.c new file mode 100644 index 00000000000..66be2dc7972 --- /dev/null +++ b/src/plugins/srv6-ad-flow/node.c @@ -0,0 +1,1025 @@ +/* + * node.c + * + * Copyright (c) 2015 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <vlib/vlib.h> +#include <vnet/vnet.h> +#include <vppinfra/error.h> +#include <srv6-ad-flow/ad-flow.h> + +/****************************** Packet tracing ******************************/ + +typedef struct +{ + u32 localsid_index; +} srv6_ad_flow_localsid_trace_t; + +typedef struct +{ + u8 error; + ip6_address_t src, dst; +} srv6_ad_flow_rewrite_trace_t; + +static u8 * +format_srv6_ad_flow_localsid_trace (u8 *s, va_list *args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + srv6_ad_flow_localsid_trace_t *t = + va_arg (*args, srv6_ad_flow_localsid_trace_t *); + + return format (s, "SRv6-AD-Flow-localsid: localsid_index %d", + t->localsid_index); +} + +static u8 * +format_srv6_ad_flow_rewrite_trace (u8 *s, va_list *args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + srv6_ad_flow_rewrite_trace_t *t = + va_arg (*args, srv6_ad_flow_rewrite_trace_t *); + + if (PREDICT_FALSE (t->error != 0)) + { + return format (s, "SRv6-AD-Flow-rewrite: cache is empty"); + } + + return format (s, "SRv6-AD-Flow-rewrite: src %U dst %U", format_ip6_address, + &t->src, format_ip6_address, &t->dst); +} + +/**************************** Nodes registration *****************************/ + +vlib_node_registration_t srv6_ad4_flow_rewrite_node; +vlib_node_registration_t srv6_ad6_flow_rewrite_node; + +/****************************** Packet counters ******************************/ + +#define foreach_srv6_ad_flow_rewrite_counter \ + _ (PROCESSED, "srv6-ad-flow rewritten packets") \ + _ (NO_RW, "(Error) No header for rewriting.") + +typedef enum +{ +#define _(sym, str) SRV6_AD_FLOW_REWRITE_COUNTER_##sym, + foreach_srv6_ad_flow_rewrite_counter +#undef _ + SRV6_AD_FLOW_REWRITE_N_COUNTERS, +} srv6_ad_flow_rewrite_counters; + +static char *srv6_ad_flow_rewrite_counter_strings[] = { +#define _(sym, string) string, + foreach_srv6_ad_flow_rewrite_counter +#undef _ +}; + +/******************************** Next nodes *********************************/ + +typedef enum +{ + SRV6_AD_FLOW_LOCALSID_NEXT_ERROR, + SRV6_AD_FLOW_LOCALSID_NEXT_REWRITE4, + SRV6_AD_FLOW_LOCALSID_NEXT_REWRITE6, + SRV6_AD_FLOW_LOCALSID_NEXT_BYPASS, + SRV6_AD_FLOW_LOCALSID_NEXT_PUNT, + SRV6_AD_FLOW_LOCALSID_N_NEXT, +} srv6_ad_flow_localsid_next_t; + +typedef enum +{ + SRV6_AD_FLOW_REWRITE_NEXT_ERROR, + SRV6_AD_FLOW_REWRITE_NEXT_LOOKUP, + SRV6_AD_FLOW_REWRITE_N_NEXT, +} srv6_ad_flow_rewrite_next_t; + +/***************************** Inline functions ******************************/ + +static_always_inline int +ad_flow_lru_insert (srv6_ad_flow_localsid_t *ls, srv6_ad_flow_entry_t *e, + f64 now) +{ + dlist_elt_t *lru_list_elt; + pool_get (ls->lru_pool, lru_list_elt); + e->lru_index = lru_list_elt - ls->lru_pool; + clib_dlist_addtail (ls->lru_pool, ls->lru_head_index, e->lru_index); + lru_list_elt->value = e - ls->cache; + e->last_lru_update = now; + return 1; +} + +always_inline void +ad_flow_entry_update_lru (srv6_ad_flow_localsid_t *ls, srv6_ad_flow_entry_t *e) +{ + /* don't update too often - timeout is in magnitude of seconds anyway */ + if (e->last_heard > e->last_lru_update + 1) + { + clib_dlist_remove (ls->lru_pool, e->lru_index); + clib_dlist_addtail (ls->lru_pool, ls->lru_head_index, e->lru_index); + e->last_lru_update = e->last_heard; + } +} + +always_inline void +ad_flow_entry_delete (srv6_ad_flow_localsid_t *ls, srv6_ad_flow_entry_t *e, + int lru_delete) +{ + clib_bihash_kv_40_8_t kv; + + if (ls->inner_type == AD_TYPE_IP4) + { + kv.key[0] = ((u64) e->key.s_addr.ip4.as_u32 << 32) | + (u64) e->key.d_addr.ip4.as_u32; + kv.key[1] = ((u64) e->key.s_port << 16) | ((u64) e->key.d_port); + kv.key[2] = 0; + kv.key[3] = 0; + kv.key[4] = 0; + } + else + { + kv.key[0] = e->key.s_addr.ip6.as_u64[0]; + kv.key[1] = e->key.s_addr.ip6.as_u64[1]; + kv.key[2] = e->key.d_addr.ip6.as_u64[0]; + kv.key[3] = e->key.d_addr.ip6.as_u64[1]; + kv.key[4] = ((u64) e->key.s_port << 16) | ((u64) e->key.d_port); + } + + clib_bihash_add_del_40_8 (&ls->ftable, &kv, 0); + + vec_free (e->rw_data); + + if (lru_delete) + { + clib_dlist_remove (ls->lru_pool, e->lru_index); + } + pool_put_index (ls->lru_pool, e->lru_index); + pool_put (ls->cache, e); +} + +static_always_inline int +ad_flow_lru_free_one (srv6_ad_flow_localsid_t *ls, f64 now) +{ + srv6_ad_flow_entry_t *e = NULL; + dlist_elt_t *oldest_elt; + f64 entry_timeout_time; + u32 oldest_index; + oldest_index = clib_dlist_remove_head (ls->lru_pool, ls->lru_head_index); + if (~0 != oldest_index) + { + oldest_elt = pool_elt_at_index (ls->lru_pool, oldest_index); + e = pool_elt_at_index (ls->cache, oldest_elt->value); + + entry_timeout_time = e->last_heard + (f64) SRV6_AD_CACHE_TIMEOUT; + if (now >= entry_timeout_time) + { + ad_flow_entry_delete (ls, e, 0); + return 1; + } + else + { + clib_dlist_addhead (ls->lru_pool, ls->lru_head_index, oldest_index); + } + } + return 0; +} + +static_always_inline srv6_ad_flow_entry_t * +ad_flow_entry_alloc (srv6_ad_flow_localsid_t *ls, f64 now) +{ + srv6_ad_flow_entry_t *e; + + ad_flow_lru_free_one (ls, now); + + pool_get (ls->cache, e); + clib_memset (e, 0, sizeof *e); + + ad_flow_lru_insert (ls, e, now); + + return e; +} + +always_inline u32 +ad_flow_value_get_session_index (clib_bihash_kv_40_8_t *value) +{ + return value->value & ~(u32) 0; +} + +int +ad_flow_is_idle_entry_cb (clib_bihash_kv_40_8_t *kv, void *arg) +{ + srv6_ad_is_idle_entry_ctx_t *ctx = arg; + srv6_ad_flow_entry_t *e; + u64 entry_timeout_time; + srv6_ad_flow_localsid_t *ls = ctx->ls; + + e = pool_elt_at_index (ls->cache, ad_flow_value_get_session_index (kv)); + entry_timeout_time = e->last_heard + (f64) SRV6_AD_CACHE_TIMEOUT; + if (ctx->now >= entry_timeout_time) + { + ad_flow_entry_delete (ls, e, 1); + return 1; + } + return 0; +} + +/****************************** Local SID node *******************************/ + +/** + * @brief Function doing SRH processing for AD behavior + */ +static_always_inline int +end_ad_flow_walk_expect_first_hdr (vlib_main_t *vm, vlib_buffer_t *b, + ip6_ext_header_t *first_hdr, + u8 first_hdr_type, u8 expected_hdr_type, + u32 *encap_length, u8 **found_hdr) +{ + if (PREDICT_TRUE (first_hdr_type == expected_hdr_type)) + { + *found_hdr = (void *) first_hdr; + } + else + { + u8 ext_hdr_type = first_hdr_type; + ip6_ext_header_t *ext_hdr = first_hdr; + + if (!ip6_ext_hdr (ext_hdr_type)) + { + *found_hdr = NULL; + return -1; + } + + u32 ext_hdr_length = ip6_ext_header_len (ext_hdr); + if (!vlib_object_within_buffer_data (vm, b, ext_hdr, ext_hdr_length)) + { + *found_hdr = NULL; + return -2; + } + *encap_length += ext_hdr_length; + ext_hdr_type = ext_hdr->next_hdr; + + while (ext_hdr_type != expected_hdr_type && ip6_ext_hdr (ext_hdr_type)) + { + ext_hdr = ip6_ext_next_header (ext_hdr); + ext_hdr_length = ip6_ext_header_len (ext_hdr); + if (!vlib_object_within_buffer_data (vm, b, ext_hdr, ext_hdr_length)) + { + *found_hdr = NULL; + return -2; + } + *encap_length += ext_hdr_length; + ext_hdr_type = ext_hdr->next_hdr; + } + + if (ext_hdr_type != expected_hdr_type) + { + *found_hdr = NULL; + return -1; + } + + *found_hdr = ip6_ext_next_header (ext_hdr); + } + + return 0; +} + +/** + * @brief Function doing SRH processing for per-flow AD behavior (IPv6 inner + * traffic) + */ +static_always_inline void +end_ad_flow_processing_v6 (vlib_main_t *vm, vlib_buffer_t *b, ip6_header_t *ip, + srv6_ad_flow_localsid_t *ls_mem, u32 *next, + vlib_combined_counter_main_t **cnt, u32 *cnt_idx, + f64 now) +{ + ip6_sr_main_t *srm = &sr_main; + srv6_ad_flow_main_t *sm = &srv6_ad_flow_main; + ip6_address_t *new_dst; + u32 encap_length = sizeof (ip6_header_t); + ip6_sr_header_t *srh; + clib_bihash_40_8_t *h = &ls_mem->ftable; + ip6_header_t *ulh = NULL; + u16 src_port = 0, dst_port = 0; + srv6_ad_flow_entry_t *e = NULL; + clib_bihash_kv_40_8_t kv, value; + srv6_ad_is_idle_entry_ctx_t ctx; + + /* Find SRH in the extension header chain */ + end_ad_flow_walk_expect_first_hdr (vm, b, (void *) (ip + 1), ip->protocol, + IP_PROTOCOL_IPV6_ROUTE, &encap_length, + (u8 **) &srh); + + /* Punt the packet if no SRH or SRH with SL = 0 */ + if (PREDICT_FALSE (srh == NULL || srh->type != ROUTING_HEADER_TYPE_SR || + srh->segments_left == 0)) + { + *next = SRV6_AD_FLOW_LOCALSID_NEXT_PUNT; + *cnt = &(sm->sid_punt_counters); + *cnt_idx = ls_mem->index; + return; + } + + /* Decrement Segments Left and update Destination Address */ + srh->segments_left -= 1; + new_dst = (ip6_address_t *) (srh->segments) + srh->segments_left; + ip->dst_address.as_u64[0] = new_dst->as_u64[0]; + ip->dst_address.as_u64[1] = new_dst->as_u64[1]; + + /* Compute the total encapsulation size and determine ULH type */ + encap_length += ip6_ext_header_len ((ip6_ext_header_t *) srh); + + /* Find the inner IPv6 header (ULH) */ + int ret = end_ad_flow_walk_expect_first_hdr ( + vm, b, ip6_ext_next_header ((ip6_ext_header_t *) srh), srh->protocol, + IP_PROTOCOL_IPV6, &encap_length, (u8 **) &ulh); + + if (PREDICT_FALSE (ulh == NULL)) + { + if (ret == -1) /* Bypass the NF if ULH is not of expected type */ + { + *next = SRV6_AD_FLOW_LOCALSID_NEXT_BYPASS; + *cnt = &(sm->sid_bypass_counters); + *cnt_idx = ls_mem->index; + } + else + { + *next = SRV6_AD_FLOW_LOCALSID_NEXT_ERROR; + *cnt = &(srm->sr_ls_invalid_counters); + } + return; + } + + /* Compute flow hash on ULH */ + if (PREDICT_TRUE (ulh->protocol == IP_PROTOCOL_UDP || + ulh->protocol == IP_PROTOCOL_TCP)) + { + udp_header_t *ulh_l4_hdr = (udp_header_t *) (ulh + 1); + src_port = ulh_l4_hdr->src_port; + dst_port = ulh_l4_hdr->dst_port; + } + + kv.key[0] = ulh->src_address.as_u64[0]; + kv.key[1] = ulh->src_address.as_u64[1]; + kv.key[2] = ulh->dst_address.as_u64[0]; + kv.key[3] = ulh->dst_address.as_u64[1]; + kv.key[4] = ((u64) src_port << 16) | ((u64) dst_port); + + /* Lookup flow in hashtable */ + if (!clib_bihash_search_40_8 (h, &kv, &value)) + { + e = pool_elt_at_index (ls_mem->cache, + ad_flow_value_get_session_index (&value)); + } + + if (!e) + { + if (pool_elts (ls_mem->cache) >= ls_mem->cache_size) + { + if (!ad_flow_lru_free_one (ls_mem, now)) + { + *next = SRV6_AD_FLOW_LOCALSID_NEXT_ERROR; + *cnt = &(sm->sid_cache_full_counters); + *cnt_idx = ls_mem->index; + return; + } + } + + e = ad_flow_entry_alloc (ls_mem, now); + ASSERT (e); + e->key.s_addr.ip6.as_u64[0] = ulh->src_address.as_u64[0]; + e->key.s_addr.ip6.as_u64[1] = ulh->src_address.as_u64[1]; + e->key.d_addr.ip6.as_u64[0] = ulh->dst_address.as_u64[0]; + e->key.d_addr.ip6.as_u64[1] = ulh->dst_address.as_u64[1]; + e->key.s_port = src_port; + e->key.d_port = dst_port; + e->key.proto = ulh->protocol; + + kv.value = (u64) (e - ls_mem->cache); + + ctx.now = now; + ctx.ls = ls_mem; + clib_bihash_add_or_overwrite_stale_40_8 (h, &kv, + ad_flow_is_idle_entry_cb, &ctx); + } + e->last_heard = now; + + /* Cache encapsulation headers */ + if (PREDICT_FALSE (encap_length > e->rw_len)) + { + vec_validate (e->rw_data, encap_length - 1); + } + clib_memcpy_fast (e->rw_data, ip, encap_length); + e->rw_len = encap_length; + + /* Update LRU */ + ad_flow_entry_update_lru (ls_mem, e); + + /* Decapsulate the packet */ + vlib_buffer_advance (b, encap_length); + + /* Set next node */ + *next = SRV6_AD_FLOW_LOCALSID_NEXT_REWRITE6; + + /* Set Xconnect adjacency to VNF */ + vnet_buffer (b)->ip.adj_index[VLIB_TX] = ls_mem->nh_adj; +} + +static_always_inline void +end_ad_flow_processing_v4 (vlib_main_t *vm, vlib_buffer_t *b, ip6_header_t *ip, + srv6_ad_flow_localsid_t *ls_mem, u32 *next, + vlib_combined_counter_main_t **cnt, u32 *cnt_idx, + f64 now) +{ + ip6_sr_main_t *srm = &sr_main; + srv6_ad_flow_main_t *sm = &srv6_ad_flow_main; + ip6_address_t *new_dst; + u32 encap_length = sizeof (ip6_header_t); + ip6_sr_header_t *srh; + clib_bihash_40_8_t *h = &ls_mem->ftable; + ip4_header_t *ulh = NULL; + u16 src_port = 0, dst_port = 0; + srv6_ad_flow_entry_t *e = NULL; + clib_bihash_kv_40_8_t kv, value; + srv6_ad_is_idle_entry_ctx_t ctx; + + /* Find SRH in the extension header chain */ + end_ad_flow_walk_expect_first_hdr (vm, b, (void *) (ip + 1), ip->protocol, + IP_PROTOCOL_IPV6_ROUTE, &encap_length, + (u8 **) &srh); + + /* Punt the packet if no SRH or SRH with SL = 0 */ + if (PREDICT_FALSE (srh == NULL || srh->type != ROUTING_HEADER_TYPE_SR || + srh->segments_left == 0)) + { + *next = SRV6_AD_FLOW_LOCALSID_NEXT_PUNT; + *cnt = &(sm->sid_punt_counters); + *cnt_idx = ls_mem->index; + return; + } + + /* Decrement Segments Left and update Destination Address */ + srh->segments_left -= 1; + new_dst = (ip6_address_t *) (srh->segments) + srh->segments_left; + ip->dst_address.as_u64[0] = new_dst->as_u64[0]; + ip->dst_address.as_u64[1] = new_dst->as_u64[1]; + + /* Add SRH length to the total encapsulation size */ + encap_length += ip6_ext_header_len ((ip6_ext_header_t *) srh); + + /* Find the inner IPv6 header (ULH) */ + int ret = end_ad_flow_walk_expect_first_hdr ( + vm, b, ip6_ext_next_header ((ip6_ext_header_t *) srh), srh->protocol, + IP_PROTOCOL_IP_IN_IP, &encap_length, (u8 **) &ulh); + + if (PREDICT_FALSE (ulh == NULL)) + { + if (ret == -1) /* Bypass the NF if ULH is not of expected type */ + { + *next = SRV6_AD_FLOW_LOCALSID_NEXT_BYPASS; + *cnt = &(sm->sid_bypass_counters); + *cnt_idx = ls_mem->index; + } + else + { + *next = SRV6_AD_FLOW_LOCALSID_NEXT_ERROR; + *cnt = &(srm->sr_ls_invalid_counters); + } + return; + } + + /* Compute flow hash on ULH */ + if (PREDICT_TRUE (ulh->protocol == IP_PROTOCOL_UDP || + ulh->protocol == IP_PROTOCOL_TCP)) + { + udp_header_t *ulh_l4_hdr = (udp_header_t *) (ulh + 1); + src_port = ulh_l4_hdr->src_port; + dst_port = ulh_l4_hdr->dst_port; + } + + kv.key[0] = *((u64 *) &ulh->address_pair); + kv.key[1] = ((u64) src_port << 16) | ((u64) dst_port); + kv.key[2] = 0; + kv.key[3] = 0; + kv.key[4] = 0; + + /* Lookup flow in hashtable */ + if (!clib_bihash_search_40_8 (h, &kv, &value)) + { + e = pool_elt_at_index (ls_mem->cache, + ad_flow_value_get_session_index (&value)); + } + + if (!e) + { + if (pool_elts (ls_mem->cache) >= ls_mem->cache_size) + { + if (!ad_flow_lru_free_one (ls_mem, now)) + { + *next = SRV6_AD_FLOW_LOCALSID_NEXT_ERROR; + *cnt = &(sm->sid_cache_full_counters); + *cnt_idx = ls_mem->index; + return; + } + } + + e = ad_flow_entry_alloc (ls_mem, now); + ASSERT (e); + e->key.s_addr.ip4 = ulh->src_address; + e->key.d_addr.ip4 = ulh->dst_address; + e->key.s_port = src_port; + e->key.d_port = dst_port; + e->key.proto = ulh->protocol; + + kv.value = (u64) (e - ls_mem->cache); + + ctx.now = now; + ctx.ls = ls_mem; + clib_bihash_add_or_overwrite_stale_40_8 (h, &kv, + ad_flow_is_idle_entry_cb, &ctx); + } + e->last_heard = now; + + /* Cache encapsulation headers */ + if (PREDICT_FALSE (encap_length > e->rw_len)) + { + vec_validate (e->rw_data, encap_length - 1); + } + clib_memcpy_fast (e->rw_data, ip, encap_length); + e->rw_len = encap_length; + + /* Update LRU */ + ad_flow_entry_update_lru (ls_mem, e); + + /* Decapsulate the packet */ + vlib_buffer_advance (b, encap_length); + + /* Set next node */ + *next = SRV6_AD_FLOW_LOCALSID_NEXT_REWRITE4; + + /* Set Xconnect adjacency to VNF */ + vnet_buffer (b)->ip.adj_index[VLIB_TX] = ls_mem->nh_adj; +} + +/** + * @brief SRv6 AD Localsid graph node + */ +static uword +srv6_ad_flow_localsid_fn (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) +{ + ip6_sr_main_t *srm = &sr_main; + f64 now = vlib_time_now (vm); + u32 n_left_from, next_index, *from, *to_next, n_left_to_next; + u32 thread_index = vm->thread_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* TODO: Dual/quad loop */ + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + ip6_header_t *ip0 = 0; + ip6_sr_localsid_t *ls0; + srv6_ad_flow_localsid_t *ls_mem0; + u32 next0; + vlib_combined_counter_main_t *cnt0 = &(srm->sr_ls_valid_counters); + u32 cnt_idx0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + ip0 = vlib_buffer_get_current (b0); + + /* Retrieve local SID context based on IP DA (adj) */ + ls0 = pool_elt_at_index (srm->localsids, + vnet_buffer (b0)->ip.adj_index[VLIB_TX]); + + cnt_idx0 = ls0 - srm->localsids; + + /* Retrieve local SID's plugin memory */ + ls_mem0 = ls0->plugin_mem; + + /* SRH processing */ + if (ls_mem0->inner_type == AD_TYPE_IP6) + end_ad_flow_processing_v6 (vm, b0, ip0, ls_mem0, &next0, &cnt0, + &cnt_idx0, now); + else + end_ad_flow_processing_v4 (vm, b0, ip0, ls_mem0, &next0, &cnt0, + &cnt_idx0, now); + + /* Trace packet (if enabled) */ + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + srv6_ad_flow_localsid_trace_t *tr = + vlib_add_trace (vm, node, b0, sizeof *tr); + tr->localsid_index = ls_mem0->index; + } + + /* Increment the appropriate per-SID counter */ + vlib_increment_combined_counter ( + cnt0, thread_index, cnt_idx0, 1, + vlib_buffer_length_in_chain (vm, b0)); + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (srv6_ad_flow_localsid_node) = { + .function = srv6_ad_flow_localsid_fn, + .name = "srv6-ad-flow-localsid", + .vector_size = sizeof (u32), + .format_trace = format_srv6_ad_flow_localsid_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_next_nodes = SRV6_AD_FLOW_LOCALSID_N_NEXT, + .next_nodes = { + [SRV6_AD_FLOW_LOCALSID_NEXT_PUNT] = "ip6-local", + [SRV6_AD_FLOW_LOCALSID_NEXT_BYPASS] = "ip6-lookup", + [SRV6_AD_FLOW_LOCALSID_NEXT_REWRITE4] = "ip4-rewrite", + [SRV6_AD_FLOW_LOCALSID_NEXT_REWRITE6] = "ip6-rewrite", + [SRV6_AD_FLOW_LOCALSID_NEXT_ERROR] = "error-drop", + }, +}; + +/****************************** Rewriting node *******************************/ + +/** + * @brief Graph node for applying a SR policy into an IPv6 packet. + * Encapsulation + */ +static uword +srv6_ad4_flow_rewrite_fn (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) +{ + ip6_sr_main_t *srm = &sr_main; + srv6_ad_flow_main_t *sm = &srv6_ad_flow_main; + u32 n_left_from, next_index, *from, *to_next; + u32 cnt_packets = 0; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* TODO: Dual/quad loop */ + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + ip4_header_t *ip0_encap = 0; + ip6_header_t *ip0 = 0; + ip6_sr_localsid_t *ls0; + srv6_ad_flow_localsid_t *ls0_mem; + srv6_ad_flow_entry_t *s0; + u32 next0 = SRV6_AD_FLOW_REWRITE_NEXT_LOOKUP; + u16 new_l0 = 0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + ip0_encap = vlib_buffer_get_current (b0); + ls0 = pool_elt_at_index ( + srm->localsids, + sm->sw_iface_localsid4[vnet_buffer (b0)->sw_if_index[VLIB_RX]]); + ls0_mem = ls0->plugin_mem; + + if (PREDICT_FALSE (ls0_mem == NULL)) + { + next0 = SRV6_AD_FLOW_REWRITE_NEXT_ERROR; + b0->error = node->errors[SRV6_AD_FLOW_REWRITE_COUNTER_NO_RW]; + } + else + { + clib_bihash_kv_40_8_t kv0, value0; + + /* Compute flow hash */ + u64 ports = 0; + if (PREDICT_TRUE (ip0_encap->protocol == IP_PROTOCOL_UDP || + ip0_encap->protocol == IP_PROTOCOL_TCP)) + { + udp_header_t *udp0 = (udp_header_t *) (ip0_encap + 1); + ports = + ((u64) udp0->src_port << 16) | ((u64) udp0->dst_port); + } + + kv0.key[0] = *((u64 *) &ip0_encap->address_pair); + kv0.key[1] = ports; + kv0.key[2] = 0; + kv0.key[3] = 0; + kv0.key[4] = 0; + + /* Lookup flow in hashtable */ + if (clib_bihash_search_40_8 (&ls0_mem->ftable, &kv0, &value0) < + 0) + { + /* not found */ + next0 = SRV6_AD_FLOW_REWRITE_NEXT_ERROR; + b0->error = node->errors[SRV6_AD_FLOW_REWRITE_COUNTER_NO_RW]; + } + else + { + /* found */ + s0 = pool_elt_at_index ( + ls0_mem->cache, ad_flow_value_get_session_index (&value0)); + ASSERT (s0); + ASSERT (VLIB_BUFFER_PRE_DATA_SIZE >= + (s0->rw_len + b0->current_data)); + + clib_memcpy_fast (((u8 *) ip0_encap) - s0->rw_len, + s0->rw_data, s0->rw_len); + vlib_buffer_advance (b0, -(word) s0->rw_len); + + ip0 = vlib_buffer_get_current (b0); + + /* Update inner IPv4 TTL and checksum */ + u32 checksum0; + ip0_encap->ttl -= 1; + checksum0 = + ip0_encap->checksum + clib_host_to_net_u16 (0x0100); + checksum0 += checksum0 >= 0xffff; + ip0_encap->checksum = checksum0; + + /* Update outer IPv6 length (in case it has changed) */ + new_l0 = s0->rw_len - sizeof (ip6_header_t) + + clib_net_to_host_u16 (ip0_encap->length); + ip0->payload_length = clib_host_to_net_u16 (new_l0); + } + } + + if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) && + PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + srv6_ad_flow_rewrite_trace_t *tr = + vlib_add_trace (vm, node, b0, sizeof *tr); + tr->error = 0; + + if (next0 == SRV6_AD_FLOW_REWRITE_NEXT_ERROR) + { + tr->error = 1; + } + else + { + clib_memcpy_fast (tr->src.as_u8, ip0->src_address.as_u8, + sizeof tr->src.as_u8); + clib_memcpy_fast (tr->dst.as_u8, ip0->dst_address.as_u8, + sizeof tr->dst.as_u8); + } + } + + /* Increment per-SID AD rewrite counters */ + vlib_increment_combined_counter ( + ((next0 == SRV6_AD_FLOW_REWRITE_NEXT_ERROR) ? + &(sm->rw_invalid_counters) : + &(sm->rw_valid_counters)), + vm->thread_index, ls0_mem->index, 1, + vlib_buffer_length_in_chain (vm, b0)); + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + + cnt_packets++; + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + /* Update counters */ + vlib_node_increment_counter (vm, srv6_ad4_flow_rewrite_node.index, + SRV6_AD_FLOW_REWRITE_COUNTER_PROCESSED, + cnt_packets); + + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (srv6_ad4_flow_rewrite_node) = { + .function = srv6_ad4_flow_rewrite_fn, + .name = "srv6-ad4-flow-rewrite", + .vector_size = sizeof (u32), + .format_trace = format_srv6_ad_flow_rewrite_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = SRV6_AD_FLOW_REWRITE_N_COUNTERS, + .error_strings = srv6_ad_flow_rewrite_counter_strings, + .n_next_nodes = SRV6_AD_FLOW_REWRITE_N_NEXT, + .next_nodes = { + [SRV6_AD_FLOW_REWRITE_NEXT_LOOKUP] = "ip6-lookup", + [SRV6_AD_FLOW_REWRITE_NEXT_ERROR] = "error-drop", + }, +}; + +/** + * @brief Graph node for applying a SR policy into an IPv6 packet. + * Encapsulation + */ +static uword +srv6_ad6_flow_rewrite_fn (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) +{ + ip6_sr_main_t *srm = &sr_main; + srv6_ad_flow_main_t *sm = &srv6_ad_flow_main; + u32 n_left_from, next_index, *from, *to_next; + u32 cnt_packets = 0; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* TODO: Dual/quad loop */ + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + ip6_header_t *ip0 = 0, *ip0_encap = 0; + ip6_sr_localsid_t *ls0; + srv6_ad_flow_localsid_t *ls0_mem; + srv6_ad_flow_entry_t *s0; + u32 next0 = SRV6_AD_FLOW_REWRITE_NEXT_LOOKUP; + u16 new_l0 = 0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + ip0_encap = vlib_buffer_get_current (b0); + ls0 = pool_elt_at_index ( + srm->localsids, + sm->sw_iface_localsid6[vnet_buffer (b0)->sw_if_index[VLIB_RX]]); + ls0_mem = ls0->plugin_mem; + + if (PREDICT_FALSE (ls0_mem == NULL)) + { + next0 = SRV6_AD_FLOW_REWRITE_NEXT_ERROR; + b0->error = node->errors[SRV6_AD_FLOW_REWRITE_COUNTER_NO_RW]; + } + else + { + /* ############################################# */ + clib_bihash_kv_40_8_t kv0, value0; + + /* Compute flow hash */ + u64 ports = 0; + if (PREDICT_TRUE (ip0_encap->protocol == IP_PROTOCOL_UDP || + ip0_encap->protocol == IP_PROTOCOL_TCP)) + { + udp_header_t *udp0 = (udp_header_t *) (ip0_encap + 1); + ports = + ((u64) udp0->src_port << 16) | ((u64) udp0->dst_port); + } + + kv0.key[0] = ip0_encap->src_address.as_u64[0]; + kv0.key[1] = ip0_encap->src_address.as_u64[1]; + kv0.key[2] = ip0_encap->dst_address.as_u64[0]; + kv0.key[3] = ip0_encap->dst_address.as_u64[1]; + kv0.key[4] = ports; + + /* Lookup flow in hashtable */ + if (clib_bihash_search_40_8 (&ls0_mem->ftable, &kv0, &value0)) + { + /* not found */ + next0 = SRV6_AD_FLOW_REWRITE_NEXT_ERROR; + b0->error = node->errors[SRV6_AD_FLOW_REWRITE_COUNTER_NO_RW]; + } + else + { + /* found */ + s0 = pool_elt_at_index ( + ls0_mem->cache, ad_flow_value_get_session_index (&value0)); + ASSERT (s0); + + ASSERT (VLIB_BUFFER_PRE_DATA_SIZE >= + (s0->rw_len + b0->current_data)); + + clib_memcpy_fast (((u8 *) ip0_encap) - s0->rw_len, + s0->rw_data, s0->rw_len); + vlib_buffer_advance (b0, -(word) s0->rw_len); + + ip0 = vlib_buffer_get_current (b0); + + /* Update inner IPv6 hop limit */ + ip0_encap->hop_limit -= 1; + + /* Update outer IPv6 length (in case it has changed) */ + new_l0 = s0->rw_len + + clib_net_to_host_u16 (ip0_encap->payload_length); + ip0->payload_length = clib_host_to_net_u16 (new_l0); + } + } + + if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) && + PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + srv6_ad_flow_rewrite_trace_t *tr = + vlib_add_trace (vm, node, b0, sizeof *tr); + tr->error = 0; + + if (next0 == SRV6_AD_FLOW_REWRITE_NEXT_ERROR) + { + tr->error = 1; + } + else + { + clib_memcpy_fast (tr->src.as_u8, ip0->src_address.as_u8, + sizeof tr->src.as_u8); + clib_memcpy_fast (tr->dst.as_u8, ip0->dst_address.as_u8, + sizeof tr->dst.as_u8); + } + } + + /* Increment per-SID AD rewrite counters */ + vlib_increment_combined_counter ( + ((next0 == SRV6_AD_FLOW_REWRITE_NEXT_ERROR) ? + &(sm->rw_invalid_counters) : + &(sm->rw_valid_counters)), + vm->thread_index, ls0_mem->index, 1, + vlib_buffer_length_in_chain (vm, b0)); + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + + cnt_packets++; + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + /* Update counters */ + vlib_node_increment_counter (vm, srv6_ad6_flow_rewrite_node.index, + SRV6_AD_FLOW_REWRITE_COUNTER_PROCESSED, + cnt_packets); + + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (srv6_ad6_flow_rewrite_node) = { + .function = srv6_ad6_flow_rewrite_fn, + .name = "srv6-ad6-flow-rewrite", + .vector_size = sizeof (u32), + .format_trace = format_srv6_ad_flow_rewrite_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = SRV6_AD_FLOW_REWRITE_N_COUNTERS, + .error_strings = srv6_ad_flow_rewrite_counter_strings, + .n_next_nodes = SRV6_AD_FLOW_REWRITE_N_NEXT, + .next_nodes = { + [SRV6_AD_FLOW_REWRITE_NEXT_LOOKUP] = "ip6-lookup", + [SRV6_AD_FLOW_REWRITE_NEXT_ERROR] = "error-drop", + }, +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |