/* * 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 #include #include #include /****************************** 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: */