/* * sr_localsid.c: ipv6 segment routing Endpoint behaviors * * Copyright (c) 2016 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. */ /** * @file * @brief Processing of packets with a SRH * * CLI to define new Segment Routing End processing functions. * Graph node to support such functions. * * Each function associates an SRv6 segment (IPv6 address) with an specific * Segment Routing function. * */ #include <vlib/vlib.h> #include <vnet/vnet.h> #include <vnet/srv6/sr.h> #include <vnet/ip/ip.h> #include <vnet/srv6/sr_packet.h> #include <vnet/ip/ip6_packet.h> #include <vnet/fib/ip6_fib.h> #include <vnet/dpo/dpo.h> #include <vnet/adj/adj.h> #include <vppinfra/error.h> #include <vppinfra/elog.h> /** * @brief Dynamically added SR localsid DPO type */ static dpo_type_t sr_localsid_dpo_type; static dpo_type_t sr_localsid_d_dpo_type; /** * @brief SR localsid add/del * * Function to add or delete SR LocalSIDs. * * @param is_del Boolean of whether its a delete instruction * @param localsid_addr IPv6 address of the localsid * @param is_decap Boolean of whether decapsulation is allowed in this function * @param behavior Type of behavior (function) for this localsid * @param sw_if_index Only for L2/L3 xconnect. OIF. In VRF variant the fib_table. * @param vlan_index Only for L2 xconnect. Outgoing VLAN tag. * @param fib_table FIB table in which we should install the localsid entry * @param nh_addr Next Hop IPv4/IPv6 address. Only for L2/L3 xconnect. * * @return 0 on success, error otherwise. */ int sr_cli_localsid (char is_del, ip6_address_t * localsid_addr, u16 prefixlen, char end_psp, u8 behavior, u32 sw_if_index, u32 vlan_index, u32 fib_table, ip46_address_t * nh_addr, void *ls_plugin_mem) { ip6_sr_main_t *sm = &sr_main; uword *p; int rv; u8 pref_length = 128; sr_localsid_fn_registration_t *plugin = 0; ip6_sr_localsid_t *ls = 0; dpo_id_t dpo = DPO_INVALID; /* Search for the item */ p = mhash_get (&sm->sr_localsids_index_hash, localsid_addr); if (p) { if (is_del) { /* Retrieve localsid */ ls = pool_elt_at_index (sm->localsids, p[0]); if (ls->behavior >= SR_BEHAVIOR_LAST) { plugin = pool_elt_at_index (sm->plugin_functions, ls->behavior - SR_BEHAVIOR_LAST); pref_length = plugin->prefix_length; } if (prefixlen != 0) { pref_length = prefixlen; } /* Delete FIB entry */ fib_prefix_t pfx = { .fp_proto = FIB_PROTOCOL_IP6, .fp_len = pref_length, .fp_addr = { .ip6 = *localsid_addr, } }; fib_table_entry_delete (fib_table_find (FIB_PROTOCOL_IP6, fib_table), &pfx, FIB_SOURCE_SR); /* In case it is a Xconnect iface remove the (OIF, NHOP) adj */ if (ls->behavior == SR_BEHAVIOR_X || ls->behavior == SR_BEHAVIOR_DX6 || ls->behavior == SR_BEHAVIOR_DX4) adj_unlock (ls->nh_adj); if (ls->behavior >= SR_BEHAVIOR_LAST) { /* Callback plugin removal function */ rv = plugin->removal (ls); } /* Delete localsid registry */ pool_put (sm->localsids, ls); mhash_unset (&sm->sr_localsids_index_hash, localsid_addr, NULL); return 0; } else /* create with function already existing; complain */ return -1; } else /* delete; localsid does not exist; complain */ if (is_del) return -2; if (behavior >= SR_BEHAVIOR_LAST) { sr_localsid_fn_registration_t *plugin = 0; plugin = pool_elt_at_index (sm->plugin_functions, behavior - SR_BEHAVIOR_LAST); pref_length = plugin->prefix_length; } /* Check whether there exists a FIB entry with such address */ fib_prefix_t pfx = { .fp_proto = FIB_PROTOCOL_IP6, .fp_len = pref_length, }; pfx.fp_addr.as_u64[0] = localsid_addr->as_u64[0]; pfx.fp_addr.as_u64[1] = localsid_addr->as_u64[1]; if (prefixlen != 0) { pfx.fp_len = prefixlen; } /* Lookup the FIB index associated to the table id provided */ u32 fib_index = fib_table_find (FIB_PROTOCOL_IP6, fib_table); if (fib_index == ~0) return -3; /* Lookup the localsid in such FIB table */ fib_node_index_t fei = fib_table_lookup_exact_match (fib_index, &pfx); if (FIB_NODE_INDEX_INVALID != fei) return -4; //There is an entry for such address (the localsid addr) /* Create a new localsid registry */ pool_get (sm->localsids, ls); clib_memset (ls, 0, sizeof (*ls)); clib_memcpy (&ls->localsid, localsid_addr, sizeof (ip6_address_t)); ls->end_psp = end_psp; ls->behavior = behavior; ls->nh_adj = (u32) ~ 0; ls->fib_table = fib_table; ls->localsid_len = pfx.fp_len; switch (behavior) { case SR_BEHAVIOR_END: break; case SR_BEHAVIOR_X: ls->sw_if_index = sw_if_index; clib_memcpy (&ls->next_hop.ip6, &nh_addr->ip6, sizeof (ip6_address_t)); break; case SR_BEHAVIOR_T: ls->vrf_index = fib_table_find (FIB_PROTOCOL_IP6, sw_if_index); break; case SR_BEHAVIOR_DX4: ls->sw_if_index = sw_if_index; clib_memcpy (&ls->next_hop.ip4, &nh_addr->ip4, sizeof (ip4_address_t)); break; case SR_BEHAVIOR_DX6: ls->sw_if_index = sw_if_index; clib_memcpy (&ls->next_hop.ip6, &nh_addr->ip6, sizeof (ip6_address_t)); break; case SR_BEHAVIOR_DT6: ls->vrf_index = fib_table_find (FIB_PROTOCOL_IP6, sw_if_index); break; case SR_BEHAVIOR_DT4: ls->vrf_index = fib_table_find (FIB_PROTOCOL_IP4, sw_if_index); break; case SR_BEHAVIOR_DX2: ls->sw_if_index = sw_if_index; ls->vlan_index = vlan_index; break; } /* Figure out the adjacency magic for Xconnect variants */ if (ls->behavior == SR_BEHAVIOR_X || ls->behavior == SR_BEHAVIOR_DX4 || ls->behavior == SR_BEHAVIOR_DX6) { adj_index_t nh_adj_index = ADJ_INDEX_INVALID; /* Retrieve the adjacency corresponding to the (OIF, next_hop) */ if (ls->behavior == SR_BEHAVIOR_DX6 || ls->behavior == SR_BEHAVIOR_X) nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6, VNET_LINK_IP6, nh_addr, sw_if_index); else if (ls->behavior == SR_BEHAVIOR_DX4) nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4, nh_addr, sw_if_index); /* Check for ADJ creation error. If so panic */ if (nh_adj_index == ADJ_INDEX_INVALID) { pool_put (sm->localsids, ls); return -5; } ls->nh_adj = nh_adj_index; } /* Set DPO */ if (ls->behavior == SR_BEHAVIOR_END || ls->behavior == SR_BEHAVIOR_X || ls->behavior == SR_BEHAVIOR_T) dpo_set (&dpo, sr_localsid_dpo_type, DPO_PROTO_IP6, ls - sm->localsids); else if (ls->behavior > SR_BEHAVIOR_D_FIRST && ls->behavior < SR_BEHAVIOR_LAST) dpo_set (&dpo, sr_localsid_d_dpo_type, DPO_PROTO_IP6, ls - sm->localsids); else if (ls->behavior >= SR_BEHAVIOR_LAST) { sr_localsid_fn_registration_t *plugin = 0; plugin = pool_elt_at_index (sm->plugin_functions, ls->behavior - SR_BEHAVIOR_LAST); /* Copy the unformat memory result */ ls->plugin_mem = ls_plugin_mem; /* Callback plugin creation function */ rv = plugin->creation (ls); if (rv) { pool_put (sm->localsids, ls); return -6; } dpo_set (&dpo, plugin->dpo, DPO_PROTO_IP6, ls - sm->localsids); } /* Set hash key for searching localsid by address */ mhash_set (&sm->sr_localsids_index_hash, localsid_addr, ls - sm->localsids, NULL); fib_table_entry_special_dpo_add (fib_index, &pfx, FIB_SOURCE_SR, FIB_ENTRY_FLAG_EXCLUSIVE, &dpo); dpo_reset (&dpo); /* Set counter to zero */ vlib_validate_combined_counter (&(sm->sr_ls_valid_counters), ls - sm->localsids); vlib_validate_combined_counter (&(sm->sr_ls_invalid_counters), ls - sm->localsids); vlib_zero_combined_counter (&(sm->sr_ls_valid_counters), ls - sm->localsids); vlib_zero_combined_counter (&(sm->sr_ls_invalid_counters), ls - sm->localsids); return 0; } /** * @brief SR LocalSID CLI function. * * @see sr_cli_localsid */ static clib_error_t * sr_cli_localsid_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { vnet_main_t *vnm = vnet_get_main (); ip6_sr_main_t *sm = &sr_main; u32 sw_if_index = (u32) ~ 0, vlan_index = (u32) ~ 0, fib_index = 0; u16 prefix_len = 0; int is_del = 0; int end_psp = 0; ip6_address_t resulting_address; ip46_address_t next_hop; char address_set = 0; char behavior = 0; void *ls_plugin_mem = 0; int rv; clib_memset (&resulting_address, 0, sizeof (ip6_address_t)); ip46_address_reset (&next_hop); while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "del")) is_del = 1; else if (!address_set && unformat (input, "address %U", unformat_ip6_address, &resulting_address)) address_set = 1; else if (!address_set && unformat (input, "prefix %U/%d", unformat_ip6_address, &resulting_address, &prefix_len)) address_set = 1; else if (!address_set && unformat (input, "addr %U", unformat_ip6_address, &resulting_address)) address_set = 1; else if (unformat (input, "fib-table %u", &fib_index)); else if (vlan_index == (u32) ~ 0 && unformat (input, "vlan %u", &vlan_index)); else if (!behavior && unformat (input, "behavior")) { if (unformat (input, "end.x %U %U", unformat_vnet_sw_interface, vnm, &sw_if_index, unformat_ip6_address, &next_hop.ip6)) behavior = SR_BEHAVIOR_X; else if (unformat (input, "end.t %u", &sw_if_index)) behavior = SR_BEHAVIOR_T; else if (unformat (input, "end.dx6 %U %U", unformat_vnet_sw_interface, vnm, &sw_if_index, unformat_ip6_address, &next_hop.ip6)) behavior = SR_BEHAVIOR_DX6; else if (unformat (input, "end.dx4 %U %U", unformat_vnet_sw_interface, vnm, &sw_if_index, unformat_ip4_address, &next_hop.ip4)) behavior = SR_BEHAVIOR_DX4; else if (unformat (input, "end.dx2 %U", unformat_vnet_sw_interface, vnm, &sw_if_index)) behavior = SR_BEHAVIOR_DX2; else if (unformat (input, "end.dt6 %u", &sw_if_index)) behavior = SR_BEHAVIOR_DT6; else if (unformat (input, "end.dt4 %u", &sw_if_index)) behavior = SR_BEHAVIOR_DT4; else { /* Loop over all the plugin behavior format functions */ sr_localsid_fn_registration_t *plugin = 0, **vec_plugins = 0; sr_localsid_fn_registration_t **plugin_it = 0; /* Create a vector out of the plugin pool as recommended */ /* *INDENT-OFF* */ pool_foreach (plugin, sm->plugin_functions, { vec_add1 (vec_plugins, plugin); }); /* *INDENT-ON* */ vec_foreach (plugin_it, vec_plugins) { if (unformat (input, "%U", (*plugin_it)->ls_unformat, &ls_plugin_mem)) { behavior = (*plugin_it)->sr_localsid_function_number; break; } } } if (!behavior) { if (unformat (input, "end")) behavior = SR_BEHAVIOR_END; else break; } } else if (!end_psp && unformat (input, "psp")) end_psp = 1; else break; } if (!behavior && end_psp) behavior = SR_BEHAVIOR_END; if (!address_set) return clib_error_return (0, "Error: SRv6 LocalSID address is mandatory."); if (!is_del && !behavior) return clib_error_return (0, "Error: SRv6 LocalSID behavior is mandatory."); if (vlan_index != (u32) ~ 0) return clib_error_return (0, "Error: SRv6 End.DX2 with rewrite VLAN tag not supported by now."); if (end_psp && !(behavior == SR_BEHAVIOR_END || behavior == SR_BEHAVIOR_X)) return clib_error_return (0, "Error: SRv6 PSP only compatible with End and End.X"); rv = sr_cli_localsid (is_del, &resulting_address, prefix_len, end_psp, behavior, sw_if_index, vlan_index, fib_index, &next_hop, ls_plugin_mem); switch (rv) { case 0: break; case 1: return 0; case -1: return clib_error_return (0, "Identical localsid already exists. Requested localsid not created."); case -2: return clib_error_return (0, "The requested localsid could not be deleted. SR localsid not found"); case -3: return clib_error_return (0, "FIB table %u does not exist", fib_index); case -4: return clib_error_return (0, "There is already one FIB entry for the" "requested localsid non segment routing related"); case -5: return clib_error_return (0, "Could not create ARP/ND entry for such next_hop. Internal error."); case -6: return clib_error_return (0, "Error on the plugin based localsid creation."); default: return clib_error_return (0, "BUG: sr localsid returns %d", rv); } return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (sr_localsid_command, static) = { .path = "sr localsid", .short_help = "sr localsid (del) address XX:XX::YY:YY" "(fib-table 8) behavior STRING", .long_help = "Create SR LocalSID and binds it to a particular behavior\n" "Arguments:\n" "\tlocalSID IPv6_addr(128b) LocalSID IPv6 address\n" "\t(fib-table X) Optional. VRF where to install SRv6 localsid\n" "\tbehavior STRING Specifies the behavior\n" "\n\tBehaviors:\n" "\tEnd\t-> Endpoint.\n" "\tEnd.X\t-> Endpoint with decapsulation and Layer-3 cross-connect.\n" "\t\tParameters: '<iface> <ip6_next_hop>'\n" "\tEnd.DX2\t-> Endpoint with decapsulation and Layer-2 cross-connect.\n" "\t\tParameters: '<iface>'\n" "\tEnd.DX6\t-> Endpoint with decapsulation and IPv6 cross-connect.\n" "\t\tParameters: '<iface> <ip6_next_hop>'\n" "\tEnd.DX4\t-> Endpoint with decapsulation and IPv4 cross-connect.\n" "\t\tParameters: '<iface> <ip4_next_hop>'\n" "\tEnd.DT6\t-> Endpoint with decapsulation and specific IPv6 table lookup.\n" "\t\tParameters: '<ip6_fib_table>'\n" "\tEnd.DT4\t-> Endpoint with decapsulation and specific IPv4 table lookup.\n" "\t\tParameters: '<ip4_fib_table>'\n", .function = sr_cli_localsid_command_fn, }; /* *INDENT-ON* */ /** * @brief CLI function to 'show' all SR LocalSIDs on console. */ static clib_error_t * show_sr_localsid_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { vnet_main_t *vnm = vnet_get_main (); ip6_sr_main_t *sm = &sr_main; ip6_sr_localsid_t **localsid_list = 0; ip6_sr_localsid_t *ls; int i; vlib_cli_output (vm, "SRv6 - My LocalSID Table:"); vlib_cli_output (vm, "========================="); /* *INDENT-OFF* */ pool_foreach (ls, sm->localsids, ({ vec_add1 (localsid_list, ls); })); /* *INDENT-ON* */ for (i = 0; i < vec_len (localsid_list); i++) { ls = localsid_list[i]; switch (ls->behavior) { case SR_BEHAVIOR_END: vlib_cli_output (vm, "\tAddress: \t%U\n\tBehavior: \tEnd", format_ip6_address, &ls->localsid); break; case SR_BEHAVIOR_X: vlib_cli_output (vm, "\tAddress: \t%U\n\tBehavior: \tX (Endpoint with Layer-3 cross-connect)" "\n\tIface: \t%U\n\tNext hop: \t%U", format_ip6_address, &ls->localsid, format_vnet_sw_if_index_name, vnm, ls->sw_if_index, format_ip6_address, &ls->next_hop.ip6); break; case SR_BEHAVIOR_T: vlib_cli_output (vm, "\tAddress: \t%U\n\tBehavior: \tT (Endpoint with specific IPv6 table lookup)" "\n\tTable: \t%u", format_ip6_address, &ls->localsid, ls->vrf_index); break; case SR_BEHAVIOR_DX4: vlib_cli_output (vm, "\tAddress: \t%U\n\tBehavior: \tDX4 (Endpoint with decapsulation and IPv4 cross-connect)" "\n\tIface: \t%U\n\tNext hop: \t%U", format_ip6_address, &ls->localsid, format_vnet_sw_if_index_name, vnm, ls->sw_if_index, format_ip4_address, &ls->next_hop.ip4); break; case SR_BEHAVIOR_DX6: vlib_cli_output (vm, "\tAddress: \t%U\n\tBehavior: \tDX6 (Endpoint with decapsulation and IPv6 cross-connect)" "\n\tIface: \t%U\n\tNext hop: \t%U", format_ip6_address, &ls->localsid, format_vnet_sw_if_index_name, vnm, ls->sw_if_index, format_ip6_address, &ls->next_hop.ip6); break; case SR_BEHAVIOR_DX2: if (ls->vlan_index == (u32) ~ 0) vlib_cli_output (vm, "\tAddress: \t%U\n\tBehavior: \tDX2 (Endpoint with decapulation and Layer-2 cross-connect)" "\n\tIface: \t%U", format_ip6_address, &ls->localsid, format_vnet_sw_if_index_name, vnm, ls->sw_if_index); else vlib_cli_output (vm, "Unsupported yet. (DX2 with egress VLAN rewrite)"); break; case SR_BEHAVIOR_DT6: vlib_cli_output (vm, "\tAddress: \t%U\n\tBehavior: \tDT6 (Endpoint with decapsulation and specific IPv6 table lookup)" "\n\tTable: %u", format_ip6_address, &ls->localsid, ls->vrf_index); break; case SR_BEHAVIOR_DT4: vlib_cli_output (vm, "\tAddress: \t%U\n\tBehavior: \tDT4 (Endpoint with decapsulation and specific IPv4 table lookup)" "\n\tTable: \t%u", format_ip6_address, &ls->localsid, ls->vrf_index); break; default: if (ls->behavior >= SR_BEHAVIOR_LAST) { sr_localsid_fn_registration_t *plugin = pool_elt_at_index (sm->plugin_functions, ls->behavior - SR_BEHAVIOR_LAST); vlib_cli_output (vm, "\tAddress: \t%U\n" "\tBehavior: \t%s (%s)\n\t%U", format_ip6_address, &ls->localsid, plugin->keyword_str, plugin->def_str, plugin->ls_format, ls->plugin_mem); } else //Should never get here... vlib_cli_output (vm, "Internal error"); break; } if (ls->end_psp) vlib_cli_output (vm, "\tPSP: \tTrue\n"); /* Print counters */ vlib_counter_t valid, invalid; vlib_get_combined_counter (&(sm->sr_ls_valid_counters), i, &valid); vlib_get_combined_counter (&(sm->sr_ls_invalid_counters), i, &invalid); vlib_cli_output (vm, "\tGood traffic: \t[%Ld packets : %Ld bytes]\n", valid.packets, valid.bytes); vlib_cli_output (vm, "\tBad traffic: \t[%Ld packets : %Ld bytes]\n", invalid.packets, invalid.bytes); vlib_cli_output (vm, "--------------------"); } return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_sr_localsid_command, static) = { .path = "show sr localsids", .short_help = "show sr localsids", .function = show_sr_localsid_command_fn, }; /* *INDENT-ON* */ /** * @brief Function to 'clear' ALL SR localsid counters */ static clib_error_t * clear_sr_localsid_counters_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { ip6_sr_main_t *sm = &sr_main; vlib_clear_combined_counters (&(sm->sr_ls_valid_counters)); vlib_clear_combined_counters (&(sm->sr_ls_invalid_counters)); return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (clear_sr_localsid_counters_command, static) = { .path = "clear sr localsid-counters", .short_help = "clear sr localsid-counters", .function = clear_sr_localsid_counters_command_fn, }; /* *INDENT-ON* */ /************************ SR LocalSID graphs node ****************************/ /** * @brief SR localsid node trace */ typedef struct { ip6_address_t localsid; u16 behavior; u8 sr[256]; u8 num_segments; u8 segments_left; } sr_localsid_trace_t; #define foreach_sr_localsid_error \ _(NO_INNER_HEADER, "(SR-Error) No inner IP header") \ _(NO_MORE_SEGMENTS, "(SR-Error) No more segments") \ _(NO_SRH, "(SR-Error) No SR header") \ _(NO_PSP, "(SR-Error) PSP Not available (segments left > 0)") \ _(NOT_LS, "(SR-Error) Decaps not available (segments left > 0)") \ _(L2, "(SR-Error) SRv6 decapsulated a L2 frame without dest") typedef enum { #define _(sym,str) SR_LOCALSID_ERROR_##sym, foreach_sr_localsid_error #undef _ SR_LOCALSID_N_ERROR, } sr_localsid_error_t; static char *sr_localsid_error_strings[] = { #define _(sym,string) string, foreach_sr_localsid_error #undef _ }; #define foreach_sr_localsid_next \ _(ERROR, "error-drop") \ _(IP6_LOOKUP, "ip6-lookup") \ _(IP4_LOOKUP, "ip4-lookup") \ _(IP6_REWRITE, "ip6-rewrite") \ _(IP4_REWRITE, "ip4-rewrite") \ _(INTERFACE_OUTPUT, "interface-output") typedef enum { #define _(s,n) SR_LOCALSID_NEXT_##s, foreach_sr_localsid_next #undef _ SR_LOCALSID_N_NEXT, } sr_localsid_next_t; /** * @brief SR LocalSID graph node trace function * * @see sr_localsid */ u8 * format_sr_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 *); sr_localsid_trace_t *t = va_arg (*args, sr_localsid_trace_t *); s = format (s, "SR-LOCALSID:\n\tLocalsid: %U\n", format_ip6_address, &t->localsid); switch (t->behavior) { case SR_BEHAVIOR_END: s = format (s, "\tBehavior: End\n"); break; case SR_BEHAVIOR_DX6: s = format (s, "\tBehavior: Decapsulation with IPv6 L3 xconnect\n"); break; case SR_BEHAVIOR_DX4: s = format (s, "\tBehavior: Decapsulation with IPv4 L3 xconnect\n"); break; case SR_BEHAVIOR_X: s = format (s, "\tBehavior: IPv6 L3 xconnect\n"); break; case SR_BEHAVIOR_T: s = format (s, "\tBehavior: IPv6 specific table lookup\n"); break; case SR_BEHAVIOR_DT6: s = format (s, "\tBehavior: Decapsulation with IPv6 Table lookup\n"); break; case SR_BEHAVIOR_DT4: s = format (s, "\tBehavior: Decapsulation with IPv4 Table lookup\n"); break; case SR_BEHAVIOR_DX2: s = format (s, "\tBehavior: Decapsulation with L2 xconnect\n"); break; default: s = format (s, "\tBehavior: defined in plugin\n"); //TODO break; } if (t->num_segments != 0xFF) { if (t->num_segments > 0) { s = format (s, "\tSegments left: %d\n", t->segments_left); s = format (s, "\tSID list: [in ietf order]"); int i = 0; for (i = 0; i < t->num_segments; i++) { s = format (s, "\n\t-> %U", format_ip6_address, (ip6_address_t *) & t->sr[i * sizeof (ip6_address_t)]); } } } return s; } /** * @brief Function doing End processing. */ static_always_inline void end_srh_processing (vlib_node_runtime_t * node, vlib_buffer_t * b0, ip6_header_t * ip0, ip6_sr_header_t * sr0, ip6_sr_localsid_t * ls0, u32 * next0, u8 psp, ip6_ext_header_t * prev0) { ip6_address_t *new_dst0; if (PREDICT_TRUE (sr0->type == ROUTING_HEADER_TYPE_SR)) { if (sr0->segments_left == 1 && psp) { u32 new_l0, sr_len; u64 *copy_dst0, *copy_src0; u32 copy_len_u64s0 = 0; ip0->dst_address.as_u64[0] = sr0->segments->as_u64[0]; ip0->dst_address.as_u64[1] = sr0->segments->as_u64[1]; /* Remove the SRH taking care of the rest of IPv6 ext header */ if (prev0) prev0->next_hdr = sr0->protocol; else ip0->protocol = sr0->protocol; sr_len = ip6_ext_header_len (sr0); vlib_buffer_advance (b0, sr_len); new_l0 = clib_net_to_host_u16 (ip0->payload_length) - sr_len; ip0->payload_length = clib_host_to_net_u16 (new_l0); copy_src0 = (u64 *) ip0; copy_dst0 = copy_src0 + (sr0->length + 1); /* number of 8 octet units to copy * By default in absence of extension headers it is equal to length of ip6 header * With extension headers it number of 8 octet units of ext headers preceding * SR header */ copy_len_u64s0 = (((u8 *) sr0 - (u8 *) ip0) - sizeof (ip6_header_t)) >> 3; copy_dst0[4 + copy_len_u64s0] = copy_src0[4 + copy_len_u64s0]; copy_dst0[3 + copy_len_u64s0] = copy_src0[3 + copy_len_u64s0]; copy_dst0[2 + copy_len_u64s0] = copy_src0[2 + copy_len_u64s0]; copy_dst0[1 + copy_len_u64s0] = copy_src0[1 + copy_len_u64s0]; copy_dst0[0 + copy_len_u64s0] = copy_src0[0 + copy_len_u64s0]; int i; for (i = copy_len_u64s0 - 1; i >= 0; i--) { copy_dst0[i] = copy_src0[i]; } if (ls0->behavior == SR_BEHAVIOR_X) { vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ls0->nh_adj; *next0 = SR_LOCALSID_NEXT_IP6_REWRITE; } else if (ls0->behavior == SR_BEHAVIOR_T) { vnet_buffer (b0)->sw_if_index[VLIB_TX] = ls0->vrf_index; } } else if (PREDICT_TRUE (sr0->segments_left > 0)) { sr0->segments_left -= 1; new_dst0 = (ip6_address_t *) (sr0->segments); new_dst0 += sr0->segments_left; ip0->dst_address.as_u64[0] = new_dst0->as_u64[0]; ip0->dst_address.as_u64[1] = new_dst0->as_u64[1]; if (ls0->behavior == SR_BEHAVIOR_X) { vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ls0->nh_adj; *next0 = SR_LOCALSID_NEXT_IP6_REWRITE; } else if (ls0->behavior == SR_BEHAVIOR_T) { vnet_buffer (b0)->sw_if_index[VLIB_TX] = ls0->vrf_index; } } else { *next0 = SR_LOCALSID_NEXT_ERROR; b0->error = node->errors[SR_LOCALSID_ERROR_NO_MORE_SEGMENTS]; } } else { /* Error. Routing header of type != SR */ *next0 = SR_LOCALSID_NEXT_ERROR; b0->error = node->errors[SR_LOCALSID_ERROR_NO_SRH]; } } /* * @brief Function doing SRH processing for D* variants */ static_always_inline void end_decaps_srh_processing (vlib_node_runtime_t * node, vlib_buffer_t * b0, ip6_header_t * ip0, ip6_sr_header_t * sr0, ip6_sr_localsid_t * ls0, u32 * next0) { /* Compute the size of the IPv6 header with all Ext. headers */ u8 next_proto; ip6_ext_header_t *next_ext_header; u16 total_size = 0; next_proto = ip0->protocol; next_ext_header = (void *) (ip0 + 1); total_size = sizeof (ip6_header_t); while (ip6_ext_hdr (next_proto)) { total_size += ip6_ext_header_len (next_ext_header); next_proto = next_ext_header->next_hdr; next_ext_header = ip6_ext_next_header (next_ext_header); } /* Ensure this is the last segment. Otherwise drop. */ if (sr0 && sr0->segments_left != 0) { *next0 = SR_LOCALSID_NEXT_ERROR; b0->error = node->errors[SR_LOCALSID_ERROR_NOT_LS]; return; } switch (next_proto) { case IP_PROTOCOL_IPV6: /* Encap-End IPv6. Pop outer IPv6 header. */ if (ls0->behavior == SR_BEHAVIOR_DX6) { vlib_buffer_advance (b0, total_size); vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ls0->nh_adj; *next0 = SR_LOCALSID_NEXT_IP6_REWRITE; return; } else if (ls0->behavior == SR_BEHAVIOR_DT6) { vlib_buffer_advance (b0, total_size); vnet_buffer (b0)->sw_if_index[VLIB_TX] = ls0->vrf_index; return; } break; case IP_PROTOCOL_IP_IN_IP: /* Encap-End IPv4. Pop outer IPv6 header */ if (ls0->behavior == SR_BEHAVIOR_DX4) { vlib_buffer_advance (b0, total_size); vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ls0->nh_adj; *next0 = SR_LOCALSID_NEXT_IP4_REWRITE; return; } else if (ls0->behavior == SR_BEHAVIOR_DT4) { vlib_buffer_advance (b0, total_size); vnet_buffer (b0)->sw_if_index[VLIB_TX] = ls0->vrf_index; *next0 = SR_LOCALSID_NEXT_IP4_LOOKUP; return; } break; case IP_PROTOCOL_IP6_NONXT: /* L2 encaps */ if (ls0->behavior == SR_BEHAVIOR_DX2) { vlib_buffer_advance (b0, total_size); vnet_buffer (b0)->sw_if_index[VLIB_TX] = ls0->sw_if_index; *next0 = SR_LOCALSID_NEXT_INTERFACE_OUTPUT; return; } break; } *next0 = SR_LOCALSID_NEXT_ERROR; b0->error = node->errors[SR_LOCALSID_ERROR_NO_INNER_HEADER]; return; } /** * @brief SR LocalSID graph node. Supports all default SR Endpoint variants with decaps */ static uword sr_localsid_d_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { u32 n_left_from, next_index, *from, *to_next; ip6_sr_main_t *sm = &sr_main; from = vlib_frame_vector_args (from_frame); n_left_from = from_frame->n_vectors; next_index = node->cached_next_index; u32 thread_index = vm->thread_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); /* Quad - Loop */ while (n_left_from >= 8 && n_left_to_next >= 4) { u32 bi0, bi1, bi2, bi3; vlib_buffer_t *b0, *b1, *b2, *b3; ip6_header_t *ip0, *ip1, *ip2, *ip3; ip6_sr_header_t *sr0, *sr1, *sr2, *sr3; u32 next0, next1, next2, next3; next0 = next1 = next2 = next3 = SR_LOCALSID_NEXT_IP6_LOOKUP; ip6_sr_localsid_t *ls0, *ls1, *ls2, *ls3; /* Prefetch next iteration. */ { vlib_buffer_t *p4, *p5, *p6, *p7; p4 = vlib_get_buffer (vm, from[4]); p5 = vlib_get_buffer (vm, from[5]); p6 = vlib_get_buffer (vm, from[6]); p7 = vlib_get_buffer (vm, from[7]); /* Prefetch the buffer header and packet for the N+4 loop iteration */ vlib_prefetch_buffer_header (p4, LOAD); vlib_prefetch_buffer_header (p5, LOAD); vlib_prefetch_buffer_header (p6, LOAD); vlib_prefetch_buffer_header (p7, LOAD); CLIB_PREFETCH (p4->data, CLIB_CACHE_LINE_BYTES, STORE); CLIB_PREFETCH (p5->data, CLIB_CACHE_LINE_BYTES, STORE); CLIB_PREFETCH (p6->data, CLIB_CACHE_LINE_BYTES, STORE); CLIB_PREFETCH (p7->data, CLIB_CACHE_LINE_BYTES, STORE); } to_next[0] = bi0 = from[0]; to_next[1] = bi1 = from[1]; to_next[2] = bi2 = from[2]; to_next[3] = bi3 = from[3]; from += 4; to_next += 4; n_left_from -= 4; n_left_to_next -= 4; b0 = vlib_get_buffer (vm, bi0); b1 = vlib_get_buffer (vm, bi1); b2 = vlib_get_buffer (vm, bi2); b3 = vlib_get_buffer (vm, bi3); ls0 = pool_elt_at_index (sm->localsids, vnet_buffer (b0)->ip.adj_index[VLIB_TX]); ls1 = pool_elt_at_index (sm->localsids, vnet_buffer (b1)->ip.adj_index[VLIB_TX]); ls2 = pool_elt_at_index (sm->localsids, vnet_buffer (b2)->ip.adj_index[VLIB_TX]); ls3 = pool_elt_at_index (sm->localsids, vnet_buffer (b3)->ip.adj_index[VLIB_TX]); ip0 = vlib_buffer_get_current (b0); ip1 = vlib_buffer_get_current (b1); ip2 = vlib_buffer_get_current (b2); ip3 = vlib_buffer_get_current (b3); sr0 = ip6_ext_header_find (vm, b0, ip0, IP_PROTOCOL_IPV6_ROUTE, NULL); sr1 = ip6_ext_header_find (vm, b1, ip1, IP_PROTOCOL_IPV6_ROUTE, NULL); sr2 = ip6_ext_header_find (vm, b2, ip2, IP_PROTOCOL_IPV6_ROUTE, NULL); sr3 = ip6_ext_header_find (vm, b3, ip3, IP_PROTOCOL_IPV6_ROUTE, NULL); end_decaps_srh_processing (node, b0, ip0, sr0, ls0, &next0); end_decaps_srh_processing (node, b1, ip1, sr1, ls1, &next1); end_decaps_srh_processing (node, b2, ip2, sr2, ls2, &next2); end_decaps_srh_processing (node, b3, ip3, sr3, ls3, &next3); if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) { sr_localsid_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); tr->num_segments = 0; clib_memcpy (tr->localsid.as_u8, ls0->localsid.as_u8, sizeof (tr->localsid.as_u8)); tr->behavior = ls0->behavior; if (ip0 == vlib_buffer_get_current (b0)) { if (ip0->protocol == IP_PROTOCOL_IPV6_ROUTE && sr0->type == ROUTING_HEADER_TYPE_SR) { clib_memcpy (tr->sr, sr0->segments, sr0->length * 8); tr->num_segments = sr0->length * 8 / sizeof (ip6_address_t); tr->segments_left = sr0->segments_left; } } else tr->num_segments = 0xFF; } if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED)) { sr_localsid_trace_t *tr = vlib_add_trace (vm, node, b1, sizeof (*tr)); tr->num_segments = 0; clib_memcpy (tr->localsid.as_u8, ls1->localsid.as_u8, sizeof (tr->localsid.as_u8)); tr->behavior = ls1->behavior; if (ip1 == vlib_buffer_get_current (b1)) { if (ip1->protocol == IP_PROTOCOL_IPV6_ROUTE && sr1->type == ROUTING_HEADER_TYPE_SR) { clib_memcpy (tr->sr, sr1->segments, sr1->length * 8); tr->num_segments = sr1->length * 8 / sizeof (ip6_address_t); tr->segments_left = sr1->segments_left; } } else tr->num_segments = 0xFF; } if (PREDICT_FALSE (b2->flags & VLIB_BUFFER_IS_TRACED)) { sr_localsid_trace_t *tr = vlib_add_trace (vm, node, b2, sizeof (*tr)); tr->num_segments = 0; clib_memcpy (tr->localsid.as_u8, ls2->localsid.as_u8, sizeof (tr->localsid.as_u8)); tr->behavior = ls2->behavior; if (ip2 == vlib_buffer_get_current (b2)) { if (ip2->protocol == IP_PROTOCOL_IPV6_ROUTE && sr2->type == ROUTING_HEADER_TYPE_SR) { clib_memcpy (tr->sr, sr2->segments, sr2->length * 8); tr->num_segments = sr2->length * 8 / sizeof (ip6_address_t); tr->segments_left = sr2->segments_left; } } else tr->num_segments = 0xFF; } if (PREDICT_FALSE (b3->flags & VLIB_BUFFER_IS_TRACED)) { sr_localsid_trace_t *tr = vlib_add_trace (vm, node, b3, sizeof (*tr)); tr->num_segments = 0; clib_memcpy (tr->localsid.as_u8, ls3->localsid.as_u8, sizeof (tr->localsid.as_u8)); tr->behavior = ls3->behavior; if (ip3 == vlib_buffer_get_current (b3)) { if (ip3->protocol == IP_PROTOCOL_IPV6_ROUTE && sr3->type == ROUTING_HEADER_TYPE_SR) { clib_memcpy (tr->sr, sr3->segments, sr3->length * 8); tr->num_segments = sr3->length * 8 / sizeof (ip6_address_t); tr->segments_left = sr3->segments_left; } } else tr->num_segments = 0xFF; } vlib_increment_combined_counter (((next0 == SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) : &(sm->sr_ls_valid_counters)), thread_index, ls0 - sm->localsids, 1, vlib_buffer_length_in_chain (vm, b0)); vlib_increment_combined_counter (((next1 == SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) : &(sm->sr_ls_valid_counters)), thread_index, ls1 - sm->localsids, 1, vlib_buffer_length_in_chain (vm, b1)); vlib_increment_combined_counter (((next2 == SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) : &(sm->sr_ls_valid_counters)), thread_index, ls2 - sm->localsids, 1, vlib_buffer_length_in_chain (vm, b2)); vlib_increment_combined_counter (((next3 == SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) : &(sm->sr_ls_valid_counters)), thread_index, ls3 - sm->localsids, 1, vlib_buffer_length_in_chain (vm, b3)); vlib_validate_buffer_enqueue_x4 (vm, node, next_index, to_next, n_left_to_next, bi0, bi1, bi2, bi3, next0, next1, next2, next3); } /* Single loop for potentially the last three packets */ while (n_left_from > 0 && n_left_to_next > 0) { u32 bi0; vlib_buffer_t *b0; ip6_header_t *ip0; ip6_sr_header_t *sr0; u32 next0 = SR_LOCALSID_NEXT_IP6_LOOKUP; ip6_sr_localsid_t *ls0; 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); /* Lookup the SR End behavior based on IP DA (adj) */ ls0 = pool_elt_at_index (sm->localsids, vnet_buffer (b0)->ip.adj_index[VLIB_TX]); /* Find SRH as well as previous header */ sr0 = ip6_ext_header_find (vm, b0, ip0, IP_PROTOCOL_IPV6_ROUTE, NULL); /* SRH processing and End variants */ end_decaps_srh_processing (node, b0, ip0, sr0, ls0, &next0); if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) { sr_localsid_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); tr->num_segments = 0; clib_memcpy (tr->localsid.as_u8, ls0->localsid.as_u8, sizeof (tr->localsid.as_u8)); tr->behavior = ls0->behavior; if (ip0 == vlib_buffer_get_current (b0)) { if (ip0->protocol == IP_PROTOCOL_IPV6_ROUTE && sr0->type == ROUTING_HEADER_TYPE_SR) { clib_memcpy (tr->sr, sr0->segments, sr0->length * 8); tr->num_segments = sr0->length * 8 / sizeof (ip6_address_t); tr->segments_left = sr0->segments_left; } } else tr->num_segments = 0xFF; } /* Increase the counters */ vlib_increment_combined_counter (((next0 == SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) : &(sm->sr_ls_valid_counters)), thread_index, ls0 - sm->localsids, 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 from_frame->n_vectors; } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (sr_localsid_d_node) = { .function = sr_localsid_d_fn, .name = "sr-localsid-d", .vector_size = sizeof (u32), .format_trace = format_sr_localsid_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = SR_LOCALSID_N_ERROR, .error_strings = sr_localsid_error_strings, .n_next_nodes = SR_LOCALSID_N_NEXT, .next_nodes = { #define _(s,n) [SR_LOCALSID_NEXT_##s] = n, foreach_sr_localsid_next #undef _ }, }; /* *INDENT-ON* */ /** * @brief SR LocalSID graph node. Supports all default SR Endpoint without decaps */ static uword sr_localsid_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { u32 n_left_from, next_index, *from, *to_next; ip6_sr_main_t *sm = &sr_main; from = vlib_frame_vector_args (from_frame); n_left_from = from_frame->n_vectors; next_index = node->cached_next_index; u32 thread_index = vm->thread_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); /* Quad - Loop */ while (n_left_from >= 8 && n_left_to_next >= 4) { u32 bi0, bi1, bi2, bi3; vlib_buffer_t *b0, *b1, *b2, *b3; ip6_header_t *ip0, *ip1, *ip2, *ip3; ip6_sr_header_t *sr0, *sr1, *sr2, *sr3; ip6_ext_header_t *prev0, *prev1, *prev2, *prev3; u32 next0, next1, next2, next3; next0 = next1 = next2 = next3 = SR_LOCALSID_NEXT_IP6_LOOKUP; ip6_sr_localsid_t *ls0, *ls1, *ls2, *ls3; /* Prefetch next iteration. */ { vlib_buffer_t *p4, *p5, *p6, *p7; p4 = vlib_get_buffer (vm, from[4]); p5 = vlib_get_buffer (vm, from[5]); p6 = vlib_get_buffer (vm, from[6]); p7 = vlib_get_buffer (vm, from[7]); /* Prefetch the buffer header and packet for the N+2 loop iteration */ vlib_prefetch_buffer_header (p4, LOAD); vlib_prefetch_buffer_header (p5, LOAD); vlib_prefetch_buffer_header (p6, LOAD); vlib_prefetch_buffer_header (p7, LOAD); CLIB_PREFETCH (p4->data, CLIB_CACHE_LINE_BYTES, STORE); CLIB_PREFETCH (p5->data, CLIB_CACHE_LINE_BYTES, STORE); CLIB_PREFETCH (p6->data, CLIB_CACHE_LINE_BYTES, STORE); CLIB_PREFETCH (p7->data, CLIB_CACHE_LINE_BYTES, STORE); } to_next[0] = bi0 = from[0]; to_next[1] = bi1 = from[1]; to_next[2] = bi2 = from[2]; to_next[3] = bi3 = from[3]; from += 4; to_next += 4; n_left_from -= 4; n_left_to_next -= 4; b0 = vlib_get_buffer (vm, bi0); b1 = vlib_get_buffer (vm, bi1); b2 = vlib_get_buffer (vm, bi2); b3 = vlib_get_buffer (vm, bi3); ip0 = vlib_buffer_get_current (b0); ip1 = vlib_buffer_get_current (b1); ip2 = vlib_buffer_get_current (b2); ip3 = vlib_buffer_get_current (b3); sr0 = ip6_ext_header_find (vm, b0, ip0, IP_PROTOCOL_IPV6_ROUTE, &prev0); sr1 = ip6_ext_header_find (vm, b1, ip1, IP_PROTOCOL_IPV6_ROUTE, &prev1); sr2 = ip6_ext_header_find (vm, b2, ip2, IP_PROTOCOL_IPV6_ROUTE, &prev2); sr3 = ip6_ext_header_find (vm, b3, ip3, IP_PROTOCOL_IPV6_ROUTE, &prev3); ls0 = pool_elt_at_index (sm->localsids, vnet_buffer (b0)->ip.adj_index[VLIB_TX]); ls1 = pool_elt_at_index (sm->localsids, vnet_buffer (b1)->ip.adj_index[VLIB_TX]); ls2 = pool_elt_at_index (sm->localsids, vnet_buffer (b2)->ip.adj_index[VLIB_TX]); ls3 = pool_elt_at_index (sm->localsids, vnet_buffer (b3)->ip.adj_index[VLIB_TX]); end_srh_processing (node, b0, ip0, sr0, ls0, &next0, ls0->end_psp, prev0); end_srh_processing (node, b1, ip1, sr1, ls1, &next1, ls1->end_psp, prev1); end_srh_processing (node, b2, ip2, sr2, ls2, &next2, ls2->end_psp, prev2); end_srh_processing (node, b3, ip3, sr3, ls3, &next3, ls3->end_psp, prev3); if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) { sr_localsid_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); tr->num_segments = 0; clib_memcpy (tr->localsid.as_u8, ls0->localsid.as_u8, sizeof (tr->localsid.as_u8)); tr->behavior = ls0->behavior; if (ip0 == vlib_buffer_get_current (b0)) { if (ip0->protocol == IP_PROTOCOL_IPV6_ROUTE && sr0->type == ROUTING_HEADER_TYPE_SR) { clib_memcpy (tr->sr, sr0->segments, sr0->length * 8); tr->num_segments = sr0->length * 8 / sizeof (ip6_address_t); tr->segments_left = sr0->segments_left; } } else tr->num_segments = 0xFF; } if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED)) { sr_localsid_trace_t *tr = vlib_add_trace (vm, node, b1, sizeof (*tr)); tr->num_segments = 0; clib_memcpy (tr->localsid.as_u8, ls1->localsid.as_u8, sizeof (tr->localsid.as_u8)); tr->behavior = ls1->behavior; if (ip1 == vlib_buffer_get_current (b1)) { if (ip1->protocol == IP_PROTOCOL_IPV6_ROUTE && sr1->type == ROUTING_HEADER_TYPE_SR) { clib_memcpy (tr->sr, sr1->segments, sr1->length * 8); tr->num_segments = sr1->length * 8 / sizeof (ip6_address_t); tr->segments_left = sr1->segments_left; } } else tr->num_segments = 0xFF; } if (PREDICT_FALSE (b2->flags & VLIB_BUFFER_IS_TRACED)) { sr_localsid_trace_t *tr = vlib_add_trace (vm, node, b2, sizeof (*tr)); tr->num_segments = 0; clib_memcpy (tr->localsid.as_u8, ls2->localsid.as_u8, sizeof (tr->localsid.as_u8)); tr->behavior = ls2->behavior; if (ip2 == vlib_buffer_get_current (b2)) { if (ip2->protocol == IP_PROTOCOL_IPV6_ROUTE && sr2->type == ROUTING_HEADER_TYPE_SR) { clib_memcpy (tr->sr, sr2->segments, sr2->length * 8); tr->num_segments = sr2->length * 8 / sizeof (ip6_address_t); tr->segments_left = sr2->segments_left; } } else tr->num_segments = 0xFF; } if (PREDICT_FALSE (b3->flags & VLIB_BUFFER_IS_TRACED)) { sr_localsid_trace_t *tr = vlib_add_trace (vm, node, b3, sizeof (*tr)); tr->num_segments = 0; clib_memcpy (tr->localsid.as_u8, ls3->localsid.as_u8, sizeof (tr->localsid.as_u8)); tr->behavior = ls3->behavior; if (ip3 == vlib_buffer_get_current (b3)) { if (ip3->protocol == IP_PROTOCOL_IPV6_ROUTE && sr3->type == ROUTING_HEADER_TYPE_SR) { clib_memcpy (tr->sr, sr3->segments, sr3->length * 8); tr->num_segments = sr3->length * 8 / sizeof (ip6_address_t); tr->segments_left = sr3->segments_left; } } else tr->num_segments = 0xFF; } vlib_increment_combined_counter (((next0 == SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) : &(sm->sr_ls_valid_counters)), thread_index, ls0 - sm->localsids, 1, vlib_buffer_length_in_chain (vm, b0)); vlib_increment_combined_counter (((next1 == SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) : &(sm->sr_ls_valid_counters)), thread_index, ls1 - sm->localsids, 1, vlib_buffer_length_in_chain (vm, b1)); vlib_increment_combined_counter (((next2 == SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) : &(sm->sr_ls_valid_counters)), thread_index, ls2 - sm->localsids, 1, vlib_buffer_length_in_chain (vm, b2)); vlib_increment_combined_counter (((next3 == SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) : &(sm->sr_ls_valid_counters)), thread_index, ls3 - sm->localsids, 1, vlib_buffer_length_in_chain (vm, b3)); vlib_validate_buffer_enqueue_x4 (vm, node, next_index, to_next, n_left_to_next, bi0, bi1, bi2, bi3, next0, next1, next2, next3); } /* Single loop for potentially the last three packets */ while (n_left_from > 0 && n_left_to_next > 0) { u32 bi0; vlib_buffer_t *b0; ip6_header_t *ip0 = 0; ip6_ext_header_t *prev0; ip6_sr_header_t *sr0; u32 next0 = SR_LOCALSID_NEXT_IP6_LOOKUP; ip6_sr_localsid_t *ls0; 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); sr0 = ip6_ext_header_find (vm, b0, ip0, IP_PROTOCOL_IPV6_ROUTE, &prev0); /* Lookup the SR End behavior based on IP DA (adj) */ ls0 = pool_elt_at_index (sm->localsids, vnet_buffer (b0)->ip.adj_index[VLIB_TX]); /* SRH processing */ end_srh_processing (node, b0, ip0, sr0, ls0, &next0, ls0->end_psp, prev0); if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) { sr_localsid_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); tr->num_segments = 0; clib_memcpy (tr->localsid.as_u8, ls0->localsid.as_u8, sizeof (tr->localsid.as_u8)); tr->behavior = ls0->behavior; if (ip0 == vlib_buffer_get_current (b0)) { if (ip0->protocol == IP_PROTOCOL_IPV6_ROUTE && sr0->type == ROUTING_HEADER_TYPE_SR) { clib_memcpy (tr->sr, sr0->segments, sr0->length * 8); tr->num_segments = sr0->length * 8 / sizeof (ip6_address_t); tr->segments_left = sr0->segments_left; } } else tr->num_segments = 0xFF; } vlib_increment_combined_counter (((next0 == SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) : &(sm->sr_ls_valid_counters)), thread_index, ls0 - sm->localsids, 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 from_frame->n_vectors; } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (sr_localsid_node) = { .function = sr_localsid_fn, .name = "sr-localsid", .vector_size = sizeof (u32), .format_trace = format_sr_localsid_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = SR_LOCALSID_N_ERROR, .error_strings = sr_localsid_error_strings, .n_next_nodes = SR_LOCALSID_N_NEXT, .next_nodes = { #define _(s,n) [SR_LOCALSID_NEXT_##s] = n, foreach_sr_localsid_next #undef _ }, }; /* *INDENT-ON* */ static u8 * format_sr_dpo (u8 * s, va_list * args) { index_t index = va_arg (*args, index_t); CLIB_UNUSED (u32 indent) = va_arg (*args, u32); return (format (s, "SR: localsid_index:[%d]", index)); } const static dpo_vft_t sr_loc_vft = { .dv_lock = sr_dpo_lock, .dv_unlock = sr_dpo_unlock, .dv_format = format_sr_dpo, }; const static char *const sr_loc_ip6_nodes[] = { "sr-localsid", NULL, }; const static char *const *const sr_loc_nodes[DPO_PROTO_NUM] = { [DPO_PROTO_IP6] = sr_loc_ip6_nodes, }; const static char *const sr_loc_d_ip6_nodes[] = { "sr-localsid-d", NULL, }; const static char *const *const sr_loc_d_nodes[DPO_PROTO_NUM] = { [DPO_PROTO_IP6] = sr_loc_d_ip6_nodes, }; /*************************** SR LocalSID plugins ******************************/ /** * @brief SR LocalSID plugin registry */ int sr_localsid_register_function (vlib_main_t * vm, u8 * fn_name, u8 * keyword_str, u8 * def_str, u8 * params_str, u8 prefix_length, dpo_type_t * dpo, format_function_t * ls_format, unformat_function_t * ls_unformat, sr_plugin_callback_t * creation_fn, sr_plugin_callback_t * removal_fn) { ip6_sr_main_t *sm = &sr_main; uword *p; sr_localsid_fn_registration_t *plugin; /* Did this function exist? If so update it */ p = hash_get_mem (sm->plugin_functions_by_key, fn_name); if (p) { plugin = pool_elt_at_index (sm->plugin_functions, p[0]); } /* Else create a new one and set hash key */ else { pool_get (sm->plugin_functions, plugin); hash_set_mem (sm->plugin_functions_by_key, fn_name, plugin - sm->plugin_functions); } clib_memset (plugin, 0, sizeof (*plugin)); plugin->sr_localsid_function_number = (plugin - sm->plugin_functions); plugin->sr_localsid_function_number += SR_BEHAVIOR_LAST; plugin->prefix_length = prefix_length; plugin->ls_format = ls_format; plugin->ls_unformat = ls_unformat; plugin->creation = creation_fn; plugin->removal = removal_fn; clib_memcpy (&plugin->dpo, dpo, sizeof (dpo_type_t)); plugin->function_name = format (0, "%s%c", fn_name, 0); plugin->keyword_str = format (0, "%s%c", keyword_str, 0); plugin->def_str = format (0, "%s%c", def_str, 0); plugin->params_str = format (0, "%s%c", params_str, 0); return plugin->sr_localsid_function_number; } /** * @brief CLI function to 'show' all available SR LocalSID behaviors */ static clib_error_t * show_sr_localsid_behaviors_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { ip6_sr_main_t *sm = &sr_main; sr_localsid_fn_registration_t *plugin; sr_localsid_fn_registration_t **plugins_vec = 0; int i; vlib_cli_output (vm, "SR LocalSIDs behaviors:\n-----------------------\n\n"); /* *INDENT-OFF* */ pool_foreach (plugin, sm->plugin_functions, ({ vec_add1 (plugins_vec, plugin); })); /* *INDENT-ON* */ /* Print static behaviors */ vlib_cli_output (vm, "Default behaviors:\n" "\tEnd\t-> Endpoint.\n" "\tEnd.X\t-> Endpoint with Layer-3 cross-connect.\n" "\t\tParameters: '<iface> <ip6_next_hop>'\n" "\tEnd.T\t-> Endpoint with specific IPv6 table lookup.\n" "\t\tParameters: '<fib_table>'\n" "\tEnd.DX2\t-> Endpoint with decapsulation and Layer-2 cross-connect.\n" "\t\tParameters: '<iface>'\n" "\tEnd.DX6\t-> Endpoint with decapsulation and IPv6 cross-connect.\n" "\t\tParameters: '<iface> <ip6_next_hop>'\n" "\tEnd.DX4\t-> Endpoint with decapsulation and IPv4 cross-connect.\n" "\t\tParameters: '<iface> <ip4_next_hop>'\n" "\tEnd.DT6\t-> Endpoint with decapsulation and specific IPv6 table lookup.\n" "\t\tParameters: '<ip6_fib_table>'\n" "\tEnd.DT4\t-> Endpoint with decapsulation and specific IPv4 table lookup.\n" "\t\tParameters: '<ip4_fib_table>'\n"); vlib_cli_output (vm, "Plugin behaviors:\n"); for (i = 0; i < vec_len (plugins_vec); i++) { plugin = plugins_vec[i]; vlib_cli_output (vm, "\t%s\t-> %s.\n", plugin->keyword_str, plugin->def_str); vlib_cli_output (vm, "\t\tParameters: '%s'\n", plugin->params_str); } return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_sr_localsid_behaviors_command, static) = { .path = "show sr localsids behaviors", .short_help = "show sr localsids behaviors", .function = show_sr_localsid_behaviors_command_fn, }; /* *INDENT-ON* */ /** * @brief SR LocalSID initialization */ clib_error_t * sr_localsids_init (vlib_main_t * vm) { /* Init memory for function keys */ ip6_sr_main_t *sm = &sr_main; mhash_init (&sm->sr_localsids_index_hash, sizeof (uword), sizeof (ip6_address_t)); /* Init SR behaviors DPO type */ sr_localsid_dpo_type = dpo_register_new_type (&sr_loc_vft, sr_loc_nodes); /* Init SR behaviors DPO type */ sr_localsid_d_dpo_type = dpo_register_new_type (&sr_loc_vft, sr_loc_d_nodes); /* Init memory for localsid plugins */ sm->plugin_functions_by_key = hash_create_string (0, sizeof (uword)); return 0; } VLIB_INIT_FUNCTION (sr_localsids_init); /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */