aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/ipip/sixrd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vnet/ipip/sixrd.c')
-rw-r--r--src/vnet/ipip/sixrd.c519
1 files changed, 519 insertions, 0 deletions
diff --git a/src/vnet/ipip/sixrd.c b/src/vnet/ipip/sixrd.c
new file mode 100644
index 00000000000..c44c8345e30
--- /dev/null
+++ b/src/vnet/ipip/sixrd.c
@@ -0,0 +1,519 @@
+/*
+ * sixrd.c - 6RD specific functions (RFC5969)
+ *
+ * Copyright (c) 2018 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.
+ */
+
+/**
+ * This code supports the following sixrd modes:
+ *
+ * 32 EA bits (Complete IPv4 address is embedded):
+ * ea_bits_len = 32
+ * IPv4 suffix is embedded:
+ * ea_bits_len = < 32
+ * No embedded address bits (1:1 mode):
+ * ea_bits_len = 0
+ */
+
+#include "ipip.h"
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vnet/adj/adj.h>
+#include <vnet/adj/adj_delegate.h>
+#include <vnet/adj/adj_midchain.h>
+#include <vnet/dpo/lookup_dpo.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h> // Really needed?
+
+extern vlib_node_registration_t ip4_sixrd_node;
+
+/**
+ * Adj delegate data
+ */
+typedef struct sixrd_adj_delegate_t_
+{
+ u32 adj_index;
+ fib_node_t sixrd_node;
+ fib_node_index_t sixrd_fib_entry_index;
+ u32 sixrd_sibling;
+} sixrd_adj_delegate_t;
+
+/**
+ * Pool of delegate structs
+ */
+static sixrd_adj_delegate_t *sixrd_adj_delegate_pool;
+
+/**
+ * Adj delegate registered type
+ */
+static adj_delegate_type_t sixrd_adj_delegate_type;
+
+/**
+ * FIB node registered type
+ */
+static fib_node_type_t sixrd_fib_node_type;
+
+static inline sixrd_adj_delegate_t *
+sixrd_adj_from_base (adj_delegate_t * ad)
+{
+ if (ad == NULL)
+ return (NULL);
+ return (pool_elt_at_index (sixrd_adj_delegate_pool, ad->ad_index));
+}
+
+static inline const sixrd_adj_delegate_t *
+sixrd_adj_from_const_base (const adj_delegate_t * ad)
+{
+ if (ad == NULL)
+ {
+ return (NULL);
+ }
+ return (pool_elt_at_index (sixrd_adj_delegate_pool, ad->ad_index));
+}
+
+static void
+sixrd_fixup (vlib_main_t * vm, ip_adjacency_t * adj, vlib_buffer_t * b0,
+ const void *data)
+{
+ ip4_header_t *ip4 = vlib_buffer_get_current (b0);
+ ip6_header_t *ip6 = vlib_buffer_get_current (b0) + sizeof (ip4_header_t);
+ const ipip_tunnel_t *t = data;
+
+ ip4->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
+ ip4->dst_address.as_u32 =
+ sixrd_get_addr_net (t, ip6->dst_address.as_u64[0]);
+ ip4->checksum = ip4_header_checksum (ip4);
+}
+
+static void
+ip6ip_fixup (vlib_main_t * vm, ip_adjacency_t * adj, vlib_buffer_t * b0,
+ const void *data)
+{
+ const ipip_tunnel_t *t = data;
+ ip4_header_t *ip4 = vlib_buffer_get_current (b0);
+ ip4->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
+ ip4->dst_address.as_u32 =
+ sixrd_get_addr_net (t, adj->sub_type.nbr.next_hop.as_u64[0]);
+ ip4->checksum = ip4_header_checksum (ip4);
+}
+
+static u8 *
+sixrd_build_rewrite (vnet_main_t * vnm, u32 sw_if_index,
+ vnet_link_t link_type, const void *dst_address)
+{
+ u8 *rewrite = NULL;
+ ipip_tunnel_t *t;
+
+ t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
+ if (!t)
+ return 0;
+
+ vec_validate (rewrite, sizeof (ip4_header_t) - 1);
+ ip4_header_t *ip4 = (ip4_header_t *) rewrite;
+ ip4->ip_version_and_header_length = 0x45;
+ ip4->ttl = 64;
+ ip4->protocol = IP_PROTOCOL_IPV6;
+ /* fixup ip4 header length and checksum after-the-fact */
+ ip4->src_address.as_u32 = t->tunnel_src.ip4.as_u32;
+ ip4->dst_address.as_u32 = 0;
+ ip4->checksum = ip4_header_checksum (ip4);
+
+ return rewrite;
+}
+
+static void
+ip6ip_tunnel_stack (adj_index_t ai, u32 fib_entry_index)
+{
+ ip_adjacency_t *adj = adj_get (ai);
+ ipip_tunnel_t *t;
+ u32 sw_if_index = adj->rewrite_header.sw_if_index;
+
+ t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
+ if (!t)
+ return;
+
+ /*
+ * find the adjacency that is contributed by the FIB entry
+ * that this tunnel resolves via, and use it as the next adj
+ * in the midchain
+ */
+ if (vnet_hw_interface_get_flags (vnet_get_main (), t->hw_if_index) &
+ VNET_HW_INTERFACE_FLAG_LINK_UP)
+ {
+ adj_nbr_midchain_stack (ai,
+ fib_entry_contribute_ip_forwarding
+ (fib_entry_index));
+ }
+ else
+ {
+ adj_nbr_midchain_unstack (ai);
+ }
+}
+
+static void
+sixrd_tunnel_stack (adj_index_t ai, u32 fib_index)
+{
+ dpo_id_t dpo = DPO_INVALID;
+ ip_adjacency_t *adj = adj_get (ai);
+ u32 sw_if_index = adj->rewrite_header.sw_if_index;
+
+ ipip_tunnel_t *t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
+ if (!t)
+ return;
+
+ lookup_dpo_add_or_lock_w_fib_index (fib_index, DPO_PROTO_IP4,
+ LOOKUP_UNICAST, LOOKUP_INPUT_DST_ADDR,
+ LOOKUP_TABLE_FROM_CONFIG, &dpo);
+ adj_nbr_midchain_stack (ai, &dpo);
+}
+
+const static ip46_address_t sixrd_special_nh = {
+ .ip6 = {
+ .as_u64 = {
+ [0] = 0xffffffffffffffff,
+ [1] = 0xffffffffffffffff,
+ },
+ },
+};
+
+static void
+sixrd_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
+{
+ ip_adjacency_t *adj = adj_get (ai);
+ ipip_tunnel_t *t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
+
+ if (!memcmp (&sixrd_special_nh, &adj->sub_type.nbr.next_hop,
+ sizeof (sixrd_special_nh)))
+ {
+ adj_nbr_midchain_update_rewrite (ai, sixrd_fixup, t, ADJ_FLAG_NONE,
+ sixrd_build_rewrite (vnm, sw_if_index,
+ adj_get_link_type
+ (ai), NULL));
+ sixrd_tunnel_stack (ai, t->fib_index);
+ }
+ else
+ {
+ sixrd_adj_delegate_t *sixrd_ad;
+ ip4_address_t da4;
+
+ da4.as_u32 =
+ sixrd_get_addr_net (t, adj->sub_type.nbr.next_hop.as_u64[0]);
+
+ fib_prefix_t pfx = {
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_len = 32,
+ .fp_addr = {
+ .ip4 = da4,
+ }
+ ,
+ };
+
+ adj_nbr_midchain_update_rewrite (ai, ip6ip_fixup, t, ADJ_FLAG_NONE,
+ sixrd_build_rewrite (vnm, sw_if_index,
+ adj_get_link_type
+ (ai), NULL));
+
+ sixrd_ad =
+ sixrd_adj_from_base (adj_delegate_get (adj, sixrd_adj_delegate_type));
+ if (sixrd_ad == NULL)
+ {
+ pool_get (sixrd_adj_delegate_pool, sixrd_ad);
+ fib_node_init (&sixrd_ad->sixrd_node, sixrd_fib_node_type);
+ sixrd_ad->adj_index = ai;
+ sixrd_ad->sixrd_fib_entry_index =
+ fib_table_entry_special_add (t->fib_index, &pfx, FIB_SOURCE_RR,
+ FIB_ENTRY_FLAG_NONE);
+ sixrd_ad->sixrd_sibling =
+ fib_entry_child_add (sixrd_ad->sixrd_fib_entry_index,
+ sixrd_fib_node_type,
+ sixrd_ad - sixrd_adj_delegate_pool);
+
+ adj_delegate_add (adj, sixrd_adj_delegate_type,
+ sixrd_ad - sixrd_adj_delegate_pool);
+
+ ip6ip_tunnel_stack (ai, sixrd_ad->sixrd_fib_entry_index);
+ }
+ }
+}
+
+clib_error_t *
+sixrd_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
+{
+ /* Always up */
+ vnet_hw_interface_set_flags (vnm, hw_if_index,
+ VNET_HW_INTERFACE_FLAG_LINK_UP);
+ return /* no error */ 0;
+}
+
+/* *INDENT-OFF* */
+VNET_HW_INTERFACE_CLASS(sixrd_hw_interface_class) = {
+ .name = "ip6ip-6rd",
+ .build_rewrite = sixrd_build_rewrite,
+ .update_adjacency = sixrd_update_adj,
+};
+
+VNET_DEVICE_CLASS(sixrd_device_class) = {
+ .name = "ip6ip-6rd",
+ .admin_up_down_function = sixrd_interface_admin_up_down,
+#ifdef SOON
+ .clear counter = 0;
+#endif
+}
+;
+/* *INDENT-ON* */
+
+int
+sixrd_add_tunnel (ip6_address_t * ip6_prefix, u8 ip6_prefix_len,
+ ip4_address_t * ip4_prefix, u8 ip4_prefix_len,
+ ip4_address_t * ip4_src, bool security_check,
+ u32 fib_index, u32 * sw_if_index)
+{
+ ipip_main_t *gm = &ipip_main;
+ ipip_tunnel_t *t;
+
+ if (fib_index == ~0)
+ return VNET_API_ERROR_NO_SUCH_FIB;
+
+ if ((ip6_prefix_len + 32 - ip4_prefix_len) > 64)
+ return VNET_API_ERROR_INVALID_VALUE;
+
+ /* Tunnel already configured */
+ ip46_address_t src = ip46_address_initializer, dst =
+ ip46_address_initializer;
+ ip_set (&src, ip4_src, true);
+ ipip_tunnel_key_t key = {.transport = IPIP_TRANSPORT_IP4,
+ .fib_index = fib_index,
+ .src = src,
+ .dst = dst
+ };
+
+ t = ipip_tunnel_db_find (&key);
+ if (t)
+ return VNET_API_ERROR_IF_ALREADY_EXISTS;
+
+ /* Get tunnel index */
+ pool_get_aligned (gm->tunnels, t, CLIB_CACHE_LINE_BYTES);
+ memset (t, 0, sizeof (*t));
+ u32 t_idx = t - gm->tunnels; /* tunnel index (or instance) */
+
+ /* Init tunnel struct */
+ t->mode = IPIP_MODE_6RD;
+ t->sixrd.ip4_prefix.as_u32 = ip4_prefix->as_u32;
+ t->sixrd.ip4_prefix_len = ip4_prefix_len;
+ t->sixrd.ip6_prefix = *ip6_prefix;
+ t->sixrd.ip6_prefix_len = ip6_prefix_len;
+ t->tunnel_src = src;
+ t->sixrd.security_check = security_check;
+ t->sixrd.shift =
+ (ip4_prefix_len < 32) ? 64 - ip6_prefix_len - (32 - ip4_prefix_len) : 0;
+
+ /* Create interface */
+ u32 hw_if_index =
+ vnet_register_interface (vnet_get_main (), sixrd_device_class.index,
+ t_idx,
+ sixrd_hw_interface_class.index, t_idx);
+
+ /* Default the interface to up and enable IPv6 (payload) */
+ vnet_hw_interface_t *hi =
+ vnet_get_hw_interface (vnet_get_main (), hw_if_index);
+ t->hw_if_index = hw_if_index;
+ t->fib_index = fib_index;
+ t->sw_if_index = hi->sw_if_index;
+ t->dev_instance = t_idx;
+ t->user_instance = t_idx;
+
+ hi->max_l3_packet_bytes[VLIB_RX] = hi->max_l3_packet_bytes[VLIB_TX] = 1480;
+
+ ipip_tunnel_db_add (t, &key);
+
+ vec_validate_init_empty (gm->tunnel_index_by_sw_if_index, hi->sw_if_index,
+ ~0);
+ gm->tunnel_index_by_sw_if_index[hi->sw_if_index] = t_idx;
+
+ vnet_hw_interface_set_flags (vnet_get_main (), hw_if_index,
+ VNET_HW_INTERFACE_FLAG_LINK_UP);
+ vnet_sw_interface_set_flags (vnet_get_main (), hi->sw_if_index,
+ VNET_SW_INTERFACE_FLAG_ADMIN_UP);
+ ip6_sw_interface_enable_disable (hi->sw_if_index, true);
+
+ /* Create IPv6 route/adjacency */
+ fib_prefix_t pfx6 = {
+ .fp_proto = FIB_PROTOCOL_IP6,
+ .fp_len = t->sixrd.ip6_prefix_len,
+ .fp_addr = {
+ .ip6 = t->sixrd.ip6_prefix,
+ }
+ ,
+ };
+
+ fib_table_entry_update_one_path (fib_index, &pfx6, FIB_SOURCE_CLI,
+ FIB_ENTRY_FLAG_ATTACHED, DPO_PROTO_IP6,
+ &sixrd_special_nh, hi->sw_if_index, ~0, 1,
+ NULL, FIB_ROUTE_PATH_FLAG_NONE);
+
+ *sw_if_index = hi->sw_if_index;
+
+ if (!gm->ip4_protocol_registered)
+ {
+ vlib_node_t *ipip4_input =
+ vlib_get_node_by_name (gm->vlib_main, (u8 *) "ipip4-input");
+ ASSERT (ipip4_input);
+ ip4_register_protocol (IP_PROTOCOL_IPV6, ipip4_input->index);
+ }
+ return 0;
+}
+
+/*
+ * sixrd_del_tunnel
+ */
+int
+sixrd_del_tunnel (u32 sw_if_index)
+{
+ ipip_main_t *gm = &ipip_main;
+ ipip_tunnel_t *t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
+
+ if (!t)
+ {
+ clib_warning ("SIXRD tunnel delete: tunnel does not exist: %d",
+ sw_if_index);
+ return -1;
+ }
+
+ fib_prefix_t pfx6 = {
+ .fp_proto = FIB_PROTOCOL_IP6,
+ .fp_len = t->sixrd.ip6_prefix_len,
+ .fp_addr = {
+ .ip6 = t->sixrd.ip6_prefix,
+ }
+ ,
+ };
+ fib_table_entry_special_remove (0, &pfx6, FIB_SOURCE_CLI);
+ vnet_sw_interface_set_flags (vnet_get_main (), t->sw_if_index,
+ 0 /* down */ );
+ ip6_sw_interface_enable_disable (t->sw_if_index, false);
+ gm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0;
+
+ vnet_delete_hw_interface (vnet_get_main (), t->hw_if_index);
+ ipip_tunnel_db_remove (t);
+ pool_put (gm->tunnels, t);
+
+ return 0;
+}
+
+static void
+sixrd_adj_delegate_adj_deleted (adj_delegate_t * aed)
+{
+ sixrd_adj_delegate_t *sixrd_ad;
+
+ sixrd_ad = sixrd_adj_from_base (aed);
+ fib_entry_child_remove (sixrd_ad->sixrd_fib_entry_index,
+ sixrd_ad->sixrd_sibling);
+ fib_table_entry_delete_index (sixrd_ad->sixrd_fib_entry_index,
+ FIB_SOURCE_RR);
+ pool_put (sixrd_adj_delegate_pool, sixrd_ad);
+}
+
+static u8 *
+sixrd_adj_delegate_format (const adj_delegate_t * aed, u8 * s)
+{
+ const sixrd_adj_delegate_t *sixrd_ad;
+
+ sixrd_ad = sixrd_adj_from_const_base (aed);
+ s = format (s, "SIXRD:[fib-entry:%d]", sixrd_ad->sixrd_fib_entry_index);
+
+ return (s);
+}
+
+static void
+sixrd_fib_node_last_lock_gone (fib_node_t * node)
+{
+ /* top of the dependency tree, locks not managed here. */
+}
+
+static sixrd_adj_delegate_t *
+sixrd_adj_delegate_from_fib_node (fib_node_t * node)
+{
+ return ((sixrd_adj_delegate_t *) (((char *) node) -
+ STRUCT_OFFSET_OF (sixrd_adj_delegate_t,
+ sixrd_node)));
+}
+
+static fib_node_back_walk_rc_t
+sixrd_fib_node_back_walk_notify (fib_node_t * node,
+ fib_node_back_walk_ctx_t * ctx)
+{
+ sixrd_adj_delegate_t *sixrd_ad;
+
+ sixrd_ad = sixrd_adj_delegate_from_fib_node (node);
+ ip6ip_tunnel_stack (sixrd_ad->adj_index, sixrd_ad->sixrd_fib_entry_index);
+
+ return (FIB_NODE_BACK_WALK_CONTINUE);
+}
+
+/**
+ * Function definition to get a FIB node from its index
+ */
+static fib_node_t *
+sixrd_fib_node_get (fib_node_index_t index)
+{
+ sixrd_adj_delegate_t *sixrd_ad;
+
+ sixrd_ad = pool_elt_at_index (sixrd_adj_delegate_pool, index);
+
+ return (&sixrd_ad->sixrd_node);
+}
+
+/**
+ * VFT registered with the adjacency delegate
+ */
+const static adj_delegate_vft_t sixrd_adj_delegate_vft = {
+ .adv_adj_deleted = sixrd_adj_delegate_adj_deleted,
+ .adv_format = sixrd_adj_delegate_format,
+};
+
+/**
+ * VFT registered with the FIB node for the adj delegate
+ */
+const static fib_node_vft_t sixrd_fib_node_vft = {
+ .fnv_get = sixrd_fib_node_get,
+ .fnv_last_lock = sixrd_fib_node_last_lock_gone,
+ .fnv_back_walk = sixrd_fib_node_back_walk_notify,
+};
+
+static clib_error_t *
+sixrd_init (vlib_main_t * vm)
+{
+ clib_error_t *error = 0;
+
+ /* Make sure the IPIP tunnel subsystem is initialised */
+ vlib_call_init_function (vm, ipip_init);
+
+ sixrd_adj_delegate_type =
+ adj_delegate_register_new_type (&sixrd_adj_delegate_vft);
+ sixrd_fib_node_type = fib_node_register_new_type (&sixrd_fib_node_vft);
+
+ return error;
+}
+
+VLIB_INIT_FUNCTION (sixrd_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */