diff options
Diffstat (limited to 'src/examples/srv6-sample-localsid')
4 files changed, 531 insertions, 0 deletions
diff --git a/src/examples/srv6-sample-localsid/node.c b/src/examples/srv6-sample-localsid/node.c new file mode 100644 index 00000000..3ac7108b --- /dev/null +++ b/src/examples/srv6-sample-localsid/node.c @@ -0,0 +1,261 @@ +/* + * 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 <vnet/pg/pg.h> +#include <vppinfra/error.h> +#include <srv6-localsid/srv6_localsid_sample.h> + +typedef struct { + u32 localsid_index; +} srv6_localsid_sample_trace_t; + +/* packet trace format function */ +static u8 * format_srv6_localsid_sample_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_localsid_sample_trace_t * t = va_arg (*args, srv6_localsid_sample_trace_t *); + s = format (s, "SRv6-sample-localsid: localsid_index %d\n", + t->localsid_index); + return s; +} + +vlib_node_registration_t srv6_localsid_sample_node; + +#define foreach_srv6_localsid_counter \ +_(PROCESSED, "srv6-sample-localsid processed packets") \ +_(NO_SRH, "(Error) No SRH.") + +typedef enum { +#define _(sym,str) SRV6_LOCALSID_COUNTER_##sym, + foreach_srv6_localsid_counter +#undef _ + SRV6_LOCALSID_N_COUNTERS, +} srv6_localsid_sample_counters; + +static char * srv6_localsid_counter_strings[] = { +#define _(sym,string) string, + foreach_srv6_localsid_counter +#undef _ +}; + +typedef enum { + SRV6_SAMPLE_LOCALSID_NEXT_ERROR, + SRV6_SAMPLE_LOCALSID_NEXT_IP6LOOKUP, + SRV6_SAMPLE_LOCALSID_N_NEXT, +} srv6_localsid_sample_next_t; + +/** + * @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 SRv6 Sample Localsid graph node + * WARNING: YOU MUST DO THE DUAL LOOP + */ +static uword +srv6_localsid_sample_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + u32 next_index; + u32 pkts_swapped = 0; + + ip6_sr_main_t * sm = &sr_main; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + u32 thread_index = vlib_get_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); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + ip6_header_t * ip0 = 0; + ip6_sr_header_t * sr0; + ip6_ext_header_t *prev0 + u32 next0 = SRV6_SAMPLE_LOCALSID_NEXT_IP6LOOKUP; + ip6_sr_localsid_t *ls0; + srv6_localsid_sample_per_sid_memory_t *ls0_mem; + + 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_sr_header_t *)(ip0+1); + + /* 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]); + ls0_mem = ls0->plugin_mem; + + /* SRH processing */ + ip6_ext_header_find_t (ip0, prev0, sr0, IP_PROTOCOL_IPV6_ROUTE); + end_decaps_srh_processing (node, b0, ip0, sr0, ls0, &next0); + + /* ==================================================================== */ + /* INSERT CODE HERE */ + /* Example starts here */ + //In this example we are changing the next VRF table by the one in CLI + vnet_buffer(b0)->sw_if_index[VLIB_TX] = ls0_mem->fib_table; + /* Example finishes here */ + /* ==================================================================== */ + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + srv6_localsid_sample_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->localsid_index = ls0 - sm->localsids; + } + + /* This increments the SRv6 per LocalSID counters.*/ + vlib_increment_combined_counter + (((next0 == SRV6_SAMPLE_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); + + pkts_swapped ++; + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + + } + + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (srv6_localsid_sample_node) = { + .function = srv6_localsid_sample_fn, + .name = "srv6-localsid-sample", + .vector_size = sizeof (u32), + .format_trace = format_srv6_localsid_sample_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = SRV6_LOCALSID_N_COUNTERS, + .error_strings = srv6_localsid_counter_strings, + .n_next_nodes = SRV6_SAMPLE_LOCALSID_N_NEXT, + .next_nodes = { + [SRV6_SAMPLE_LOCALSID_NEXT_IP6LOOKUP] = "ip6-lookup", + [SRV6_SAMPLE_LOCALSID_NEXT_ERROR] = "error-drop", + }, +}; diff --git a/src/examples/srv6-sample-localsid/srv6_localsid_sample.c b/src/examples/srv6-sample-localsid/srv6_localsid_sample.c new file mode 100755 index 00000000..ec16547e --- /dev/null +++ b/src/examples/srv6-sample-localsid/srv6_localsid_sample.c @@ -0,0 +1,179 @@ +/* + * 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. + */ +/* + *------------------------------------------------------------------ + * srv6_localsid_sample.c - Simple SRv6 LocalSID + *------------------------------------------------------------------ + */ + +#include <vnet/vnet.h> +#include <vnet/plugin/plugin.h> +#include <srv6-localsid/srv6_localsid_sample.h> + +#include <vlibapi/api.h> +#include <vlibmemory/api.h> +#include <vlibsocket/api.h> + +unsigned char srv6_localsid_name[32] = "Sample-SRv6-LocalSID-plugin"; +unsigned char keyword_str[32] = "new_srv6_localsid"; +unsigned char def_str[64] = "This is a definition of a sample new_srv6_localsid"; +unsigned char params_str[32] = "<fib_table>"; + +/*****************************************/ +/* SRv6 LocalSID instantiation and removal functions */ +static int +srv6_localsid_creation_fn (ip6_sr_localsid_t *localsid) +{ + /* + * Do you want to do anything fancy upon localsid instantiation? + * You can do it here + * (If return != 0 the localsid creation will be cancelled.) + */ + /* As an example Im going to do a +1 to the fib table inserted by the user */ + srv6_localsid_sample_per_sid_memory_t *ls_mem = localsid->plugin_mem; + ls_mem->fib_table += 1; + return 0; +} + +static int +srv6_localsid_removal_fn (ip6_sr_localsid_t *localsid) +{ + /* Do you want to do anything fancy upon localsid removal? + * You can do it here + * (If return != 0 the localsid removal will be cancelled.) + */ + /* + * BTW if you stored something in localsid->plugin_mem you should clean it now + */ + + //In this example we are only cleaning the memory allocated per localsid + clib_mem_free(localsid->plugin_mem); + return 0; +} + +/**********************************/ +/* SRv6 LocalSID format functions */ +/* + * Prints nicely the parameters of a localsid + * Example: print "Table 5" + */ +u8 * +format_srv6_localsid_sample (u8 * s, va_list * args) +{ + srv6_localsid_sample_per_sid_memory_t *ls_mem = va_arg (*args, void *); + return (format (s, "Table: %u", ls_mem->fib_table)); +} + +/* + * Process the parameters of a localsid + * Example: process from: + * sr localsid address cafe::1 behavior new_srv6_localsid 5 + * everything from behavior on... so in this case 'new_srv6_localsid 5' + * Notice that it MUST match the keyword_str and params_str defined above. + */ +uword +unformat_srv6_localsid_sample (unformat_input_t * input, va_list * args) +{ + void **plugin_mem = va_arg (*args, void **); + srv6_localsid_sample_per_sid_memory_t *ls_mem; + u32 table_id; + if (unformat (input, "new_srv6_localsid %u", &table_id)) + { + /* Allocate a portion of memory */ + ls_mem = clib_mem_alloc_aligned_at_offset ( + sizeof(srv6_localsid_sample_per_sid_memory_t), 0, 0, 1); + + /* Set to zero the memory */ + memset (ls_mem, 0, sizeof(srv6_localsid_sample_per_sid_memory_t)); + + /* Our brand-new car is ready */ + ls_mem->fib_table = table_id; + + /* Dont forget to add it to the localsid */ + *plugin_mem = ls_mem; + return 1; + } + return 0; +} + +/*************************/ +/* SRv6 LocalSID FIB DPO */ +static u8 * +format_srv6_localsid_sample_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_sample_index:[%u]", index)); +} + +void +srv6_localsid_sample_dpo_lock (dpo_id_t * dpo) +{ +} + +void +srv6_localsid_sample_dpo_unlock (dpo_id_t * dpo) +{ +} + +const static dpo_vft_t srv6_localsid_sample_vft = { + .dv_lock = srv6_localsid_sample_dpo_lock, + .dv_unlock = srv6_localsid_sample_dpo_unlock, + .dv_format = format_srv6_localsid_sample_dpo, +}; + +const static char *const srv6_localsid_sample_ip6_nodes[] = { + "srv6-localsid-sample", + NULL, +}; + +const static char *const *const srv6_localsid_sample_nodes[DPO_PROTO_NUM] = { + [DPO_PROTO_IP6] = srv6_localsid_sample_ip6_nodes, +}; + +/**********************/ +static clib_error_t * srv6_localsid_sample_init (vlib_main_t * vm) +{ + srv6_localsid_sample_main_t * sm = &srv6_localsid_sample_main; + int rv = 0; + /* Create DPO */ + sm->srv6_localsid_sample_dpo_type = dpo_register_new_type ( + &srv6_localsid_sample_vft, srv6_localsid_sample_nodes); + + /* Register SRv6 LocalSID */ + rv = sr_localsid_register_function (vm, + srv6_localsid_name, + keyword_str, + def_str, + params_str, + &sm->srv6_localsid_sample_dpo_type, + format_srv6_localsid_sample, + unformat_srv6_localsid_sample, + srv6_localsid_creation_fn, + srv6_localsid_removal_fn); + if (rv < 0) + clib_error_return (0, "SRv6 LocalSID function could not be registered."); + else + sm->srv6_localsid_behavior_id = rv; + + return 0; +} + +VLIB_INIT_FUNCTION (srv6_localsid_sample_init); + +VLIB_PLUGIN_REGISTER () = { + .version = "1.0", +}; diff --git a/src/examples/srv6-sample-localsid/srv6_localsid_sample.h b/src/examples/srv6-sample-localsid/srv6_localsid_sample.h new file mode 100644 index 00000000..ef74ea3e --- /dev/null +++ b/src/examples/srv6-sample-localsid/srv6_localsid_sample.h @@ -0,0 +1,61 @@ +/* + * 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. + */ +#ifndef __included_srv6_localsid_sample_h__ +#define __included_srv6_localsid_sample_h__ + +#include <vnet/vnet.h> +#include <vnet/ip/ip.h> +#include <vnet/srv6/sr.h> +#include <vnet/srv6/sr_packet.h> + +#include <vppinfra/hash.h> +#include <vppinfra/error.h> +#include <vppinfra/elog.h> + +typedef struct { + /* API message ID base */ + u16 msg_id_base; + + /* convenience */ + vlib_main_t * vlib_main; + vnet_main_t * vnet_main; + + /* DPO type */ + dpo_type_t srv6_localsid_sample_dpo_type; + + /* SRv6 LocalSID behavior number */ + u32 srv6_localsid_behavior_id; + +} srv6_localsid_sample_main_t; + +/* + * This is the memory that will be stored per each localsid + * the user instantiates + */ +typedef struct { + u32 fib_table; /* Stupid index used as an example.. */ +} srv6_localsid_sample_per_sid_memory_t ; + +srv6_localsid_sample_main_t srv6_localsid_sample_main; + +format_function_t format_srv6_localsid_sample; +unformat_function_t unformat_srv6_localsid_sample; + +void srv6_localsid_sample_dpo_lock (dpo_id_t * dpo); +void srv6_localsid_sample_dpo_unlock (dpo_id_t * dpo); + +extern vlib_node_registration_t srv6_localsid_sample_node; + +#endif /* __included_sample_h__ */ diff --git a/src/examples/srv6-sample-localsid/srv6_sample_localsid_doc.md b/src/examples/srv6-sample-localsid/srv6_sample_localsid_doc.md new file mode 100644 index 00000000..cd717db8 --- /dev/null +++ b/src/examples/srv6-sample-localsid/srv6_sample_localsid_doc.md @@ -0,0 +1,30 @@ +# Sample SRv6 LocalSID documentation {#srv6_plugin_doc} + +## Introduction + +This plugin is an example of how an user can create a new SRv6 LocalSID behavior by using VPP plugins with the appropiate API calls to the existing SR code. + +This **example** plugin registers a new localsid behavior, with cli keyword 'new_srv6_localsid' which only takes one parameter, a fib-table. Upon recival of a packet, this plugin will enforce the next IP6 lookup in the specific fib-table specified by the user. (Indeed it will do the lookup in the fib_table n+1 (since for the shake of the example we increment the fib-table.) + +Notice that the plugin only 'defines' a new SRv6 LocalSID behavior, but the existing SR code in VNET is the one actually instantiating new LocalSIDs. Notice that there are callback functions such that when you create or remove a LocalSID you can actually setup specific parameters through the functions in this plugin. + +## Variables to watch for + +* srv6_localsid_name: This variable is the name (used as a unique key) identifying this SR LocalSID plugin. +* keyword_str: This is the CLI keyword to be used for the plugin. In this example 'new_srv6_localsid'. (i.e. sr localsid address cafe::1 behavior new_srv6_localsid <parameters>) +* def_str: This is a definition of this SR behavior. This is printed when you do 'show sr localsid behaviors'. +* params_str: This is a definition of the parameters of this localsid. This is printed when you do 'show sr localsid behaviors'. + +## Functions to watch for + +* srv6_localsid_creation_fn: This function will be called every time a new SR LocalSID is instantiated with the behavior defined in this plugin. +* srv6_localsid_removal_fn: This function will be called every time a new SR LocalSID is removed with the behavior defined in this plugin. This function tends to be used for freeing up all the memory created in the previous function. +* format_srv6_localsid_sample: This function prints nicely the parameters of every SR LocalSID using this behavior. +* unformat_srv6_localsid_sample: This function parses the CLI command when initialising a new SR LocalSID using this behavior. It parses all the parameters and ensures that the parameters are correct. +* format_srv6_localsid_sample_dpo: This function formats the 'show ip6 fib' message for the SR LocalSIDs created with this plugin behavior. + +## Graph node + +The current graph node uses the function 'end_srh_processing' to do the Segment Routing Endpoint behavior. Notice that it does not allow the cleanup of a Segment Routing header (as per the SRv6 behavior specs). +This function is identical to the one found in /src/vnet/srv6/sr_localsid.c +In case that by some other reason you want to do decapsulation, or SRH clean_up you can use the functions 'end_decaps_srh_processing' or 'end_psp_srh_processing' respectively. |