| /* |
| * 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> |
| |
| 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); |
| |
| /* Not our tunnel */ |
| if (!t) |
| return; |
| 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: |
| */ |