From: Neale Ranns Date: Thu, 7 Feb 2019 15:26:12 +0000 (-0800) Subject: ipsec: ipsec-tun protect X-Git-Tag: v20.01-rc0~386 X-Git-Url: https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commitdiff_plain;h=c87b66c86201458c0475d50c6e93f1497f9eec2e ipsec: ipsec-tun protect please consult the new tunnel proposal at: https://wiki.fd.io/view/VPP/IPSec Type: feature Change-Id: I52857fc92ae068b85f59be08bdbea1bd5932e291 Signed-off-by: Neale Ranns --- diff --git a/doxygen/user_doc.md b/doxygen/user_doc.md index d607d39f619..f39bc10c958 100644 --- a/doxygen/user_doc.md +++ b/doxygen/user_doc.md @@ -10,7 +10,6 @@ Several modules provide operational, dataplane-user focused documentation. - @subpage dhcp6_pd_doc - @subpage flowprobe_plugin_doc - @subpage ioam_plugin_doc -- @subpage ipsec_gre_doc - @subpage kp_plugin_doc - @subpage lacp_plugin_doc - @subpage lb_plugin_doc diff --git a/src/scripts/vnet/ipsec_tun_protect b/src/scripts/vnet/ipsec_tun_protect new file mode 100644 index 00000000000..ed81f913139 --- /dev/null +++ b/src/scripts/vnet/ipsec_tun_protect @@ -0,0 +1,71 @@ + +create packet-generator interface pg0 +create packet-generator interface pg1 + +pipe create + +ip table add 1 +set int ip table pg1 1 +set int ip table pipe0.1 1 + +set int ip address pg0 192.168.0.1/24 +set int ip address pg1 192.168.1.1/24 + +set int ip address pipe0.0 10.0.0.1/24 +set int ip address pipe0.1 10.0.0.2/24 + +set int state pg0 up +set int state pg1 up +set int state pipe0 up + +ipsec sa add 20 spi 200 crypto-key 6541686776336961656264656f6f6579 crypto-alg aes-cbc-128 +ipsec sa add 30 spi 300 crypto-key 6541686776336961656264656f6f6579 crypto-alg aes-cbc-128 + +create ipip tunnel src 10.0.0.1 dst 10.0.0.2 +create ipip tunnel src 10.0.0.2 dst 10.0.0.1 + +ipsec tunnel protect ipip0 sa-in 20 sa-out 30 +ipsec tunnel protect ipip1 sa-in 30 sa-out 20 + +set int state ipip0 up +set int unnum ipip0 use pg0 + +set int state ipip1 up +set int ip table ipip1 1 +set int unnum ipip1 use pg1 + +ip route add 192.168.1.0/24 via ipip0 +set ip arp pg1 192.168.1.2 00:11:22:33:44:55 +ip route add table 1 192.168.0.0/24 via ipip1 +set ip arp pg0 192.168.0.2 00:11:22:33:44:66 + +trace add pg-input 100 + +packet-generator new { + name ipsec1 + limit 1 + rate 1e4 + node ip4-input + interface pg0 + size 100-100 + data { + UDP: 192.168.0.2 -> 192.168.1.2 + UDP: 4321 -> 1234 + length 72 + incrementing 100 + } +} +packet-generator new { + name ipsec2 + limit 1 + rate 1e4 + node ip4-input + interface pg1 + size 100-100 + data { + UDP: 192.168.1.2 -> 192.168.0.2 + UDP: 4321 -> 1234 + length 72 + incrementing 100 + } +} diff --git a/src/vat/api_format.c b/src/vat/api_format.c index fe1a87f573e..536c4b06ba2 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -5084,41 +5084,6 @@ static void vl_api_policer_classify_details_t_handler_json vat_json_object_add_uint (node, "table_index", ntohl (mp->table_index)); } -static void vl_api_ipsec_gre_tunnel_add_del_reply_t_handler - (vl_api_ipsec_gre_tunnel_add_del_reply_t * mp) -{ - vat_main_t *vam = &vat_main; - i32 retval = ntohl (mp->retval); - if (vam->async_mode) - { - vam->async_errors += (retval < 0); - } - else - { - vam->retval = retval; - vam->sw_if_index = ntohl (mp->sw_if_index); - vam->result_ready = 1; - } - vam->regenerate_interface_table = 1; -} - -static void vl_api_ipsec_gre_tunnel_add_del_reply_t_handler_json - (vl_api_ipsec_gre_tunnel_add_del_reply_t * mp) -{ - vat_main_t *vam = &vat_main; - vat_json_node_t node; - - vat_json_init_object (&node); - vat_json_object_add_int (&node, "retval", ntohl (mp->retval)); - vat_json_object_add_uint (&node, "sw_if_index", ntohl (mp->sw_if_index)); - - vat_json_print (vam->ofp, &node); - vat_json_free (&node); - - vam->retval = ntohl (mp->retval); - vam->result_ready = 1; -} - static void vl_api_flow_classify_details_t_handler (vl_api_flow_classify_details_t * mp) { @@ -5599,8 +5564,6 @@ _(IP_SOURCE_AND_PORT_RANGE_CHECK_ADD_DEL_REPLY, \ ip_source_and_port_range_check_add_del_reply) \ _(IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL_REPLY, \ ip_source_and_port_range_check_interface_add_del_reply) \ -_(IPSEC_GRE_TUNNEL_ADD_DEL_REPLY, ipsec_gre_tunnel_add_del_reply) \ -_(IPSEC_GRE_TUNNEL_DETAILS, ipsec_gre_tunnel_details) \ _(DELETE_SUBIF_REPLY, delete_subif_reply) \ _(L2_INTERFACE_PBB_TAG_REWRITE_REPLY, l2_interface_pbb_tag_rewrite_reply) \ _(SET_PUNT_REPLY, set_punt_reply) \ @@ -20086,52 +20049,6 @@ api_ip_source_and_port_range_check_interface_add_del (vat_main_t * vam) return ret; } -static int -api_ipsec_gre_tunnel_add_del (vat_main_t * vam) -{ - unformat_input_t *i = vam->input; - vl_api_ipsec_gre_tunnel_add_del_t *mp; - u32 local_sa_id = 0; - u32 remote_sa_id = 0; - vl_api_ip4_address_t src_address; - vl_api_ip4_address_t dst_address; - u8 is_add = 1; - int ret; - - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) - { - if (unformat (i, "local_sa %d", &local_sa_id)) - ; - else if (unformat (i, "remote_sa %d", &remote_sa_id)) - ; - else - if (unformat (i, "src %U", unformat_vl_api_ip4_address, &src_address)) - ; - else - if (unformat (i, "dst %U", unformat_vl_api_ip4_address, &dst_address)) - ; - else if (unformat (i, "del")) - is_add = 0; - else - { - clib_warning ("parse error '%U'", format_unformat_error, i); - return -99; - } - } - - M (IPSEC_GRE_TUNNEL_ADD_DEL, mp); - - mp->tunnel.local_sa_id = ntohl (local_sa_id); - mp->tunnel.remote_sa_id = ntohl (remote_sa_id); - clib_memcpy (mp->tunnel.src, &src_address, sizeof (src_address)); - clib_memcpy (mp->tunnel.dst, &dst_address, sizeof (dst_address)); - mp->is_add = is_add; - - S (mp); - W (ret); - return ret; -} - static int api_set_punt (vat_main_t * vam) { @@ -20173,99 +20090,6 @@ api_set_punt (vat_main_t * vam) return ret; } -static void vl_api_ipsec_gre_tunnel_details_t_handler - (vl_api_ipsec_gre_tunnel_details_t * mp) -{ - vat_main_t *vam = &vat_main; - - print (vam->ofp, "%11d%15U%15U%14d%14d", - ntohl (mp->tunnel.sw_if_index), - format_vl_api_ip4_address, mp->tunnel.src, - format_vl_api_ip4_address, mp->tunnel.dst, - ntohl (mp->tunnel.local_sa_id), ntohl (mp->tunnel.remote_sa_id)); -} - -static void -vat_json_object_add_vl_api_ip4 (vat_json_node_t * node, - const char *name, - const vl_api_ip4_address_t addr) -{ - struct in_addr ip4; - - clib_memcpy (&ip4, addr, sizeof (ip4)); - vat_json_object_add_ip4 (node, name, ip4); -} - -static void vl_api_ipsec_gre_tunnel_details_t_handler_json - (vl_api_ipsec_gre_tunnel_details_t * mp) -{ - vat_main_t *vam = &vat_main; - vat_json_node_t *node = NULL; - - if (VAT_JSON_ARRAY != vam->json_tree.type) - { - ASSERT (VAT_JSON_NONE == vam->json_tree.type); - vat_json_init_array (&vam->json_tree); - } - node = vat_json_array_add (&vam->json_tree); - - vat_json_init_object (node); - vat_json_object_add_uint (node, "sw_if_index", - ntohl (mp->tunnel.sw_if_index)); - vat_json_object_add_vl_api_ip4 (node, "src", mp->tunnel.src); - vat_json_object_add_vl_api_ip4 (node, "src", mp->tunnel.dst); - vat_json_object_add_uint (node, "local_sa_id", - ntohl (mp->tunnel.local_sa_id)); - vat_json_object_add_uint (node, "remote_sa_id", - ntohl (mp->tunnel.remote_sa_id)); -} - -static int -api_ipsec_gre_tunnel_dump (vat_main_t * vam) -{ - unformat_input_t *i = vam->input; - vl_api_ipsec_gre_tunnel_dump_t *mp; - vl_api_control_ping_t *mp_ping; - u32 sw_if_index; - u8 sw_if_index_set = 0; - int ret; - - /* Parse args required to build the message */ - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) - { - if (unformat (i, "sw_if_index %d", &sw_if_index)) - sw_if_index_set = 1; - else - break; - } - - if (sw_if_index_set == 0) - { - sw_if_index = ~0; - } - - if (!vam->json_output) - { - print (vam->ofp, "%11s%15s%15s%14s%14s", - "sw_if_index", "src_address", "dst_address", - "local_sa_id", "remote_sa_id"); - } - - /* Get list of gre-tunnel interfaces */ - M (IPSEC_GRE_TUNNEL_DUMP, mp); - - mp->sw_if_index = htonl (sw_if_index); - - S (mp); - - /* Use a control ping for synchronization */ - MPING (CONTROL_PING, mp_ping); - S (mp_ping); - - W (ret); - return ret; -} - static int api_delete_subif (vat_main_t * vam) { @@ -22409,9 +22233,6 @@ _(ip_source_and_port_range_check_add_del, \ _(ip_source_and_port_range_check_interface_add_del, \ " | sw_if_index [tcp-out-vrf ] [tcp-in-vrf ]" \ "[udp-in-vrf ] [udp-out-vrf ]") \ -_(ipsec_gre_tunnel_add_del, \ - "src dst local_sa remote_sa [del]") \ -_(ipsec_gre_tunnel_dump, "[sw_if_index ]") \ _(delete_subif," | sw_if_index ") \ _(l2_interface_pbb_tag_rewrite, \ " | sw_if_index \n" \ diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt index 5465d717f8c..462ced4ae23 100644 --- a/src/vnet/CMakeLists.txt +++ b/src/vnet/CMakeLists.txt @@ -566,6 +566,8 @@ list(APPEND VNET_SOURCES ipsec/ipsec_sa.c ipsec/ipsec_spd.c ipsec/ipsec_spd_policy.c + ipsec/ipsec_tun.c + ipsec/ipsec_tun_in.c ipsec/esp_format.c ipsec/esp_encrypt.c ipsec/esp_decrypt.c @@ -582,6 +584,7 @@ list(APPEND VNET_MULTIARCH_SOURCES ipsec/ipsec_if_in.c ipsec/ipsec_output.c ipsec/ipsec_input.c + ipsec/ipsec_tun_in.c ) list(APPEND VNET_API_FILES ipsec/ipsec.api) @@ -846,28 +849,6 @@ list(APPEND VNET_HEADERS list(APPEND VNET_API_FILES vxlan-gpe/vxlan_gpe.api) -############################################################################## -# Tunnel protocol: ipsec+gre -############################################################################## -list(APPEND VNET_SOURCES - ipsec-gre/ipsec_gre.c - ipsec-gre/node.c - ipsec-gre/interface.c - ipsec-gre/ipsec_gre_api.c -) - -list(APPEND VNET_MULTIARCH_SOURCES - ipsec-gre/node.c - ipsec-gre/ipsec_gre.c -) - -list(APPEND VNET_HEADERS - ipsec-gre/ipsec_gre.h - ipsec-gre/error.def -) - -list(APPEND VNET_API_FILES ipsec-gre/ipsec_gre.api) - ############################################################################## # LISP control plane: lisp-cp ############################################################################## diff --git a/src/vnet/buffer.h b/src/vnet/buffer.h index 6738f3cdca8..812fe7478c0 100644 --- a/src/vnet/buffer.h +++ b/src/vnet/buffer.h @@ -276,6 +276,7 @@ typedef struct struct { u32 sad_index; + u32 protect_index; } ipsec; /* MAP */ diff --git a/src/vnet/gre/gre.c b/src/vnet/gre/gre.c index 72c76fca5df..ab2567dc5b7 100644 --- a/src/vnet/gre/gre.c +++ b/src/vnet/gre/gre.c @@ -530,6 +530,29 @@ format_gre_device (u8 * s, va_list * args) return s; } +static int +gre_tunnel_desc (u32 sw_if_index, + ip46_address_t * src, ip46_address_t * dst, u8 * is_l2) +{ + gre_main_t *gm = &gre_main; + gre_tunnel_t *t; + u32 ti; + + ti = gm->tunnel_index_by_sw_if_index[sw_if_index]; + + if (~0 == ti) + /* not one of ours */ + return -1; + + t = pool_elt_at_index (gm->tunnels, ti); + + *src = t->tunnel_src; + *dst = t->tunnel_dst.fp_addr; + *is_l2 = t->type == GRE_TUNNEL_TYPE_TEB; + + return (0); +} + /* *INDENT-OFF* */ VNET_DEVICE_CLASS (gre_device_class) = { .name = "GRE tunnel device", @@ -537,6 +560,7 @@ VNET_DEVICE_CLASS (gre_device_class) = { .format_device = format_gre_device, .format_tx_trace = format_gre_tx_trace, .admin_up_down_function = gre_interface_admin_up_down, + .ip_tun_desc = gre_tunnel_desc, #ifdef SOON .clear counter = 0; #endif diff --git a/src/vnet/interface.c b/src/vnet/interface.c index 8af2b58ae8a..1702cdc00d1 100644 --- a/src/vnet/interface.c +++ b/src/vnet/interface.c @@ -515,6 +515,30 @@ vnet_sw_interface_set_flags (vnet_main_t * vnm, u32 sw_if_index, VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE); } +void +vnet_sw_interface_admin_up (vnet_main_t * vnm, u32 sw_if_index) +{ + u32 flags = vnet_sw_interface_get_flags (vnm, sw_if_index); + + if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)) + { + flags |= VNET_SW_INTERFACE_FLAG_ADMIN_UP; + vnet_sw_interface_set_flags (vnm, sw_if_index, flags); + } +} + +void +vnet_sw_interface_admin_down (vnet_main_t * vnm, u32 sw_if_index) +{ + u32 flags = vnet_sw_interface_get_flags (vnm, sw_if_index); + + if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) + { + flags &= ~(VNET_SW_INTERFACE_FLAG_ADMIN_UP); + vnet_sw_interface_set_flags (vnm, sw_if_index, flags); + } +} + static u32 vnet_create_sw_interface_no_callbacks (vnet_main_t * vnm, vnet_sw_interface_t * template) diff --git a/src/vnet/interface.h b/src/vnet/interface.h index 5419fff92a5..72a93269175 100644 --- a/src/vnet/interface.h +++ b/src/vnet/interface.h @@ -47,7 +47,7 @@ struct vnet_main_t; struct vnet_hw_interface_t; struct vnet_sw_interface_t; -struct ip46_address_t; +union ip46_address_t_; typedef enum { @@ -174,6 +174,14 @@ static __clib_unused void * __clib_unused_##f = f; #define VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION_PRIO(f,p) \ _VNET_INTERFACE_FUNCTION_DECL_PRIO(f,sw_interface_admin_up_down, p) +/** + * Tunnel description parameters + */ +typedef int (*vnet_dev_class_ip_tunnel_desc_t) (u32 sw_if_index, + union ip46_address_t_ * src, + union ip46_address_t_ * dst, + u8 * is_l2); + /* A class of hardware interface devices. */ typedef struct _vnet_device_class { @@ -235,6 +243,8 @@ typedef struct _vnet_device_class /* Format flow offload entry */ format_function_t *format_flow; + vnet_dev_class_ip_tunnel_desc_t ip_tun_desc; + /* Function to clear hardware counters for device. */ void (*clear_counters) (u32 dev_class_instance); diff --git a/src/vnet/interface_funcs.h b/src/vnet/interface_funcs.h index b862f48ef36..6d404b46098 100644 --- a/src/vnet/interface_funcs.h +++ b/src/vnet/interface_funcs.h @@ -356,6 +356,9 @@ clib_error_t *vnet_hw_interface_set_flags (vnet_main_t * vnm, u32 hw_if_index, clib_error_t *vnet_sw_interface_set_flags (vnet_main_t * vnm, u32 sw_if_index, vnet_sw_interface_flags_t flags); +void vnet_sw_interface_admin_up (vnet_main_t * vnm, u32 sw_if_index); +void vnet_sw_interface_admin_down (vnet_main_t * vnm, u32 sw_if_index); + /* Change interface class. */ clib_error_t *vnet_hw_interface_set_class (vnet_main_t * vnm, u32 hw_if_index, u32 new_hw_class_index); diff --git a/src/vnet/ip/ip6_packet.h b/src/vnet/ip/ip6_packet.h index ff47830dff3..c8bc4c817e8 100644 --- a/src/vnet/ip/ip6_packet.h +++ b/src/vnet/ip/ip6_packet.h @@ -75,7 +75,7 @@ typedef enum } ip46_type_t; /* *INDENT-OFF* */ -typedef CLIB_PACKED (union { +typedef CLIB_PACKED (union ip46_address_t_ { struct { u32 pad[3]; ip4_address_t ip4; @@ -95,6 +95,21 @@ typedef CLIB_PACKED (union { && ((a1)->as_u64[1] == (a2)->as_u64[1])) #define ip46_address_initializer {{{ 0 }}} +static_always_inline int +ip46_address_is_equal_v4 (const ip46_address_t * ip46, + const ip4_address_t * ip4) +{ + return (ip46->ip4.as_u32 == ip4->as_u32); +} + +static_always_inline int +ip46_address_is_equal_v6 (const ip46_address_t * ip46, + const ip6_address_t * ip6) +{ + return ((ip46->ip6.as_u64[0] == ip6->as_u64[0]) && + (ip46->ip6.as_u64[1] == ip6->as_u64[1])); +} + static_always_inline void ip46_address_copy (ip46_address_t * dst, const ip46_address_t * src) { diff --git a/src/vnet/ipip/ipip.c b/src/vnet/ipip/ipip.c index 5d407084205..66c945e346e 100644 --- a/src/vnet/ipip/ipip.c +++ b/src/vnet/ipip/ipip.c @@ -297,6 +297,23 @@ ipip_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags) return /* no error */ 0; } +static int +ipip_tunnel_desc (u32 sw_if_index, + ip46_address_t * src, ip46_address_t * dst, u8 * is_l2) +{ + ipip_tunnel_t *t; + + t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index); + if (!t) + return -1; + + *src = t->tunnel_src; + *dst = t->tunnel_dst; + *is_l2 = 0; + + return (0); +} + /* *INDENT-OFF* */ VNET_DEVICE_CLASS(ipip_device_class) = { .name = "IPIP tunnel device", @@ -304,6 +321,7 @@ VNET_DEVICE_CLASS(ipip_device_class) = { .format_device = format_ipip_device, .format_tx_trace = format_ipip_tx_trace, .admin_up_down_function = ipip_interface_admin_up_down, + .ip_tun_desc = ipip_tunnel_desc, #ifdef SOON .clear counter = 0; #endif diff --git a/src/vnet/ipip/ipip_api.c b/src/vnet/ipip/ipip_api.c index 35b846e5c8b..62a99659eee 100644 --- a/src/vnet/ipip/ipip_api.c +++ b/src/vnet/ipip/ipip_api.c @@ -150,7 +150,9 @@ vl_api_ipip_tunnel_dump_t_handler (vl_api_ipip_tunnel_dump_t * mp) { /* *INDENT-OFF* */ pool_foreach(t, gm->tunnels, - ({ send_ipip_tunnel_details(t, reg, mp->context); })); + ({ + send_ipip_tunnel_details(t, reg, mp->context); + })); /* *INDENT-ON* */ } else diff --git a/src/vnet/ipsec-gre/dir.dox b/src/vnet/ipsec-gre/dir.dox deleted file mode 100644 index e6ffd10b01b..00000000000 --- a/src/vnet/ipsec-gre/dir.dox +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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. - */ -/** - @dir vnet/vnet/ipsec-gre - @brief L2-GRE over IPSec tunnel interface implementation -*/ diff --git a/src/vnet/ipsec-gre/error.def b/src/vnet/ipsec-gre/error.def deleted file mode 100644 index d84e8ed1759..00000000000 --- a/src/vnet/ipsec-gre/error.def +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 L2-GRE over IPSec errors. - */ - - -ipsec_gre_error (NONE, "no error") -ipsec_gre_error (UNKNOWN_PROTOCOL, "unknown protocol") -ipsec_gre_error (UNSUPPORTED_VERSION, "unsupported version") -ipsec_gre_error (PKTS_DECAP, "GRE input packets decapsulated") -ipsec_gre_error (PKTS_ENCAP, "GRE output packets encapsulated") -ipsec_gre_error (NO_SUCH_TUNNEL, "GRE input packets dropped due to missing tunnel") diff --git a/src/vnet/ipsec-gre/interface.c b/src/vnet/ipsec-gre/interface.c deleted file mode 100644 index 6a8bb7d8f0b..00000000000 --- a/src/vnet/ipsec-gre/interface.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * gre_interface.c: gre interfaces - * - * 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 L2-GRE over IPSec tunnel interface. - * - * Creates ipsec-gre tunnel interface. - * Provides a command line interface so humans can interact with VPP. - */ - -#include -#include -#include -#include -#include -#include - -#include - -u8 * -format_ipsec_gre_tunnel (u8 * s, va_list * args) -{ - ipsec_gre_tunnel_t *t = va_arg (*args, ipsec_gre_tunnel_t *); - ipsec_gre_main_t *gm = &ipsec_gre_main; - - s = format (s, - "[%d] %U (src) %U (dst) local-sa %d remote-sa %d", - t - gm->tunnels, - format_ip4_address, &t->tunnel_src, - format_ip4_address, &t->tunnel_dst, - t->local_sa_id, t->remote_sa_id); - return s; -} - -static clib_error_t * -show_ipsec_gre_tunnel_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - ipsec_gre_main_t *igm = &ipsec_gre_main; - ipsec_gre_tunnel_t *t; - - if (pool_elts (igm->tunnels) == 0) - vlib_cli_output (vm, "No IPSec GRE tunnels configured..."); - - /* *INDENT-OFF* */ - pool_foreach (t, igm->tunnels, - ({ - vlib_cli_output (vm, "%U", format_ipsec_gre_tunnel, t); - })); - /* *INDENT-ON* */ - - return 0; -} - -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (show_ipsec_gre_tunnel_command, static) = { - .path = "show ipsec gre tunnel", - .function = show_ipsec_gre_tunnel_command_fn, -}; -/* *INDENT-ON* */ - -/* force inclusion from application's main.c */ -clib_error_t * -ipsec_gre_interface_init (vlib_main_t * vm) -{ - return 0; -} - -VLIB_INIT_FUNCTION (ipsec_gre_interface_init); - -/** - * @brief Add or delete ipsec-gre tunnel interface. - * - * @param *a vnet_ipsec_gre_tunnel_add_del_args_t - tunnel interface parameters - * @param *sw_if_indexp u32 - software interface index - * @return int - 0 if success otherwise VNET_API_ERROR_ - */ -int -vnet_ipsec_gre_tunnel_add_del (const ipsec_gre_tunnel_add_del_args_t * a, - u32 * sw_if_indexp) -{ - ipsec_gre_main_t *igm = &ipsec_gre_main; - vnet_main_t *vnm = igm->vnet_main; - ipsec_main_t *im = &ipsec_main; - ipsec_gre_tunnel_t *t; - vnet_hw_interface_t *hi; - u32 hw_if_index, sw_if_index; - u32 slot; - uword *p; - u64 key; - - key = (u64) a->src.as_u32 << 32 | (u64) a->dst.as_u32; - p = hash_get (igm->tunnel_by_key, key); - - if (a->is_add) - { - /* check if same src/dst pair exists */ - if (p) - return VNET_API_ERROR_INVALID_VALUE; - - pool_get_aligned (igm->tunnels, t, CLIB_CACHE_LINE_BYTES); - clib_memset (t, 0, sizeof (*t)); - - if (vec_len (igm->free_ipsec_gre_tunnel_hw_if_indices) > 0) - { - vnet_interface_main_t *im = &vnm->interface_main; - - hw_if_index = igm->free_ipsec_gre_tunnel_hw_if_indices - [vec_len (igm->free_ipsec_gre_tunnel_hw_if_indices) - 1]; - _vec_len (igm->free_ipsec_gre_tunnel_hw_if_indices) -= 1; - - hi = vnet_get_hw_interface (vnm, hw_if_index); - hi->dev_instance = t - igm->tunnels; - hi->hw_instance = hi->dev_instance; - - /* clear old stats of freed tunnel before reuse */ - sw_if_index = hi->sw_if_index; - vnet_interface_counter_lock (im); - vlib_zero_combined_counter - (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX], - sw_if_index); - vlib_zero_combined_counter - (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_RX], - sw_if_index); - vlib_zero_simple_counter - (&im->sw_if_counters[VNET_INTERFACE_COUNTER_DROP], sw_if_index); - vnet_interface_counter_unlock (im); - } - else - { - hw_if_index = vnet_register_interface - (vnm, ipsec_gre_device_class.index, t - igm->tunnels, - ipsec_gre_hw_interface_class.index, t - igm->tunnels); - hi = vnet_get_hw_interface (vnm, hw_if_index); - sw_if_index = hi->sw_if_index; - } - - t->hw_if_index = hw_if_index; - t->sw_if_index = sw_if_index; - t->local_sa_id = a->local_sa_id; - t->remote_sa_id = a->remote_sa_id; - t->local_sa = ipsec_get_sa_index_by_sa_id (t->local_sa_id); - t->remote_sa = ipsec_get_sa_index_by_sa_id (t->remote_sa_id); - - ip4_sw_interface_enable_disable (sw_if_index, 1); - - vec_validate_init_empty (igm->tunnel_index_by_sw_if_index, - sw_if_index, ~0); - igm->tunnel_index_by_sw_if_index[sw_if_index] = t - igm->tunnels; - - hi->min_packet_bytes = 64 + sizeof (gre_header_t) + - sizeof (ip4_header_t) + sizeof (esp_header_t) + sizeof (esp_footer_t); - - /* Standard default gre MTU. */ - /* TODO: Should take tunnel overhead into consideration */ - vnet_sw_interface_set_mtu (vnm, sw_if_index, 9000); - - clib_memcpy (&t->tunnel_src, &a->src, sizeof (t->tunnel_src)); - clib_memcpy (&t->tunnel_dst, &a->dst, sizeof (t->tunnel_dst)); - - hash_set (igm->tunnel_by_key, key, t - igm->tunnels); - - slot = vlib_node_add_next_with_slot - (vnm->vlib_main, hi->tx_node_index, im->esp4_encrypt_node_index, - IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT); - - ASSERT (slot == IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT); - } - else - { /* !is_add => delete */ - /* tunnel needs to exist */ - if (!p) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - t = pool_elt_at_index (igm->tunnels, p[0]); - - sw_if_index = t->sw_if_index; - ip4_sw_interface_enable_disable (sw_if_index, 0); - vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */ ); - /* make sure tunnel is removed from l2 bd or xconnect */ - set_int_l2_mode (igm->vlib_main, vnm, MODE_L3, sw_if_index, 0, - L2_BD_PORT_TYPE_NORMAL, 0, 0); - vec_add1 (igm->free_ipsec_gre_tunnel_hw_if_indices, t->hw_if_index); - igm->tunnel_index_by_sw_if_index[sw_if_index] = ~0; - - hash_unset (igm->tunnel_by_key, key); - pool_put (igm->tunnels, t); - } - - if (sw_if_indexp) - *sw_if_indexp = sw_if_index; - - return ipsec_add_del_ipsec_gre_tunnel (vnm, a); -} - -static clib_error_t * -create_ipsec_gre_tunnel_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - u32 num_m_args = 0; - ipsec_gre_tunnel_add_del_args_t _a, *a = &_a; - int rv; - u32 sw_if_index; - clib_error_t *error = NULL; - - clib_memset (a, 0, sizeof (*a)); - a->is_add = 1; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "del")) - a->is_add = 0; - else if (unformat (line_input, "src %U", unformat_ip4_address, &a->src)) - num_m_args++; - else if (unformat (line_input, "dst %U", unformat_ip4_address, &a->dst)) - num_m_args++; - else if (unformat (line_input, "local-sa %d", &a->local_sa_id)) - num_m_args++; - else if (unformat (line_input, "remote-sa %d", &a->remote_sa_id)) - num_m_args++; - else - { - error = clib_error_return (0, "unknown input `%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (num_m_args < 4) - { - error = clib_error_return (0, "mandatory argument(s) missing"); - goto done; - } - - if (memcmp (&a->src, &a->dst, sizeof (a->src)) == 0) - { - error = clib_error_return (0, "src and dst are identical"); - goto done; - } - - rv = vnet_ipsec_gre_tunnel_add_del (a, &sw_if_index); - - switch (rv) - { - case 0: - vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, - vnet_get_main (), sw_if_index); - break; - case VNET_API_ERROR_INVALID_VALUE: - error = clib_error_return (0, "GRE tunnel already exists..."); - goto done; - default: - error = clib_error_return (0, - "vnet_ipsec_gre_tunnel_add_del returned %d", - rv); - goto done; - } - -done: - unformat_free (line_input); - - return error; -} - -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (create_ipsec_gre_tunnel_command, static) = { - .path = "create ipsec gre tunnel", - .short_help = "create ipsec gre tunnel src dst " - "local-sa remote-sa [del]", - .function = create_ipsec_gre_tunnel_command_fn, -}; -/* *INDENT-ON* */ - -/* -* fd.io coding-style-patch-verification: ON -* -* Local Variables: -* eval: (c-set-style "gnu") -* End: -*/ diff --git a/src/vnet/ipsec-gre/ipsec_gre.api b/src/vnet/ipsec-gre/ipsec_gre.api deleted file mode 100644 index b4950097f61..00000000000 --- a/src/vnet/ipsec-gre/ipsec_gre.api +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2015-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. - */ - -option version = "1.1.0"; - -import "vnet/ip/ip_types.api"; - -/** \brief Add / del ipsec gre tunnel request - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param local_sa_id - local/output SA id - @param remote_sa_id - remote/input SA id - @param is_add - 1 if adding the tunnel, 0 if deleting - @param sw_if_index - software index of the ipsec gre tunnel - ignored on create. set in dump/details - @param src - tunnel source address - @param dst - tunnel destination address -*/ -typedef ipsec_gre_tunnel { - u32 client_index; - u32 context; - u32 local_sa_id; - u32 remote_sa_id; - u8 is_add; - u32 sw_if_index; - vl_api_ip4_address_t src; - vl_api_ip4_address_t dst; -}; - -define ipsec_gre_tunnel_add_del { - u32 client_index; - u32 context; - u8 is_add; - vl_api_ipsec_gre_tunnel_t tunnel; -}; - -/** \brief Reply for add / del ipsec gre tunnel request - @param context - returned sender context, to match reply w/ request - @param retval - return code - @param sw_if_index - software index of the new ipsec gre tunnel -*/ -define ipsec_gre_tunnel_add_del_reply { - u32 context; - i32 retval; - u32 sw_if_index; -}; - -/** \brief Dump ipsec gre tunnel table - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param tunnel_index - gre tunnel identifier or -1 in case of all tunnels -*/ -define ipsec_gre_tunnel_dump { - u32 client_index; - u32 context; - u32 sw_if_index; -}; - -/** \brief ipsec gre tunnel operational state response - @param context - returned sender context, to match reply w/ request - @param local_sa_id - local SA id - @param remote_sa_id - remote SA id - @param src_address - tunnel source address - @param dst_address - tunnel destination address -*/ -define ipsec_gre_tunnel_details { - u32 context; - vl_api_ipsec_gre_tunnel_t tunnel; -}; - -/* - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ - diff --git a/src/vnet/ipsec-gre/ipsec_gre.c b/src/vnet/ipsec-gre/ipsec_gre.c deleted file mode 100644 index cdb23dd9deb..00000000000 --- a/src/vnet/ipsec-gre/ipsec_gre.c +++ /dev/null @@ -1,394 +0,0 @@ -/* - * 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 L2-GRE over IPSec packet processing. - * - * Add GRE header to thr packet and send it to the esp-encrypt node. -*/ - -#include -#include - -extern ipsec_gre_main_t ipsec_gre_main; - -#ifndef CLIB_MARCH_VARIANT -ipsec_gre_main_t ipsec_gre_main; -#endif /* CLIB_MARCH_VARIANT */ - -/** - * @brief IPv4 and GRE header union. - * -*/ -typedef struct -{ - union - { - ip4_and_gre_header_t ip4_and_gre; - u64 as_u64[3]; - }; -} ip4_and_gre_union_t; - -/** - * @brief Packet trace. - * -*/ -typedef struct -{ - u32 tunnel_id; /**< Tunnel-id / index in tunnel vector */ - - u32 length; /**< pkt length */ - - ip4_address_t src; /**< tunnel src IPv4 address */ - ip4_address_t dst; /**< tunnel dst IPv4 address */ - - u32 sa_id; /**< tunnel IPSec SA id */ -} ipsec_gre_tx_trace_t; - -static u8 * -format_ipsec_gre_tx_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 *); - ipsec_gre_tx_trace_t *t = va_arg (*args, ipsec_gre_tx_trace_t *); - - s = format (s, "GRE: tunnel %d len %d src %U dst %U sa-id %d", - t->tunnel_id, clib_net_to_host_u16 (t->length), - format_ip4_address, &t->src.as_u8, - format_ip4_address, &t->dst.as_u8, t->sa_id); - return s; -} - -/** - * @brief IPSec-GRE tunnel interface tx function. - * - * Add GRE header to the packet. - * - * @param vm vlib_main_t corresponding to the current thread. - * @param node vlib_node_runtime_t data for this node. - * @param frame vlib_frame_t whose contents should be dispatched. - * - * @par Graph mechanics: buffer metadata, next index usage - * - * Uses: - * - node->runtime_data - * - Match tunnel by rd->dev_instance in IPSec-GRE tunnels - * pool. - * - * Sets: - * - vnet_buffer(b)->output_features.ipsec_sad_index - * - Set IPSec Security Association for packet encryption. - * - vnet_buffer(b)->sw_if_index[VLIB_TX] - * - Reset output sw_if_index. - * - * Next Index: - * - Dispatches the packet to the esp-encrypt node. -*/ -VNET_DEVICE_CLASS_TX_FN (ipsec_gre_device_class) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - ipsec_gre_main_t *igm = &ipsec_gre_main; - u32 next_index; - u32 *from, *to_next, n_left_from, n_left_to_next; - vnet_interface_output_runtime_t *rd = (void *) node->runtime_data; - ipsec_gre_tunnel_t *t = pool_elt_at_index (igm->tunnels, rd->dev_instance); - - u16 l2_gre_protocol_ethertype = clib_net_to_host_u16 (GRE_PROTOCOL_teb); - - /* Vector of buffer / pkt indices we're supposed to process */ - from = vlib_frame_vector_args (frame); - - /* Number of buffers / pkts */ - n_left_from = frame->n_vectors; - - /* Speculatively send the first buffer to the last disposition we used */ - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - /* set up to enqueue to our disposition with index = next_index */ - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - /* - * As long as we have enough pkts left to process two pkts - * and prefetch two pkts... - */ - while (n_left_from >= 4 && n_left_to_next >= 2) - { - vlib_buffer_t *b0, *b1; - ip4_header_t *ip0, *ip1; - ip4_and_gre_union_t *h0, *h1; - u32 bi0, next0, bi1, next1; - __attribute__ ((unused)) u8 error0, error1; - - /* Prefetch the next iteration */ - { - vlib_buffer_t *p2, *p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - - /* - * Prefetch packet data. We expect to overwrite - * the inbound L2 header with an ip header and a - * gre header. Might want to prefetch the last line - * of rewrite space as well; need profile data - */ - CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); - CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); - } - - /* Pick up the next two buffer indices */ - bi0 = from[0]; - bi1 = from[1]; - - /* Speculatively enqueue them where we sent the last buffer */ - to_next[0] = bi0; - to_next[1] = bi1; - from += 2; - to_next += 2; - n_left_to_next -= 2; - n_left_from -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - vlib_buffer_advance (b0, -sizeof (*h0)); - vlib_buffer_advance (b1, -sizeof (*h1)); - - h0 = vlib_buffer_get_current (b0); - h1 = vlib_buffer_get_current (b1); - h0->as_u64[0] = 0; - h0->as_u64[1] = 0; - h0->as_u64[2] = 0; - - h1->as_u64[0] = 0; - h1->as_u64[1] = 0; - h1->as_u64[2] = 0; - - ip0 = &h0->ip4_and_gre.ip4; - h0->ip4_and_gre.gre.protocol = l2_gre_protocol_ethertype; - ip0->ip_version_and_header_length = 0x45; - ip0->ttl = 254; - ip0->protocol = IP_PROTOCOL_GRE; - - ip1 = &h1->ip4_and_gre.ip4; - h1->ip4_and_gre.gre.protocol = l2_gre_protocol_ethertype; - ip1->ip_version_and_header_length = 0x45; - ip1->ttl = 254; - ip1->protocol = IP_PROTOCOL_GRE; - - ip0->length = - clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); - ip1->length = - clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)); - ip0->src_address.as_u32 = t->tunnel_src.as_u32; - ip1->src_address.as_u32 = t->tunnel_src.as_u32; - ip0->dst_address.as_u32 = t->tunnel_dst.as_u32; - ip1->dst_address.as_u32 = t->tunnel_dst.as_u32; - ip0->checksum = ip4_header_checksum (ip0); - ip1->checksum = ip4_header_checksum (ip1); - - vnet_buffer (b0)->sw_if_index[VLIB_RX] = - vnet_buffer (b0)->sw_if_index[VLIB_TX]; - vnet_buffer (b1)->sw_if_index[VLIB_RX] = - vnet_buffer (b1)->sw_if_index[VLIB_TX]; - - vnet_buffer (b0)->ipsec.sad_index = t->local_sa; - vnet_buffer (b1)->ipsec.sad_index = t->local_sa; - - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; - - next0 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT; - next1 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT; - error0 = IPSEC_GRE_ERROR_NONE; - error1 = IPSEC_GRE_ERROR_NONE; - - if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) - { - ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node, - b0, sizeof (*tr)); - tr->tunnel_id = t - igm->tunnels; - tr->length = ip0->length; - tr->src.as_u32 = ip0->src_address.as_u32; - tr->dst.as_u32 = ip0->dst_address.as_u32; - tr->sa_id = t->local_sa_id; - } - - if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED)) - { - ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node, - b1, sizeof (*tr)); - tr->tunnel_id = t - igm->tunnels; - tr->length = ip1->length; - tr->src.as_u32 = ip1->src_address.as_u32; - tr->dst.as_u32 = ip1->dst_address.as_u32; - tr->sa_id = t->local_sa_id; - } - - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } - - while (n_left_from > 0 && n_left_to_next > 0) - { - vlib_buffer_t *b0; - ip4_header_t *ip0; - ip4_and_gre_union_t *h0; - u32 bi0, next0; - __attribute__ ((unused)) u8 error0; - - bi0 = to_next[0] = from[0]; - from += 1; - n_left_from -= 1; - to_next += 1; - n_left_to_next -= 1; - - b0 = vlib_get_buffer (vm, bi0); - - vlib_buffer_advance (b0, -sizeof (*h0)); - - h0 = vlib_buffer_get_current (b0); - h0->as_u64[0] = 0; - h0->as_u64[1] = 0; - h0->as_u64[2] = 0; - - ip0 = &h0->ip4_and_gre.ip4; - h0->ip4_and_gre.gre.protocol = l2_gre_protocol_ethertype; - ip0->ip_version_and_header_length = 0x45; - ip0->ttl = 254; - ip0->protocol = IP_PROTOCOL_GRE; - ip0->length = - clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); - ip0->src_address.as_u32 = t->tunnel_src.as_u32; - ip0->dst_address.as_u32 = t->tunnel_dst.as_u32; - ip0->checksum = ip4_header_checksum (ip0); - - vnet_buffer (b0)->sw_if_index[VLIB_RX] = - vnet_buffer (b0)->sw_if_index[VLIB_TX]; - vnet_buffer (b0)->ipsec.sad_index = t->local_sa; - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - - next0 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT; - error0 = IPSEC_GRE_ERROR_NONE; - - if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) - { - ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node, - b0, sizeof (*tr)); - tr->tunnel_id = t - igm->tunnels; - tr->length = ip0->length; - tr->src.as_u32 = ip0->src_address.as_u32; - tr->dst.as_u32 = ip0->dst_address.as_u32; - tr->sa_id = t->local_sa_id; - } - - 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); - } - - vlib_node_increment_counter (vm, ipsec_gre_input_node.index, - IPSEC_GRE_ERROR_PKTS_ENCAP, frame->n_vectors); - - return frame->n_vectors; -} - -static clib_error_t * -ipsec_gre_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, - u32 flags) -{ - if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) - vnet_hw_interface_set_flags (vnm, hw_if_index, - VNET_HW_INTERFACE_FLAG_LINK_UP); - else - vnet_hw_interface_set_flags (vnm, hw_if_index, 0 /* down */ ); - - return /* no error */ 0; -} - -static u8 * -format_ipsec_gre_tunnel_name (u8 * s, va_list * args) -{ - u32 dev_instance = va_arg (*args, u32); - return format (s, "ipsec-gre%d", dev_instance); -} - -static u8 * -format_ipsec_gre_device (u8 * s, va_list * args) -{ - u32 dev_instance = va_arg (*args, u32); - CLIB_UNUSED (int verbose) = va_arg (*args, int); - - s = format (s, "IPSEC-GRE tunnel: id %d\n", dev_instance); - return s; -} - -/* *INDENT-OFF* */ -VNET_DEVICE_CLASS (ipsec_gre_device_class) = { - .name = "IPSec GRE tunnel device", - .format_device_name = format_ipsec_gre_tunnel_name, - .format_device = format_ipsec_gre_device, - .format_tx_trace = format_ipsec_gre_tx_trace, - .admin_up_down_function = ipsec_gre_interface_admin_up_down, -}; - - -#ifndef CLIB_MARCH_VARIANT -VNET_HW_INTERFACE_CLASS (ipsec_gre_hw_interface_class) = { - .name = "IPSEC-GRE", -}; -#endif /* CLIB_MARCH_VARIANT */ -/* *INDENT-ON* */ - -static clib_error_t * -ipsec_gre_init (vlib_main_t * vm) -{ - ipsec_gre_main_t *igm = &ipsec_gre_main; - clib_error_t *error; - - clib_memset (igm, 0, sizeof (igm[0])); - igm->vlib_main = vm; - igm->vnet_main = vnet_get_main (); - - if ((error = vlib_call_init_function (vm, ip_main_init))) - return error; - - if ((error = vlib_call_init_function (vm, ip4_lookup_init))) - return error; - - igm->tunnel_by_key = hash_create (0, sizeof (uword)); - - return vlib_call_init_function (vm, ipsec_gre_input_init); -} - -VLIB_INIT_FUNCTION (ipsec_gre_init); - -/* -* fd.io coding-style-patch-verification: ON -* -* Local Variables: -* eval: (c-set-style "gnu") -* End: -*/ diff --git a/src/vnet/ipsec-gre/ipsec_gre.h b/src/vnet/ipsec-gre/ipsec_gre.h deleted file mode 100644 index 730cd717795..00000000000 --- a/src/vnet/ipsec-gre/ipsec_gre.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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 L2-GRE over IPSec packet processing. -*/ - -#ifndef included_ipsec_gre_h -#define included_ipsec_gre_h - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern vnet_hw_interface_class_t ipsec_gre_hw_interface_class; - -/** - * @brief IPSec-GRE errors. - * -*/ -typedef enum -{ -#define ipsec_gre_error(n,s) IPSEC_GRE_ERROR_##n, -#include -#undef ipsec_gre_error - IPSEC_GRE_N_ERROR, -} ipsec_gre_error_t; - -/** - * @brief IPSec-GRE tunnel parameters. - * -*/ -typedef struct -{ - /* Required for pool_get_aligned */ - CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); - ip4_address_t tunnel_src; /**< tunnel IPv4 src address */ - ip4_address_t tunnel_dst; /**< tunnel IPv4 dst address */ - u32 local_sa; /**< local IPSec SA index */ - u32 remote_sa; /**< remote IPSec SA index */ - u32 local_sa_id; /**< local IPSec SA id */ - u32 remote_sa_id; /**< remote IPSec SA id */ - u32 hw_if_index;; /**< hardware interface index */ - u32 sw_if_index;; /**< software interface index */ -} ipsec_gre_tunnel_t; - -/** - * @brief IPSec-GRE state. - * -*/ -typedef struct -{ - ipsec_gre_tunnel_t *tunnels; /**< pool of tunnel instances */ - - uword *tunnel_by_key; /**< hash mapping src/dst addr pair to tunnel */ - - u32 *free_ipsec_gre_tunnel_hw_if_indices; /**< free vlib hw_if_indices */ - - u32 *tunnel_index_by_sw_if_index; /**< mapping from sw_if_index to tunnel - index */ - - vlib_main_t *vlib_main; /**< convenience */ - vnet_main_t *vnet_main; /**< convenience */ -} ipsec_gre_main_t; - -extern ipsec_gre_main_t ipsec_gre_main; - -extern vlib_node_registration_t ipsec_gre_input_node; -extern vnet_device_class_t ipsec_gre_device_class; - -/* manually added to the interface output node in ipsec_gre.c */ -#define IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT 0 - -extern int vnet_ipsec_gre_tunnel_add_del (const - ipsec_gre_tunnel_add_del_args_t * a, - u32 * sw_if_indexp); - -#endif /* included_ipsec_gre_h */ - -/* -* fd.io coding-style-patch-verification: ON -* -* Local Variables: -* eval: (c-set-style "gnu") -* End: -*/ diff --git a/src/vnet/ipsec-gre/ipsec_gre_api.c b/src/vnet/ipsec-gre/ipsec_gre_api.c deleted file mode 100644 index 9e5d615f977..00000000000 --- a/src/vnet/ipsec-gre/ipsec_gre_api.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - *------------------------------------------------------------------ - * ipsec_gre_api.c - ipsec_gre api - * - * 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. - *------------------------------------------------------------------ - */ - -#include -#include - -#include -#include -#include -#include - -#include - -#define vl_typedefs /* define message structures */ -#include -#undef vl_typedefs - -#define vl_endianfun /* define message structures */ -#include -#undef vl_endianfun - -/* instantiate all the print functions we know about */ -#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) -#define vl_printfun -#include -#undef vl_printfun - -#include - -#define foreach_vpe_api_msg \ -_(IPSEC_GRE_TUNNEL_ADD_DEL, ipsec_gre_tunnel_add_del) \ -_(IPSEC_GRE_TUNNEL_DUMP, ipsec_gre_tunnel_dump) - -static void -vl_api_ipsec_gre_tunnel_add_del_t_handler (vl_api_ipsec_gre_tunnel_add_del_t * - mp) -{ - vl_api_ipsec_gre_tunnel_add_del_reply_t *rmp; - int rv = 0; - ipsec_gre_tunnel_add_del_args_t _a, *a = &_a; - u32 sw_if_index = ~0; - - clib_memset (a, 0, sizeof (*a)); - - ip4_address_decode (mp->tunnel.src, &a->src); - ip4_address_decode (mp->tunnel.dst, &a->dst); - - /* Check src & dst are different */ - if (a->src.as_u32 == a->dst.as_u32) - { - rv = VNET_API_ERROR_SAME_SRC_DST; - goto out; - } - - a->is_add = mp->is_add; - a->local_sa_id = ntohl (mp->tunnel.local_sa_id); - a->remote_sa_id = ntohl (mp->tunnel.remote_sa_id); - - rv = vnet_ipsec_gre_tunnel_add_del (a, &sw_if_index); - -out: - /* *INDENT-OFF* */ - REPLY_MACRO2(VL_API_IPSEC_GRE_TUNNEL_ADD_DEL_REPLY, - ({ - rmp->sw_if_index = ntohl (sw_if_index); - })); - /* *INDENT-ON* */ -} - -static void send_ipsec_gre_tunnel_details - (ipsec_gre_tunnel_t * t, vl_api_registration_t * reg, u32 context) -{ - vl_api_ipsec_gre_tunnel_details_t *rmp; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - clib_memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_IPSEC_GRE_TUNNEL_DETAILS); - - ip4_address_encode (&t->tunnel_src, rmp->tunnel.src); - ip4_address_encode (&t->tunnel_dst, rmp->tunnel.dst); - rmp->tunnel.sw_if_index = htonl (t->sw_if_index); - rmp->tunnel.local_sa_id = htonl (t->local_sa_id); - rmp->tunnel.remote_sa_id = htonl (t->remote_sa_id); - rmp->context = context; - - vl_api_send_msg (reg, (u8 *) rmp); -} - -static void vl_api_ipsec_gre_tunnel_dump_t_handler - (vl_api_ipsec_gre_tunnel_dump_t * mp) -{ - vl_api_registration_t *reg; - ipsec_gre_main_t *igm = &ipsec_gre_main; - ipsec_gre_tunnel_t *t; - u32 sw_if_index; - - reg = vl_api_client_index_to_registration (mp->client_index); - if (!reg) - return; - - sw_if_index = ntohl (mp->sw_if_index); - - if (~0 == sw_if_index) - { - /* *INDENT-OFF* */ - pool_foreach (t, igm->tunnels, - ({ - send_ipsec_gre_tunnel_details(t, reg, mp->context); - })); - /* *INDENT-ON* */ - } - else - { - if ((sw_if_index >= vec_len (igm->tunnel_index_by_sw_if_index)) || - (~0 == igm->tunnel_index_by_sw_if_index[sw_if_index])) - { - return; - } - t = &igm->tunnels[igm->tunnel_index_by_sw_if_index[sw_if_index]]; - send_ipsec_gre_tunnel_details (t, reg, mp->context); - } -} - -/* - * ipsec_gre_api_hookup - * Add vpe's API message handlers to the table. - * vlib has already mapped shared memory and - * added the client registration handlers. - * See .../vlib-api/vlibmemory/memclnt_vlib.c:memclnt_process() - */ -#define vl_msg_name_crc_list -#include -#undef vl_msg_name_crc_list - -static void -setup_message_id_table (api_main_t * am) -{ -#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id); - foreach_vl_msg_name_crc_ipsec_gre; -#undef _ -} - -static clib_error_t * -ipsec_gre_api_hookup (vlib_main_t * vm) -{ - api_main_t *am = &api_main; - -#define _(N,n) \ - vl_msg_api_set_handlers(VL_API_##N, #n, \ - vl_api_##n##_t_handler, \ - vl_noop_handler, \ - vl_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ - sizeof(vl_api_##n##_t), 1); - foreach_vpe_api_msg; -#undef _ - - /* - * Set up the (msg_name, crc, message-id) table - */ - setup_message_id_table (am); - - return 0; -} - -VLIB_API_INIT_FUNCTION (ipsec_gre_api_hookup); - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/vnet/ipsec-gre/ipsec_gre_doc.md b/src/vnet/ipsec-gre/ipsec_gre_doc.md deleted file mode 100644 index e1bb9cdab1a..00000000000 --- a/src/vnet/ipsec-gre/ipsec_gre_doc.md +++ /dev/null @@ -1,74 +0,0 @@ -# VPP L2-GRE over IPsec implementation {#ipsec_gre_doc} - -This is a memo intended to contain documentation of the VPP L2-GRE over IPsec implementation. -Everything that is not directly obvious should come here. - - -## L2-GRE over IPsec -GRE encapsulate layer 2 traffic and IPSec encrypt what is encapsulated by GRE. The whole point of L2-GRE over IPSec is to tunnel layer 2 over GRE and IPSec by bridging the physical interface with IPSec-GRE tunnel interface. - -There are 2 dedicated nodes for encapsulation: -* ipsec-gre-tx - add GRE header -* esp-encrypt - encrypt GRE packet to ESP packet - -There are 3 dedicated nodes for decapsulation: -* ipsec-if-input - match IPSec SA by source IP address and SPI in ESP packet -* esp-decrypt - decrypt ESP packet -* ipsec-gre-input - remove GRE header - - -### Configuration - -L2-GRE over IPsec support the following CLI configuration command: - create ipsec gre tunnel src dst local-sa remote-sa [del] - -src: tunnel source IPv4 address -dst: tunnel destination IPv4 address -local-sa: tunnel local IPSec Security Association -remote-sa: tunnel remote IPSec Security Association -del: delete IPSec-GRE tunnel - -L2-GRE over IPsec support the following API configuration command: - ipsec_gre_add_del_tunnel src dst local_sa remote_sa [del] - -src: tunnel source IPv4 address -dst: tunnel destination IPv4 address -local_sa: tunnel local IPSec Security Association -remote_sa: tunnel remote IPSec Security Association -del: delete IPSec-GRE tunnel - - -### Configuration example - -Interface GigabitEthernet0/9/0 is in bridge with ipsec-gre0 tunnel interface, interface GigabitEthernet0/8/0 sending encapsulated and encrypted traffic. - -Configure IPv4 address on sending interface: -set int ip address GigabitEthernet0/8/0 192.168.1.1/24 - -Configure IPSec Security Associations: -ipsec sa add 10 spi 1001 esp crypto-key 4a506a794f574265564551694d653768 crypto-alg aes-cbc-128 integ-key 4339314b55523947594d6d3547666b45764e6a58 integ-alg sha1-96 -ipsec sa add 20 spi 1000 esp crypto-key 49517065716d6235726c734a4372466c crypto-alg aes-cbc-128 integ-key 307439636a5542735133595835546f68534e4f64 integ-alg sha1-96 - -Create IPSec-GRE tunnel: -create ipsec gre tunnel src 192.168.1.1 dst 192.168.1.2 local-sa 10 remote-sa 20 - -Set interfaces state: -set int state GigabitEthernet0/8/0 up -set int state GigabitEthernet0/9/0 up -set int state ipsec-gre0 up - -Bridge physical interface with IPSec-GRE tunnel interface: -set interface l2 bridge GigabitEthernet0/9/0 1 -set interface l2 bridge ipsec-gre0 1 - - -### Operational data - -L2-GRE over IPsec support the following CLI show command: - show ipsec gre tunnel - -L2-GRE over IPsec support the following API dump command: - ipsec_gre_tunnel_dump [sw_if_index ] - -sw_if_index: software interface index of the IPSec-GRE tunnel interface - diff --git a/src/vnet/ipsec-gre/node.c b/src/vnet/ipsec-gre/node.c deleted file mode 100644 index 6a3aaa12e8c..00000000000 --- a/src/vnet/ipsec-gre/node.c +++ /dev/null @@ -1,426 +0,0 @@ -/* - * 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 L2-GRE over IPSec packet processing. - * - * Removes GRE header from the packet and sends it to the l2-input node. -*/ - -#include -#include -#include -#include - -#define foreach_ipsec_gre_input_next \ -_(PUNT, "error-punt") \ -_(DROP, "error-drop") \ -_(L2_INPUT, "l2-input") - -typedef enum { -#define _(s,n) IPSEC_GRE_INPUT_NEXT_##s, - foreach_ipsec_gre_input_next -#undef _ - IPSEC_GRE_INPUT_N_NEXT, -} ipsec_gre_input_next_t; - -typedef struct { - u32 tunnel_id; - u32 length; - ip4_address_t src; - ip4_address_t dst; -} ipsec_gre_rx_trace_t; - -static u8 * format_ipsec_gre_rx_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 *); - ipsec_gre_rx_trace_t * t = va_arg (*args, ipsec_gre_rx_trace_t *); - - s = format (s, "GRE: tunnel %d len %d src %U dst %U", - t->tunnel_id, clib_net_to_host_u16(t->length), - format_ip4_address, &t->src.as_u8, - format_ip4_address, &t->dst.as_u8); - return s; -} - -/** - * @brief L2-GRE over IPSec input node. - * @node ipsec-gre-input - * - * This node remove GRE header. - * - * @param vm vlib_main_t corresponding to the current thread. - * @param node vlib_node_runtime_t data for this node. - * @param from_frame vlib_frame_t whose contents should be dispatched. - * - * @par Graph mechanics: buffer metadata, next index usage - * - * Uses: - * - ip->src_address and ip->dst_address - * - Match tunnel by source and destination addresses in GRE IP header. - * - * Sets: - * - vnet_buffer(b)->gre.src - * - Save tunnel source IPv4 address. - * - vnet_buffer(b)->gre.dst - * - Save tunnel destination IPv4 address. - * - vnet_buffer(b)->sw_if_index[VLIB_RX] - * - Set input sw_if_index to IPSec-GRE tunnel for learning. - * - * Next Index: - * - Dispatches the packet to the l2-input node. -*/ -VLIB_NODE_FN (ipsec_gre_input_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * from_frame) -{ - ipsec_gre_main_t * igm = &ipsec_gre_main; - u32 n_left_from, next_index, * from, * to_next; - u64 cached_tunnel_key = (u64) ~0; - u32 cached_tunnel_sw_if_index = 0, tunnel_sw_if_index; - u32 tun_src0, tun_dst0; - u32 tun_src1, tun_dst1; - - from = vlib_frame_vector_args (from_frame); - n_left_from = from_frame->n_vectors; - - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, - to_next, n_left_to_next); - - while (n_left_from >= 4 && n_left_to_next >= 2) - { - u32 bi0, bi1; - vlib_buffer_t * b0, * b1; - gre_header_t * h0, * h1; - u16 version0, version1, protocol0, protocol1; - int verr0, verr1; - u32 next0, next1; - ip4_header_t *ip0, *ip1; - - /* Prefetch next iteration. */ - { - vlib_buffer_t * p2, * p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - - CLIB_PREFETCH (p2->data, sizeof (h0[0]), LOAD); - CLIB_PREFETCH (p3->data, sizeof (h1[0]), LOAD); - } - - bi0 = from[0]; - bi1 = from[1]; - to_next[0] = bi0; - to_next[1] = bi1; - from += 2; - to_next += 2; - n_left_to_next -= 2; - n_left_from -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - /* ip4_local hands us the ip header, not the gre header */ - ip0 = vlib_buffer_get_current (b0); - ip1 = vlib_buffer_get_current (b1); - - /* Save src + dst ip4 address */ - tun_src0 = ip0->src_address.as_u32; - tun_dst0 = ip0->dst_address.as_u32; - tun_src1 = ip1->src_address.as_u32; - tun_dst1 = ip1->dst_address.as_u32; - - vlib_buffer_advance (b0, sizeof (*ip0)); - vlib_buffer_advance (b1, sizeof (*ip1)); - - h0 = vlib_buffer_get_current (b0); - h1 = vlib_buffer_get_current (b1); - - protocol0 = clib_net_to_host_u16 (h0->protocol); - protocol1 = clib_net_to_host_u16 (h1->protocol); - if (PREDICT_TRUE(protocol0 == GRE_PROTOCOL_teb)) - { - next0 = IPSEC_GRE_INPUT_NEXT_L2_INPUT; - b0->error = node->errors[IPSEC_GRE_ERROR_NONE]; - } - else - { - b0->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL]; - next0 = IPSEC_GRE_INPUT_NEXT_DROP; - } - if (PREDICT_TRUE(protocol1 == GRE_PROTOCOL_teb)) - { - next1 = IPSEC_GRE_INPUT_NEXT_L2_INPUT; - b1->error = node->errors[IPSEC_GRE_ERROR_NONE]; - } - else - { - b1->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL]; - next1 = IPSEC_GRE_INPUT_NEXT_DROP; - } - - version0 = clib_net_to_host_u16 (h0->flags_and_version); - verr0 = version0 & GRE_VERSION_MASK; - version1 = clib_net_to_host_u16 (h1->flags_and_version); - verr1 = version1 & GRE_VERSION_MASK; - - b0->error = verr0 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION] - : b0->error; - next0 = verr0 ? IPSEC_GRE_INPUT_NEXT_DROP : next0; - b1->error = verr1 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION] - : b1->error; - next1 = verr1 ? IPSEC_GRE_INPUT_NEXT_DROP : next1; - - /* For L2 payload set input sw_if_index to GRE tunnel for learning */ - if (PREDICT_TRUE(next0 == IPSEC_GRE_INPUT_NEXT_L2_INPUT)) - { - u64 key = ((u64)(tun_dst0) << 32) | (u64)(tun_src0); - - if (cached_tunnel_key != key) - { - vnet_hw_interface_t * hi; - ipsec_gre_tunnel_t * t; - uword * p; - - p = hash_get (igm->tunnel_by_key, key); - if (!p) - { - next0 = IPSEC_GRE_INPUT_NEXT_DROP; - b0->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL]; - goto drop0; - } - t = pool_elt_at_index (igm->tunnels, p[0]); - hi = vnet_get_hw_interface (igm->vnet_main, - t->hw_if_index); - tunnel_sw_if_index = hi->sw_if_index; - cached_tunnel_sw_if_index = tunnel_sw_if_index; - } - else - { - tunnel_sw_if_index = cached_tunnel_sw_if_index; - } - vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index; - } - -drop0: - /* For L2 payload set input sw_if_index to GRE tunnel for learning */ - if (PREDICT_TRUE(next1 == IPSEC_GRE_INPUT_NEXT_L2_INPUT)) - { - u64 key = ((u64)(tun_dst1) << 32) | (u64)(tun_src1); - - if (cached_tunnel_key != key) - { - vnet_hw_interface_t * hi; - ipsec_gre_tunnel_t * t; - uword * p; - - p = hash_get (igm->tunnel_by_key, key); - if (!p) - { - next1 = IPSEC_GRE_INPUT_NEXT_DROP; - b1->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL]; - goto drop1; - } - t = pool_elt_at_index (igm->tunnels, p[0]); - hi = vnet_get_hw_interface (igm->vnet_main, - t->hw_if_index); - tunnel_sw_if_index = hi->sw_if_index; - cached_tunnel_sw_if_index = tunnel_sw_if_index; - } - else - { - tunnel_sw_if_index = cached_tunnel_sw_if_index; - } - vnet_buffer(b1)->sw_if_index[VLIB_RX] = tunnel_sw_if_index; - } - -drop1: - if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) - { - ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node, - b0, sizeof (*tr)); - tr->tunnel_id = ~0; - tr->length = ip0->length; - tr->src.as_u32 = ip0->src_address.as_u32; - tr->dst.as_u32 = ip0->dst_address.as_u32; - } - - if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) - { - ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node, - b1, sizeof (*tr)); - tr->tunnel_id = ~0; - tr->length = ip1->length; - tr->src.as_u32 = ip1->src_address.as_u32; - tr->dst.as_u32 = ip1->dst_address.as_u32; - } - - vlib_buffer_advance (b0, sizeof (*h0)); - vlib_buffer_advance (b1, sizeof (*h1)); - - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t * b0; - gre_header_t * h0; - ip4_header_t * ip0; - u16 version0, protocol0; - int verr0; - u32 next0; - u32 tun_src0, tun_dst0; - - 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); - - tun_src0 = ip0->src_address.as_u32; - tun_dst0 = ip0->dst_address.as_u32; - - vlib_buffer_advance (b0, sizeof (*ip0)); - - h0 = vlib_buffer_get_current (b0); - - protocol0 = clib_net_to_host_u16 (h0->protocol); - if (PREDICT_TRUE(protocol0 == GRE_PROTOCOL_teb)) - { - next0 = IPSEC_GRE_INPUT_NEXT_L2_INPUT; - b0->error = node->errors[IPSEC_GRE_ERROR_NONE]; - } - else - { - b0->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL]; - next0 = IPSEC_GRE_INPUT_NEXT_DROP; - } - - version0 = clib_net_to_host_u16 (h0->flags_and_version); - verr0 = version0 & GRE_VERSION_MASK; - b0->error = verr0 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION] - : b0->error; - next0 = verr0 ? IPSEC_GRE_INPUT_NEXT_DROP : next0; - - /* For L2 payload set input sw_if_index to GRE tunnel for learning */ - if (PREDICT_TRUE(next0 == IPSEC_GRE_INPUT_NEXT_L2_INPUT)) - { - u64 key = ((u64)(tun_dst0) << 32) | (u64)(tun_src0); - - if (cached_tunnel_key != key) - { - vnet_hw_interface_t * hi; - ipsec_gre_tunnel_t * t; - uword * p; - - p = hash_get (igm->tunnel_by_key, key); - if (!p) - { - next0 = IPSEC_GRE_INPUT_NEXT_DROP; - b0->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL]; - goto drop; - } - t = pool_elt_at_index (igm->tunnels, p[0]); - hi = vnet_get_hw_interface (igm->vnet_main, - t->hw_if_index); - tunnel_sw_if_index = hi->sw_if_index; - cached_tunnel_sw_if_index = tunnel_sw_if_index; - } - else - { - tunnel_sw_if_index = cached_tunnel_sw_if_index; - } - vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index; - } - -drop: - if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) - { - ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node, - b0, sizeof (*tr)); - tr->tunnel_id = ~0; - tr->length = ip0->length; - tr->src.as_u32 = ip0->src_address.as_u32; - tr->dst.as_u32 = ip0->dst_address.as_u32; - } - - vlib_buffer_advance (b0, sizeof (*h0)); - - 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); - } - vlib_node_increment_counter (vm, ipsec_gre_input_node.index, - IPSEC_GRE_ERROR_PKTS_DECAP, from_frame->n_vectors); - return from_frame->n_vectors; -} - -static char * ipsec_gre_error_strings[] = { -#define ipsec_gre_error(n,s) s, -#include "error.def" -#undef ipsec_gre_error -}; - -VLIB_REGISTER_NODE (ipsec_gre_input_node) = { - .name = "ipsec-gre-input", - /* Takes a vector of packets. */ - .vector_size = sizeof (u32), - - .n_errors = IPSEC_GRE_N_ERROR, - .error_strings = ipsec_gre_error_strings, - - .n_next_nodes = IPSEC_GRE_INPUT_N_NEXT, - .next_nodes = { -#define _(s,n) [IPSEC_GRE_INPUT_NEXT_##s] = n, - foreach_ipsec_gre_input_next -#undef _ - }, - - .format_trace = format_ipsec_gre_rx_trace, -}; - -static clib_error_t * ipsec_gre_input_init (vlib_main_t * vm) -{ - { - clib_error_t * error; - error = vlib_call_init_function (vm, ipsec_gre_init); - if (error) - clib_error_report (error); - } - - return 0; -} - -VLIB_INIT_FUNCTION (ipsec_gre_input_init); diff --git a/src/vnet/ipsec/ah_decrypt.c b/src/vnet/ipsec/ah_decrypt.c index d27d42384d5..741fa91b95c 100644 --- a/src/vnet/ipsec/ah_decrypt.c +++ b/src/vnet/ipsec/ah_decrypt.c @@ -27,8 +27,7 @@ #define foreach_ah_decrypt_next \ _ (DROP, "error-drop") \ _ (IP4_INPUT, "ip4-input") \ - _ (IP6_INPUT, "ip6-input") \ - _ (IPSEC_GRE_INPUT, "ipsec-gre-input") + _ (IP6_INPUT, "ip6-input") #define _(v, s) AH_DECRYPT_NEXT_##v, typedef enum @@ -371,10 +370,6 @@ ah_decrypt_inline (vlib_main_t * vm, } } - /* for IPSec-GRE tunnel next node is ipsec-gre-input */ - if (PREDICT_FALSE (ipsec_sa_is_set_IS_GRE (sa0))) - next[0] = AH_DECRYPT_NEXT_IPSEC_GRE_INPUT; - vnet_buffer (b[0])->sw_if_index[VLIB_TX] = (u32) ~ 0; trace: if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED)) diff --git a/src/vnet/ipsec/esp_decrypt.c b/src/vnet/ipsec/esp_decrypt.c index 710c8f17762..48f08f42e33 100644 --- a/src/vnet/ipsec/esp_decrypt.c +++ b/src/vnet/ipsec/esp_decrypt.c @@ -22,12 +22,12 @@ #include #include #include +#include #define foreach_esp_decrypt_next \ _(DROP, "error-drop") \ _(IP4_INPUT, "ip4-input-no-checksum") \ -_(IP6_INPUT, "ip6-input") \ -_(IPSEC_GRE_INPUT, "ipsec-gre-input") +_(IP6_INPUT, "ip6-input") #define _(v, s) ESP_DECRYPT_NEXT_##v, typedef enum @@ -93,7 +93,7 @@ typedef struct { u8 icv_sz; u8 iv_sz; - ipsec_sa_flags_t flags:8; + ipsec_sa_flags_t flags; u32 sa_index; }; u64 sa_data; @@ -111,7 +111,7 @@ STATIC_ASSERT_SIZEOF (esp_decrypt_packet_data_t, 2 * sizeof (u64)); always_inline uword esp_decrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame, - int is_ip6) + int is_ip6, int is_tun) { ipsec_main_t *im = &ipsec_main; u32 thread_index = vm->thread_index; @@ -378,7 +378,7 @@ esp_decrypt_inline (vlib_main_t * vm, u16 adv = pd->iv_sz + esp_sz; u16 tail = sizeof (esp_footer_t) + f->pad_length + pd->icv_sz; - if ((pd->flags & tun_flags) == 0) /* transport mode */ + if ((pd->flags & tun_flags) == 0 && !is_tun) /* transport mode */ { u8 udp_sz = (is_ip6 == 0 && pd->flags & IPSEC_SA_FLAG_UDP_ENCAP) ? sizeof (udp_header_t) : 0; @@ -437,12 +437,50 @@ esp_decrypt_inline (vlib_main_t * vm, { next[0] = ESP_DECRYPT_NEXT_DROP; b[0]->error = node->errors[ESP_DECRYPT_ERROR_DECRYPTION_FAILED]; + goto trace; + } + if (is_tun) + { + if (ipsec_sa_is_set_IS_PROTECT (sa0)) + { + /* + * Check that the reveal IP header matches that + * of the tunnel we are protecting + */ + const ipsec_tun_protect_t *itp; + + itp = + ipsec_tun_protect_get (vnet_buffer (b[0])-> + ipsec.protect_index); + if (PREDICT_TRUE (f->next_header == IP_PROTOCOL_IP_IN_IP)) + { + const ip4_header_t *ip4; + + ip4 = vlib_buffer_get_current (b[0]); + + if (!ip46_address_is_equal_v4 (&itp->itp_tun.src, + &ip4->dst_address) || + !ip46_address_is_equal_v4 (&itp->itp_tun.dst, + &ip4->src_address)) + next[0] = ESP_DECRYPT_NEXT_DROP; + + } + else if (f->next_header == IP_PROTOCOL_IPV6) + { + const ip6_header_t *ip6; + + ip6 = vlib_buffer_get_current (b[0]); + + if (!ip46_address_is_equal_v6 (&itp->itp_tun.src, + &ip6->dst_address) || + !ip46_address_is_equal_v6 (&itp->itp_tun.dst, + &ip6->src_address)) + next[0] = ESP_DECRYPT_NEXT_DROP; + } + } } } - if (PREDICT_FALSE (ipsec_sa_is_set_IS_GRE (sa0))) - next[0] = ESP_DECRYPT_NEXT_IPSEC_GRE_INPUT; - trace: if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED)) { @@ -477,7 +515,28 @@ VLIB_NODE_FN (esp4_decrypt_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { - return esp_decrypt_inline (vm, node, from_frame, 0 /* is_ip6 */ ); + return esp_decrypt_inline (vm, node, from_frame, 0, 0); +} + +VLIB_NODE_FN (esp4_decrypt_tun_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return esp_decrypt_inline (vm, node, from_frame, 0, 1); +} + +VLIB_NODE_FN (esp6_decrypt_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return esp_decrypt_inline (vm, node, from_frame, 1, 0); +} + +VLIB_NODE_FN (esp6_decrypt_tun_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return esp_decrypt_inline (vm, node, from_frame, 1, 1); } /* *INDENT-OFF* */ @@ -497,16 +556,7 @@ VLIB_REGISTER_NODE (esp4_decrypt_node) = { #undef _ }, }; -/* *INDENT-ON* */ -VLIB_NODE_FN (esp6_decrypt_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * from_frame) -{ - return esp_decrypt_inline (vm, node, from_frame, 1 /* is_ip6 */ ); -} - -/* *INDENT-OFF* */ VLIB_REGISTER_NODE (esp6_decrypt_node) = { .name = "esp6-decrypt", .vector_size = sizeof (u32), @@ -523,6 +573,40 @@ VLIB_REGISTER_NODE (esp6_decrypt_node) = { #undef _ }, }; + +VLIB_REGISTER_NODE (esp4_decrypt_tun_node) = { + .name = "esp4-decrypt-tun", + .vector_size = sizeof (u32), + .format_trace = format_esp_decrypt_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(esp_decrypt_error_strings), + .error_strings = esp_decrypt_error_strings, + + .n_next_nodes = ESP_DECRYPT_N_NEXT, + .next_nodes = { +#define _(s,n) [ESP_DECRYPT_NEXT_##s] = n, + foreach_esp_decrypt_next +#undef _ + }, +}; + +VLIB_REGISTER_NODE (esp6_decrypt_tun_node) = { + .name = "esp6-decrypt-tun", + .vector_size = sizeof (u32), + .format_trace = format_esp_decrypt_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(esp_decrypt_error_strings), + .error_strings = esp_decrypt_error_strings, + + .n_next_nodes = ESP_DECRYPT_N_NEXT, + .next_nodes = { +#define _(s,n) [ESP_DECRYPT_NEXT_##s] = n, + foreach_esp_decrypt_next +#undef _ + }, +}; /* *INDENT-ON* */ /* diff --git a/src/vnet/ipsec/esp_encrypt.c b/src/vnet/ipsec/esp_encrypt.c index d7cda052c92..cf485482c0e 100644 --- a/src/vnet/ipsec/esp_encrypt.c +++ b/src/vnet/ipsec/esp_encrypt.c @@ -408,12 +408,18 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node, ip_hdr = payload - hdr_len; /* L2 header */ - l2_len = vnet_buffer (b[0])->ip.save_rewrite_length; - hdr_len += l2_len; - l2_hdr = payload - hdr_len; + if (!is_tun) + { + l2_len = vnet_buffer (b[0])->ip.save_rewrite_length; + hdr_len += l2_len; + l2_hdr = payload - hdr_len; + + /* copy l2 and ip header */ + clib_memcpy_le32 (l2_hdr, old_ip_hdr - l2_len, l2_len); + } + else + l2_len = 0; - /* copy l2 and ip header */ - clib_memcpy_le32 (l2_hdr, old_ip_hdr - l2_len, l2_len); clib_memcpy_le64 (ip_hdr, old_ip_hdr, ip_len); if (is_ip6) @@ -440,7 +446,8 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node, esp_update_ip4_hdr (ip4, len, /* is_transport */ 1, 0); } - next[0] = ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT; + if (!is_tun) + next[0] = ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT; } esp->spi = spi; @@ -618,6 +625,13 @@ VNET_FEATURE_INIT (esp4_encrypt_tun_feat_node, static) = .node_name = "esp4-encrypt-tun", .runs_before = VNET_FEATURES ("adj-midchain-tx"), }; + +VNET_FEATURE_INIT (esp4_ethernet_encrypt_tun_feat_node, static) = +{ + .arc_name = "ethernet-output", + .node_name = "esp4-encrypt-tun", + .runs_before = VNET_FEATURES ("adj-midchain-tx", "adj-midchain-tx-no-count"), +}; /* *INDENT-ON* */ VLIB_NODE_FN (esp6_encrypt_tun_node) (vlib_main_t * vm, diff --git a/src/vnet/ipsec/ipsec.api b/src/vnet/ipsec/ipsec.api index bb9e8056251..12bdad0f9c3 100644 --- a/src/vnet/ipsec/ipsec.api +++ b/src/vnet/ipsec/ipsec.api @@ -17,6 +17,7 @@ option version = "3.0.0"; import "vnet/ip/ip_types.api"; +import "vnet/interface_types.api"; /** \brief IPsec: Add/delete Security Policy Database @param client_index - opaque cookie to identify the sender @@ -305,6 +306,81 @@ define ipsec_sad_entry_add_del_reply u32 stat_index; }; +/** \brief Add or Update Protection for a tunnel with IPSEC + + Tunnel protection directly associates an SA with all packets + ingress and egress on the tunnel. This could also be achieved by + assigning an SPD to the tunnel, but that would incur an unnessccary + SPD entry lookup. + + For tunnels the ESP acts on the post-encapsulated packet. So if this + packet: + +---------+------+ + | Payload | O-IP | + +---------+------+ + where O-IP is the overlay IP addrees that was routed into the tunnel, + the resulting encapsulated packet will be: + +---------+------+------+ + | Payload | O-IP | T-IP | + +---------+------+------+ + where T-IP is the tunnel's src.dst IP addresses. + If the SAs used for protection are in transport mode then the ESP is + inserted before T-IP, i.e.: + +---------+------+-----+------+ + | Payload | O-IP | ESP | T-IP | + +---------+------+-----+------+ + If the SAs used for protection are in tunnel mode then another + encapsulation occurs, i.e.: + +---------+------+------+-----+------+ + | Payload | O-IP | T-IP | ESP | C-IP | + +---------+------+------+-----+------+ + where C-IP are the crypto endpoint IP addresses defined as the tunnel + endpoints in the SA. + The mode for the inbound and outbound SA must be the same. + + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param sw_id_index - Tunnel interface to protect + @param sa_in - The ID [set] of inbound SAs + @param sa_out - The ID of outbound SA +*/ +typedef ipsec_tunnel_protect +{ + vl_api_interface_index_t sw_if_index; + u32 sa_out; + u8 n_sa_in; + u32 sa_in[n_sa_in]; +}; + +autoreply define ipsec_tunnel_protect_update +{ + u32 client_index; + u32 context; + + vl_api_ipsec_tunnel_protect_t tunnel; +}; + +autoreply define ipsec_tunnel_protect_del +{ + u32 client_index; + u32 context; + + vl_api_interface_index_t sw_if_index; +}; + +define ipsec_tunnel_protect_dump +{ + u32 client_index; + u32 context; + vl_api_interface_index_t sw_if_index; +}; + +define ipsec_tunnel_protect_details +{ + u32 context; + vl_api_ipsec_tunnel_protect_t tun; +}; + /** \brief IPsec: Get SPD interfaces @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request diff --git a/src/vnet/ipsec/ipsec.h b/src/vnet/ipsec/ipsec.h index 45576b3c779..c77d0fe7dd8 100644 --- a/src/vnet/ipsec/ipsec.h +++ b/src/vnet/ipsec/ipsec.h @@ -115,6 +115,8 @@ typedef struct uword *ipsec6_if_pool_index_by_key; uword *ipsec_if_real_dev_by_show_dev; uword *ipsec_if_by_sw_if_index; + uword *tun4_protect_by_key; + uword *tun6_protect_by_key; /* node indices */ u32 error_drop_node_index; diff --git a/src/vnet/ipsec/ipsec_api.c b/src/vnet/ipsec/ipsec_api.c index 2c7c0d9626d..99e25f1b17a 100644 --- a/src/vnet/ipsec/ipsec_api.c +++ b/src/vnet/ipsec/ipsec_api.c @@ -30,6 +30,7 @@ #if WITH_LIBSSL > 0 #include +#include #endif /* IPSEC */ #define vl_typedefs /* define message structures */ @@ -60,7 +61,10 @@ _(IPSEC_SPD_INTERFACE_DUMP, ipsec_spd_interface_dump) \ _(IPSEC_TUNNEL_IF_ADD_DEL, ipsec_tunnel_if_add_del) \ _(IPSEC_TUNNEL_IF_SET_SA, ipsec_tunnel_if_set_sa) \ _(IPSEC_SELECT_BACKEND, ipsec_select_backend) \ -_(IPSEC_BACKEND_DUMP, ipsec_backend_dump) +_(IPSEC_BACKEND_DUMP, ipsec_backend_dump) \ +_(IPSEC_TUNNEL_PROTECT_UPDATE, ipsec_tunnel_protect_update) \ +_(IPSEC_TUNNEL_PROTECT_DEL, ipsec_tunnel_protect_del) \ +_(IPSEC_TUNNEL_PROTECT_DUMP, ipsec_tunnel_protect_dump) static void vl_api_ipsec_spd_add_del_t_handler (vl_api_ipsec_spd_add_del_t * mp) @@ -104,6 +108,132 @@ static void vl_api_ipsec_interface_add_del_spd_t_handler REPLY_MACRO (VL_API_IPSEC_INTERFACE_ADD_DEL_SPD_REPLY); } +static void vl_api_ipsec_tunnel_protect_update_t_handler + (vl_api_ipsec_tunnel_protect_update_t * mp) +{ + vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main (); + vl_api_ipsec_tunnel_protect_update_reply_t *rmp; + u32 sw_if_index, ii, *sa_ins = NULL; + int rv; + + sw_if_index = ntohl (mp->tunnel.sw_if_index); + + VALIDATE_SW_IF_INDEX (&(mp->tunnel)); + +#if WITH_LIBSSL > 0 + + for (ii = 0; ii < mp->tunnel.n_sa_in; ii++) + vec_add1 (sa_ins, ntohl (mp->tunnel.sa_in[ii])); + + rv = ipsec_tun_protect_update (sw_if_index, + ntohl (mp->tunnel.sa_out), sa_ins); +#else + rv = VNET_API_ERROR_UNIMPLEMENTED; +#endif + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_IPSEC_TUNNEL_PROTECT_UPDATE_REPLY); +} + +static void vl_api_ipsec_tunnel_protect_del_t_handler + (vl_api_ipsec_tunnel_protect_del_t * mp) +{ + vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main (); + vl_api_ipsec_tunnel_protect_del_reply_t *rmp; + int rv; + u32 sw_if_index; + + sw_if_index = ntohl (mp->sw_if_index); + + VALIDATE_SW_IF_INDEX (mp); + +#if WITH_LIBSSL > 0 + rv = ipsec_tun_protect_del (sw_if_index); +#else + rv = VNET_API_ERROR_UNIMPLEMENTED; +#endif + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_IPSEC_TUNNEL_PROTECT_DEL_REPLY); +} + +typedef struct ipsec_tunnel_protect_walk_ctx_t_ +{ + vl_api_registration_t *reg; + u32 context; +} ipsec_tunnel_protect_walk_ctx_t; + +static walk_rc_t +send_ipsec_tunnel_protect_details (index_t itpi, void *arg) +{ + ipsec_tunnel_protect_walk_ctx_t *ctx = arg; + vl_api_ipsec_tunnel_protect_details_t *mp; + ipsec_tun_protect_t *itp; + u32 sai, ii = 0; + + itp = ipsec_tun_protect_get (itpi); + + + mp = vl_msg_api_alloc (sizeof (*mp) + (sizeof (u32) * itp->itp_n_sa_in)); + clib_memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_IPSEC_TUNNEL_PROTECT_DETAILS); + mp->context = ctx->context; + + mp->tun.sw_if_index = htonl (itp->itp_sw_if_index); + + mp->tun.sa_out = htonl (itp->itp_out_sa); + mp->tun.n_sa_in = itp->itp_n_sa_in; + /* *INDENT-OFF* */ + FOR_EACH_IPSEC_PROTECT_INPUT_SAI(itp, sai, + ({ + mp->tun.sa_in[ii++] = htonl (sai); + })); + /* *INDENT-ON* */ + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (WALK_CONTINUE); +} + +static void +vl_api_ipsec_tunnel_protect_dump_t_handler (vl_api_ipsec_tunnel_protect_dump_t + * mp) +{ + vl_api_registration_t *reg; + u32 sw_if_index; + +#if WITH_LIBSSL > 0 + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + ipsec_tunnel_protect_walk_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + + sw_if_index = ntohl (mp->sw_if_index); + + if (~0 == sw_if_index) + { + ipsec_tun_protect_walk (send_ipsec_tunnel_protect_details, &ctx); + } + else + { + index_t itpi; + + itpi = ipsec_tun_protect_find (sw_if_index); + + if (INDEX_INVALID != itpi) + send_ipsec_tunnel_protect_details (itpi, &ctx); + } +#else + clib_warning ("unimplemented"); +#endif +} + static int ipsec_spd_action_decode (vl_api_ipsec_spd_action_t in, ipsec_policy_action_t * out) @@ -878,6 +1008,13 @@ ipsec_api_hookup (vlib_main_t * vm) foreach_vpe_api_msg; #undef _ + /* + * Adding and deleting SAs is MP safe since when they are added/delete + * no traffic is using them + */ + am->is_mp_safe[VL_API_IPSEC_SAD_ENTRY_ADD_DEL] = 1; + am->is_mp_safe[VL_API_IPSEC_SAD_ENTRY_ADD_DEL_REPLY] = 1; + /* * Set up the (msg_name, crc, message-id) table */ diff --git a/src/vnet/ipsec/ipsec_cli.c b/src/vnet/ipsec/ipsec_cli.c index 4172e104a98..1648179bc20 100644 --- a/src/vnet/ipsec/ipsec_cli.c +++ b/src/vnet/ipsec/ipsec_cli.c @@ -22,6 +22,7 @@ #include #include +#include static clib_error_t * set_interface_spd_command_fn (vlib_main_t * vm, @@ -105,7 +106,7 @@ ipsec_sa_add_del_command_fn (vlib_main_t * vm, is_add = 0; else if (unformat (line_input, "spi %u", &spi)) ; - else if (unformat (line_input, "salt %u", &salt)) + else if (unformat (line_input, "salt 0x%x", &salt)) ; else if (unformat (line_input, "esp")) proto = IPSEC_PROTOCOL_ESP; @@ -446,12 +447,52 @@ show_ipsec_sa_command_fn (vlib_main_t * vm, return 0; } +static clib_error_t * +clear_ipsec_sa_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + ipsec_main_t *im = &ipsec_main; + u32 sai = ~0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%u", &sai)) + ; + else + break; + } + + if (~0 == sai) + { + /* *INDENT-OFF* */ + pool_foreach_index (sai, im->sad, ({ + ipsec_sa_clear(sai); + })); + /* *INDENT-ON* */ + } + else + { + if (pool_is_free_index (im->sad, sai)) + return clib_error_return (0, "unknown SA index: %d", sai); + else + ipsec_sa_clear (sai); + } + + return 0; +} + /* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_ipsec_sa_command, static) = { .path = "show ipsec sa", .short_help = "show ipsec sa [index]", .function = show_ipsec_sa_command_fn, }; + +VLIB_CLI_COMMAND (clear_ipsec_sa_command, static) = { + .path = "clear ipsec sa", + .short_help = "clear ipsec sa [index]", + .function = clear_ipsec_sa_command_fn, +}; /* *INDENT-ON* */ static clib_error_t * @@ -823,6 +864,88 @@ VLIB_CLI_COMMAND (create_ipsec_tunnel_command, static) = { }; /* *INDENT-ON* */ +static clib_error_t * +ipsec_tun_protect_cmd (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + u32 sw_if_index, is_del, sa_in, sa_out, *sa_ins = NULL; + vnet_main_t *vnm; + + is_del = 0; + sw_if_index = ~0; + vnm = vnet_get_main (); + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "del")) + is_del = 1; + else if (unformat (line_input, "add")) + is_del = 0; + else if (unformat (line_input, "sa-in %d", &sa_in)) + vec_add1 (sa_ins, sa_in); + else if (unformat (line_input, "sa-out %d", &sa_out)) + ; + else if (unformat (line_input, "%U", + unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; + else + return (clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input)); + } + + if (!is_del) + ipsec_tun_protect_update (sw_if_index, sa_out, sa_ins); + + unformat_free (line_input); + return NULL; +} + +/** + * Protect tunnel with IPSEC + */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ipsec_tun_protect_cmd_node, static) = +{ + .path = "ipsec tunnel protect", + .function = ipsec_tun_protect_cmd, + .short_help = "ipsec tunnel protect input-sa output-sa ", + // this is not MP safe +}; +/* *INDENT-ON* */ + +static walk_rc_t +ipsec_tun_protect_show_one (index_t itpi, void *ctx) +{ + vlib_cli_output (ctx, "%U", format_ipsec_tun_protect, itpi); + + return (WALK_CONTINUE); +} + +static clib_error_t * +ipsec_tun_protect_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + ipsec_tun_protect_walk (ipsec_tun_protect_show_one, vm); + + return NULL; +} + +/** + * show IPSEC tunnel protection + */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ipsec_tun_protect_show_node, static) = +{ + .path = "show ipsec protect", + .function = ipsec_tun_protect_show, + .short_help = "show ipsec protect", +}; +/* *INDENT-ON* */ + clib_error_t * ipsec_cli_init (vlib_main_t * vm) { diff --git a/src/vnet/ipsec/ipsec_format.c b/src/vnet/ipsec/ipsec_format.c index d0d073bd2bb..1e6e2d5cb01 100644 --- a/src/vnet/ipsec/ipsec_format.c +++ b/src/vnet/ipsec/ipsec_format.c @@ -22,6 +22,7 @@ #include #include +#include u8 * format_ipsec_policy_action (u8 * s, va_list * args) @@ -368,6 +369,40 @@ done: return (s); } +u8 * +format_ipsec_tun_protect (u8 * s, va_list * args) +{ + u32 itpi = va_arg (*args, u32); + ipsec_tun_protect_t *itp; + u32 sai; + + if (pool_is_free_index (ipsec_protect_pool, itpi)) + { + s = format (s, "No such tunnel index: %d", itpi); + goto done; + } + + itp = pool_elt_at_index (ipsec_protect_pool, itpi); + + s = format (s, "%U", format_vnet_sw_if_index_name, + vnet_get_main (), itp->itp_sw_if_index); + s = format (s, "\n output-sa:"); + s = + format (s, "\n %U", format_ipsec_sa, itp->itp_out_sa, + IPSEC_FORMAT_BRIEF); + + s = format (s, "\n input-sa:"); + /* *INDENT-OFF* */ + FOR_EACH_IPSEC_PROTECT_INPUT_SAI(itp, sai, + ({ + s = format (s, "\n %U", format_ipsec_sa, sai, IPSEC_FORMAT_BRIEF); + })); + /* *INDENT-ON* */ + +done: + return (s); +} + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vnet/ipsec/ipsec_if.c b/src/vnet/ipsec/ipsec_if.c index 6627d295f1e..8e4f3f1359d 100644 --- a/src/vnet/ipsec/ipsec_if.c +++ b/src/vnet/ipsec/ipsec_if.c @@ -84,7 +84,7 @@ ipsec_if_tunnel_stack (adj_index_t ai) } /** - * @brief Call back when restacking all adjacencies on a GRE interface + * @brief Call back when restacking all adjacencies on a IPSec interface */ static adj_walk_rc_t ipsec_if_adj_walk_cb (adj_index_t ai, void *ctx) @@ -100,7 +100,7 @@ ipsec_if_tunnel_restack (ipsec_tunnel_if_t * it) fib_protocol_t proto; /* - * walk all the adjacencies on th GRE interface and restack them + * walk all the adjacencies on the IPSec interface and restack them */ FOR_EACH_FIB_IP_PROTOCOL (proto) { @@ -433,86 +433,6 @@ ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm, return 0; } -int -ipsec_add_del_ipsec_gre_tunnel (vnet_main_t * vnm, - const ipsec_gre_tunnel_add_del_args_t * args) -{ - ipsec_tunnel_if_t *t = 0; - ipsec_main_t *im = &ipsec_main; - uword *p; - ipsec_sa_t *sa; - ipsec4_tunnel_key_t key; - u32 isa, osa; - - p = hash_get (im->sa_index_by_sa_id, args->local_sa_id); - if (!p) - return VNET_API_ERROR_INVALID_VALUE; - osa = p[0]; - sa = pool_elt_at_index (im->sad, p[0]); - ipsec_sa_set_IS_GRE (sa); - - p = hash_get (im->sa_index_by_sa_id, args->remote_sa_id); - if (!p) - return VNET_API_ERROR_INVALID_VALUE; - isa = p[0]; - sa = pool_elt_at_index (im->sad, p[0]); - ipsec_sa_set_IS_GRE (sa); - - /* we form the key from the input/remote SA whose tunnel is srouce - * at the remote end */ - if (ipsec_sa_is_set_IS_TUNNEL (sa)) - { - key.remote_ip = sa->tunnel_src_addr.ip4.as_u32; - key.spi = clib_host_to_net_u32 (sa->spi); - } - else - { - key.remote_ip = args->src.as_u32; - key.spi = clib_host_to_net_u32 (sa->spi); - } - - p = hash_get (im->ipsec4_if_pool_index_by_key, key.as_u64); - - if (args->is_add) - { - /* check if same src/dst pair exists */ - if (p) - return VNET_API_ERROR_INVALID_VALUE; - - pool_get_aligned (im->tunnel_interfaces, t, CLIB_CACHE_LINE_BYTES); - clib_memset (t, 0, sizeof (*t)); - - t->input_sa_index = isa; - t->output_sa_index = osa; - t->hw_if_index = ~0; - hash_set (im->ipsec4_if_pool_index_by_key, key.as_u64, - t - im->tunnel_interfaces); - - /*1st interface, register protocol */ - if (pool_elts (im->tunnel_interfaces) == 1) - { - ip4_register_protocol (IP_PROTOCOL_IPSEC_ESP, - ipsec4_if_input_node.index); - /* TBD, GRE IPSec6 - * - ip6_register_protocol (IP_PROTOCOL_IPSEC_ESP, - ipsec6_if_input_node.index); - */ - } - } - else - { - /* check if exists */ - if (!p) - return VNET_API_ERROR_INVALID_VALUE; - - t = pool_elt_at_index (im->tunnel_interfaces, p[0]); - hash_unset (im->ipsec4_if_pool_index_by_key, key.as_u64); - pool_put (im->tunnel_interfaces, t); - } - return 0; -} - int ipsec_set_interface_sa (vnet_main_t * vnm, u32 hw_if_index, u32 sa_id, u8 is_outbound) diff --git a/src/vnet/ipsec/ipsec_if.h b/src/vnet/ipsec/ipsec_if.h index 40867108293..042dddee880 100644 --- a/src/vnet/ipsec/ipsec_if.h +++ b/src/vnet/ipsec/ipsec_if.h @@ -84,23 +84,10 @@ typedef CLIB_PACKED }) ipsec6_tunnel_key_t; /* *INDENT-ON* */ -typedef struct -{ - u8 is_add; - u32 local_sa_id; - u32 remote_sa_id; - ip4_address_t src; - ip4_address_t dst; -} ipsec_gre_tunnel_add_del_args_t; - extern int ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm, ipsec_add_del_tunnel_args_t * args, u32 * sw_if_index); extern int ipsec_add_del_tunnel_if (ipsec_add_del_tunnel_args_t * args); -extern int ipsec_add_del_ipsec_gre_tunnel (vnet_main_t * vnm, - const - ipsec_gre_tunnel_add_del_args_t * - args); extern int ipsec_set_interface_sa (vnet_main_t * vnm, u32 hw_if_index, u32 sa_id, u8 is_outbound); diff --git a/src/vnet/ipsec/ipsec_io.h b/src/vnet/ipsec/ipsec_io.h index 32668a0474f..b17d3d7ecf0 100644 --- a/src/vnet/ipsec/ipsec_io.h +++ b/src/vnet/ipsec/ipsec_io.h @@ -30,9 +30,9 @@ typedef enum _ (PUNT, "punt-dispatch") \ _ (DROP, "error-drop") -#define _(v, s) IPSEC_INPUT_NEXT_##v, typedef enum { +#define _(v, s) IPSEC_INPUT_NEXT_##v, foreach_ipsec_input_next #undef _ IPSEC_INPUT_N_NEXT, diff --git a/src/vnet/ipsec/ipsec_sa.c b/src/vnet/ipsec/ipsec_sa.c index e8a015957ce..afdecfee10d 100644 --- a/src/vnet/ipsec/ipsec_sa.c +++ b/src/vnet/ipsec/ipsec_sa.c @@ -17,6 +17,7 @@ #include #include #include +#include /** * @brief @@ -292,7 +293,7 @@ ipsec_sa_del (u32 id) { clib_warning ("sa_id %u used in policy", sa->id); /* sa used in policy */ - return VNET_API_ERROR_SYSCALL_ERROR_1; + return VNET_API_ERROR_RSRC_IN_USE; } hash_unset (im->sa_index_by_sa_id, sa->id); err = ipsec_call_add_del_callbacks (im, sa, sa_index, 0); @@ -313,12 +314,20 @@ ipsec_sa_del (u32 id) return 0; } +void +ipsec_sa_clear (index_t sai) +{ + vlib_zero_combined_counter (&ipsec_sa_counters, sai); +} + u8 ipsec_is_sa_used (u32 sa_index) { ipsec_main_t *im = &ipsec_main; + ipsec_tun_protect_t *itp; ipsec_tunnel_if_t *t; ipsec_policy_t *p; + u32 sai; /* *INDENT-OFF* */ pool_foreach(p, im->policies, ({ @@ -335,8 +344,20 @@ ipsec_is_sa_used (u32 sa_index) if (t->output_sa_index == sa_index) return 1; })); + + /* *INDENT-OFF* */ + pool_foreach(itp, ipsec_protect_pool, ({ + FOR_EACH_IPSEC_PROTECT_INPUT_SAI(itp, sai, + ({ + if (sai == sa_index) + return 1; + })); + if (itp->itp_out_sa == sa_index) + return 1; + })); /* *INDENT-ON* */ + return 0; } @@ -415,7 +436,7 @@ ipsec_sa_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx) } /* - * Virtual function table registered by MPLS GRE tunnels + * Virtual function table registered by SAs * for participation in the FIB object graph. */ const static fib_node_vft_t ipsec_sa_vft = { diff --git a/src/vnet/ipsec/ipsec_sa.h b/src/vnet/ipsec/ipsec_sa.h index 53035aa2a20..284826772a0 100644 --- a/src/vnet/ipsec/ipsec_sa.h +++ b/src/vnet/ipsec/ipsec_sa.h @@ -93,7 +93,7 @@ typedef struct ipsec_key_t_ _ (4, IS_TUNNEL, "tunnel") \ _ (8, IS_TUNNEL_V6, "tunnel-v6") \ _ (16, UDP_ENCAP, "udp-encap") \ - _ (32, IS_GRE, "GRE") \ + _ (32, IS_PROTECT, "Protect") \ _ (64, IS_INBOUND, "inboud") \ _ (128, IS_AEAD, "aead") \ @@ -183,6 +183,13 @@ foreach_ipsec_sa_flags } foreach_ipsec_sa_flags #undef _ +#define _(a,v,s) \ + always_inline int \ + ipsec_sa_unset_##v (ipsec_sa_t *sa) { \ + return (sa->flags &= ~IPSEC_SA_FLAG_##v); \ + } + foreach_ipsec_sa_flags +#undef _ /** * @brief * SA packet & bytes counters @@ -205,6 +212,7 @@ extern int ipsec_sa_add (u32 id, const ip46_address_t * tunnel_dst_addr, u32 * sa_index); extern u32 ipsec_sa_del (u32 id); +extern void ipsec_sa_clear (index_t sai); extern void ipsec_sa_set_crypto_alg (ipsec_sa_t * sa, ipsec_crypto_alg_t crypto_alg); extern void ipsec_sa_set_integ_alg (ipsec_sa_t * sa, diff --git a/src/vnet/ipsec/ipsec_tun.c b/src/vnet/ipsec/ipsec_tun.c new file mode 100644 index 00000000000..a389cefc991 --- /dev/null +++ b/src/vnet/ipsec/ipsec_tun.c @@ -0,0 +1,398 @@ +/* + * ipsec_tun.h : IPSEC tunnel protection + * + * Copyright (c) 2015 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +/** + * Pool of tunnel protection objects + */ +ipsec_tun_protect_t *ipsec_protect_pool; + +/** + * DB of protected tunnels + */ +typedef struct ipsec_protect_db_t_ +{ + u32 *tunnels; + u32 count; +} ipsec_protect_db_t; + +static ipsec_protect_db_t ipsec_protect_db; + +static int +ipsec_tun_protect_feature_set (ipsec_tun_protect_t * itp, u8 enable) +{ + u32 sai = itp->itp_out_sa; + int is_ip4, is_l2, rv; + + is_ip4 = ip46_address_is_ip4 (&itp->itp_tun.src); + is_l2 = itp->itp_flags & IPSEC_PROTECT_L2; + + if (is_ip4) + { + if (is_l2) + rv = vnet_feature_enable_disable ("ethernet-output", + "esp4-encrypt-tun", + itp->itp_sw_if_index, enable, + &sai, sizeof (sai)); + else + rv = vnet_feature_enable_disable ("ip4-output", + "esp4-encrypt-tun", + itp->itp_sw_if_index, enable, + &sai, sizeof (sai)); + } + else + { + if (is_l2) + rv = vnet_feature_enable_disable ("ethernet-output", + "esp6-encrypt-tun", + itp->itp_sw_if_index, enable, + &sai, sizeof (sai)); + else + rv = vnet_feature_enable_disable ("ip6-output", + "esp6-encrypt-tun", + itp->itp_sw_if_index, enable, + &sai, sizeof (sai)); + } + + ASSERT (!rv); + return (rv); +} + +static void +ipsec_tun_protect_db_add (ipsec_main_t * im, const ipsec_tun_protect_t * itp) +{ + const ipsec_sa_t *sa; + u32 sai; + + /* *INDENT-OFF* */ + FOR_EACH_IPSEC_PROTECT_INPUT_SAI(itp, sai, + ({ + sa = ipsec_sa_get (sai); + + ipsec_tun_lkup_result_t res = { + .tun_index = itp - ipsec_protect_pool, + .sa_index = sai, + }; + + /* + * The key is formed from the tunnel's destination + * as the packet lookup is done from the packet's source + */ + if (ip46_address_is_ip4 (&itp->itp_crypto.dst)) + { + ipsec4_tunnel_key_t key = { + .remote_ip = itp->itp_crypto.dst.ip4.as_u32, + .spi = clib_host_to_net_u32 (sa->spi), + }; + hash_set (im->tun4_protect_by_key, key.as_u64, res.as_u64); + } + else + { + ipsec6_tunnel_key_t key = { + .remote_ip = itp->itp_crypto.dst.ip6, + .spi = clib_host_to_net_u32 (sa->spi), + }; + hash_set_mem_alloc (&im->tun6_protect_by_key, &key, res.as_u64); + } + })) + /* *INDENT-ON* */ +} + +static void +ipsec_tun_protect_db_remove (ipsec_main_t * im, + const ipsec_tun_protect_t * itp) +{ + const ipsec_sa_t *sa; + + /* *INDENT-OFF* */ + FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa, + ({ + if (ip46_address_is_ip4 (&itp->itp_crypto.dst)) + { + ipsec4_tunnel_key_t key = { + .remote_ip = itp->itp_crypto.dst.ip4.as_u32, + .spi = clib_host_to_net_u32 (sa->spi), + }; + hash_unset (im->tun4_protect_by_key, &key); + } + else + { + ipsec6_tunnel_key_t key = { + .remote_ip = itp->itp_crypto.dst.ip6, + .spi = clib_host_to_net_u32 (sa->spi), + }; + hash_unset_mem_free (&im->tun6_protect_by_key, &key); + } + })) + /* *INDENT-ON* */ +} + +static void +ipsec_tun_protect_config (ipsec_main_t * im, + ipsec_tun_protect_t * itp, u32 sa_out, u32 * sas_in) +{ + ipsec_sa_t *sa; + u32 ii; + + itp->itp_n_sa_in = vec_len (sas_in); + for (ii = 0; ii < itp->itp_n_sa_in; ii++) + itp->itp_in_sas[ii] = sas_in[ii]; + itp->itp_out_sa = sa_out; + + /* *INDENT-OFF* */ + FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa, + ({ + if (ipsec_sa_is_set_IS_TUNNEL (sa)) + { + itp->itp_crypto.src = sa->tunnel_dst_addr; + itp->itp_crypto.dst = sa->tunnel_src_addr; + ipsec_sa_set_IS_PROTECT (sa); + itp->itp_flags |= IPSEC_PROTECT_ENCAPED; + } + else + { + itp->itp_crypto.src = itp->itp_tun.src; + itp->itp_crypto.dst = itp->itp_tun.dst; + itp->itp_flags &= ~IPSEC_PROTECT_ENCAPED; + } + })); + /* *INDENT-ON* */ + + /* + * add to the DB against each SA + */ + ipsec_tun_protect_db_add (im, itp); + + /* + * enable the encrypt feature for egress. + */ + ipsec_tun_protect_feature_set (itp, 1); + +} + +static void +ipsec_tun_protect_unconfig (ipsec_main_t * im, ipsec_tun_protect_t * itp) +{ + ipsec_sa_t *sa; + + ipsec_tun_protect_feature_set (itp, 0); + + /* *INDENT-OFF* */ + FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa, + ({ + ipsec_sa_unset_IS_PROTECT (sa); + })); + /* *INDENT-ON* */ + + ipsec_tun_protect_db_remove (im, itp); +} + +index_t +ipsec_tun_protect_find (u32 sw_if_index) +{ + if (vec_len (ipsec_protect_db.tunnels) < sw_if_index) + return (INDEX_INVALID); + + return (ipsec_protect_db.tunnels[sw_if_index]); +} + +int +ipsec_tun_protect_update (u32 sw_if_index, u32 sa_out, u32 * sas_in) +{ + u32 itpi, ii; + ipsec_tun_protect_t *itp; + ipsec_main_t *im; + int rv; + + rv = 0; + im = &ipsec_main; + vec_validate_init_empty (ipsec_protect_db.tunnels, sw_if_index, + INDEX_INVALID); + itpi = ipsec_protect_db.tunnels[sw_if_index]; + + vec_foreach_index (ii, sas_in) + { + sas_in[ii] = ipsec_get_sa_index_by_sa_id (sas_in[ii]); + if (~0 == sas_in[ii]) + { + rv = VNET_API_ERROR_INVALID_VALUE; + goto out; + } + } + + sa_out = ipsec_get_sa_index_by_sa_id (sa_out); + + if (~0 == sa_out) + { + rv = VNET_API_ERROR_INVALID_VALUE; + goto out; + } + + if (INDEX_INVALID == itpi) + { + vnet_device_class_t *dev_class; + vnet_hw_interface_t *hi; + vnet_main_t *vnm; + u8 is_l2; + + vnm = vnet_get_main (); + hi = vnet_get_sup_hw_interface (vnm, sw_if_index); + dev_class = vnet_get_device_class (vnm, hi->dev_class_index); + + if (NULL == dev_class->ip_tun_desc) + { + rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; + goto out; + } + + pool_get_zero (ipsec_protect_pool, itp); + + itp->itp_sw_if_index = sw_if_index; + ipsec_protect_db.tunnels[sw_if_index] = itp - ipsec_protect_pool; + ipsec_protect_db.count++; + + itp->itp_n_sa_in = vec_len (sas_in); + for (ii = 0; ii < itp->itp_n_sa_in; ii++) + itp->itp_in_sas[ii] = sas_in[ii]; + itp->itp_out_sa = sa_out; + + rv = dev_class->ip_tun_desc (sw_if_index, + &itp->itp_tun.src, + &itp->itp_tun.dst, &is_l2); + + if (rv) + goto out; + + if (is_l2) + itp->itp_flags |= IPSEC_PROTECT_L2; + + /* + * add to the tunnel DB for ingress + * - if the SA is in trasnport mode, then the packates will arrivw + * with the IP src,dst of the protected tunnel, in which case we can + * simply strip the IP header and hand the payload to the protocol + * appropriate input handler + * - if the SA is in tunnel mode then there are two IP headers present + * one for the crytpo tunnel endpoints (described in the SA) and one + * for the tunnel endpoints. The outer IP headers in the srriving + * packets will have the crypto endpoints. So the DB needs to contain + * the crpto endpoint. Once the crypto header is stripped, revealing, + * the tunnel-IP we have 2 choices: + * 1) do a tunnel lookup based on the revealed header + * 2) skip the tunnel lookup and assume that the packet matches the + * one that is protected here. + * If we did 1) then we would allow our peer to use the SA for tunnel + * X to inject traffic onto tunnel Y, this is not good. If we do 2) + * then we don't verify that the peer is indeed using SA for tunnel + * X and addressing tunnel X. So we take a compromise, once the SA + * matches to tunnel X we veriy that the inner IP matches the value + * of the tunnel we are protecting, else it's dropped. + */ + ipsec_tun_protect_config (im, itp, sa_out, sas_in); + + if (1 == hash_elts (im->tun4_protect_by_key)) + ip4_register_protocol (IP_PROTOCOL_IPSEC_ESP, + ipsec4_tun_input_node.index); + if (1 == hash_elts (im->tun6_protect_by_key)) + ip6_register_protocol (IP_PROTOCOL_IPSEC_ESP, + ipsec6_tun_input_node.index); + } + else + { + /* updating SAs only */ + itp = pool_elt_at_index (ipsec_protect_pool, itpi); + + ipsec_tun_protect_unconfig (im, itp); + ipsec_tun_protect_config (im, itp, sa_out, sas_in); + } + + vec_free (sas_in); +out: + return (rv); +} + +int +ipsec_tun_protect_del (u32 sw_if_index) +{ + ipsec_tun_protect_t *itp; + ipsec_main_t *im; + index_t itpi; + + im = &ipsec_main; + + vec_validate_init_empty (ipsec_protect_db.tunnels, sw_if_index, + INDEX_INVALID); + itpi = ipsec_protect_db.tunnels[sw_if_index]; + + if (INDEX_INVALID == itpi) + return (VNET_API_ERROR_NO_SUCH_ENTRY); + + itp = ipsec_tun_protect_get (itpi); + ipsec_tun_protect_unconfig (im, itp); + + ipsec_protect_db.tunnels[itp->itp_sw_if_index] = INDEX_INVALID; + + pool_put (ipsec_protect_pool, itp); + + /* if (0 == hash_elts (im->tun4_protect_by_key)) */ + /* ip4_unregister_protocol (IP_PROTOCOL_IPSEC_ESP); */ + /* if (0 == hash_elts (im->tun6_protect_by_key)) */ + /* ip6_unregister_protocol (IP_PROTOCOL_IPSEC_ESP); */ + + return (0); +} + +void +ipsec_tun_protect_walk (ipsec_tun_protect_walk_cb_t fn, void *ctx) +{ + index_t itpi; + + /* *INDENT-OFF* */ + pool_foreach_index(itpi, ipsec_protect_pool, + ({ + fn (itpi, ctx); + })); + /* *INDENT-ON* */ +} + +clib_error_t * +ipsec_tunnel_protect_init (vlib_main_t * vm) +{ + ipsec_main_t *im; + + im = &ipsec_main; + im->tun6_protect_by_key = hash_create_mem (0, + sizeof (ipsec6_tunnel_key_t), + sizeof (u64)); + im->tun4_protect_by_key = hash_create (0, sizeof (u64)); + + return 0; +} + +VLIB_INIT_FUNCTION (ipsec_tunnel_protect_init); + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/ipsec/ipsec_tun.h b/src/vnet/ipsec/ipsec_tun.h new file mode 100644 index 00000000000..be5cef9a8fc --- /dev/null +++ b/src/vnet/ipsec/ipsec_tun.h @@ -0,0 +1,114 @@ +/* + * ipsec_tun.h : IPSEC tunnel protection + * + * 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 + +typedef enum ipsec_protect_flags_t_ +{ + IPSEC_PROTECT_L2 = (1 << 0), + IPSEC_PROTECT_ENCAPED = (1 << 1), +} __clib_packed ipsec_protect_flags_t; + +typedef struct ipsec_ep_t_ +{ + ip46_address_t src; + ip46_address_t dst; +} ipsec_ep_t; + +typedef struct ipsec_tun_protect_t_ +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + u32 itp_out_sa; + + /* not using a vector since we want the memory inline + * with this struct */ + u32 itp_n_sa_in; + u32 itp_in_sas[4]; + + u32 itp_sw_if_index; + + ipsec_ep_t itp_crypto; + + ipsec_protect_flags_t itp_flags; + + ipsec_ep_t itp_tun; + +} ipsec_tun_protect_t; + +#define FOR_EACH_IPSEC_PROTECT_INPUT_SAI(_itp, _sai, body) \ +{ \ + u32 __ii; \ + for (__ii = 0; __ii < _itp->itp_n_sa_in; __ii++) { \ + _sai = itp->itp_in_sas[__ii]; \ + body; \ + } \ +} +#define FOR_EACH_IPSEC_PROTECT_INPUT_SA(_itp, _sa, body) \ +{ \ + u32 __ii; \ + for (__ii = 0; __ii < _itp->itp_n_sa_in; __ii++) { \ + _sa = ipsec_sa_get(itp->itp_in_sas[__ii]); \ + body; \ + } \ +} + +extern int ipsec_tun_protect_update (u32 sw_if_index, u32 sa_out, + u32 sa_ins[2]); +extern int ipsec_tun_protect_del (u32 sw_if_index); + +typedef walk_rc_t (*ipsec_tun_protect_walk_cb_t) (index_t itpi, void *arg); +extern void ipsec_tun_protect_walk (ipsec_tun_protect_walk_cb_t fn, + void *cttx); +extern index_t ipsec_tun_protect_find (u32 sw_if_index); + +extern u8 *format_ipsec_tun_protect (u8 * s, va_list * args); + +// FIXME +extern vlib_node_registration_t ipsec4_tun_input_node; +extern vlib_node_registration_t ipsec6_tun_input_node; + +/* + * DP API + */ +extern ipsec_tun_protect_t *ipsec_protect_pool; + +typedef struct ipsec_tun_lkup_result_t_ +{ + union + { + struct + { + u32 tun_index; + u32 sa_index; + }; + u64 as_u64; + }; +} ipsec_tun_lkup_result_t; + +always_inline ipsec_tun_protect_t * +ipsec_tun_protect_get (u32 index) +{ + return (pool_elt_at_index (ipsec_protect_pool, index)); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/ipsec/ipsec_tun_in.c b/src/vnet/ipsec/ipsec_tun_in.c new file mode 100644 index 00000000000..2ce1691b242 --- /dev/null +++ b/src/vnet/ipsec/ipsec_tun_in.c @@ -0,0 +1,436 @@ +/* + * ipsec_tun_protect_in.c : IPSec interface input node + * + * Copyright (c) 2015 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* Statistics (not really errors) */ +#define foreach_ipsec_tun_protect_input_error \ + _(RX, "good packets received") \ + _(DISABLED, "ipsec packets received on disabled interface") \ + _(NO_TUNNEL, "no matching tunnel") \ + _(TUNNEL_MISMATCH, "SPI-tunnel mismatch") \ + _(SPI_0, "SPI 0") + +static char *ipsec_tun_protect_input_error_strings[] = { +#define _(sym,string) string, + foreach_ipsec_tun_protect_input_error +#undef _ +}; + +typedef enum +{ +#define _(sym,str) IPSEC_TUN_PROTECT_INPUT_ERROR_##sym, + foreach_ipsec_tun_protect_input_error +#undef _ + IPSEC_TUN_PROTECT_INPUT_N_ERROR, +} ipsec_tun_protect_input_error_t; + +typedef enum ipsec_tun_next_t_ +{ +#define _(v, s) IPSEC_TUN_PROTECT_NEXT_##v, + foreach_ipsec_input_next +#undef _ + IPSEC_TUN_PROTECT_NEXT_DECRYPT, + IPSEC_TUN_PROTECT_N_NEXT, +} ipsec_tun_next_t; + +typedef struct +{ + u32 spi; + u32 seq; +} ipsec_tun_protect_input_trace_t; + +static u8 * +format_ipsec_tun_protect_input_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 *); + ipsec_tun_protect_input_trace_t *t = + va_arg (*args, ipsec_tun_protect_input_trace_t *); + + s = format (s, "IPSec: spi %u seq %u", t->spi, t->seq); + return s; +} + +always_inline u16 +ipsec_ip4_if_no_tunnel (vlib_node_runtime_t * node, + vlib_buffer_t * b, + const esp_header_t * esp, const ip4_header_t * ip4) +{ + if (PREDICT_FALSE (0 == esp->spi)) + { + b->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_SPI_0]; + b->punt_reason = ipsec_punt_reason[(ip4->protocol == IP_PROTOCOL_UDP ? + IPSEC_PUNT_IP4_SPI_UDP_0 : + IPSEC_PUNT_IP4_SPI_0)]; + } + else + { + b->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_NO_TUNNEL]; + b->punt_reason = ipsec_punt_reason[IPSEC_PUNT_IP4_NO_SUCH_TUNNEL]; + } + return IPSEC_INPUT_NEXT_PUNT; +} + +always_inline u16 +ipsec_ip6_if_no_tunnel (vlib_node_runtime_t * node, + vlib_buffer_t * b, const esp_header_t * esp) +{ + if (PREDICT_FALSE (0 == esp->spi)) + { + b->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_NO_TUNNEL]; + b->punt_reason = ipsec_punt_reason[IPSEC_PUNT_IP6_SPI_0]; + } + else + { + b->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_NO_TUNNEL]; + b->punt_reason = ipsec_punt_reason[IPSEC_PUNT_IP6_NO_SUCH_TUNNEL]; + } + return (IPSEC_INPUT_NEXT_PUNT); +} + +always_inline uword +ipsec_tun_protect_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * from_frame, int is_ip6) +{ + ipsec_main_t *im = &ipsec_main; + vnet_main_t *vnm = im->vnet_main; + vnet_interface_main_t *vim = &vnm->interface_main; + + int is_trace = node->flags & VLIB_NODE_FLAG_TRACE; + u32 thread_index = vm->thread_index; + + u32 n_left_from, *from; + u16 nexts[VLIB_FRAME_SIZE], *next; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + vlib_get_buffers (vm, from, bufs, n_left_from); + b = bufs; + next = nexts; + + clib_memset_u16 (nexts, im->esp4_decrypt_next_index, n_left_from); + + u64 n_bytes = 0, n_packets = 0; + u32 n_disabled = 0, n_no_tunnel = 0; + + u32 last_sw_if_index = ~0; + ipsec_tun_lkup_result_t last_result = { + .tun_index = ~0 + }; + ipsec4_tunnel_key_t last_key4; + ipsec6_tunnel_key_t last_key6; + + vlib_combined_counter_main_t *rx_counter; + vlib_combined_counter_main_t *drop_counter; + ipsec_tun_protect_t *itp0; + + if (is_ip6) + clib_memset (&last_key6, 0xff, sizeof (last_key6)); + else + last_key4.as_u64 = ~0; + + rx_counter = vim->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX; + drop_counter = vim->combined_sw_if_counters + VNET_INTERFACE_COUNTER_DROP; + + while (n_left_from > 0) + { + u32 sw_if_index0, len0, hdr_sz0; + ipsec_tun_lkup_result_t itr0; + ipsec4_tunnel_key_t key40; + ipsec6_tunnel_key_t key60; + ip4_header_t *ip40; + ip6_header_t *ip60; + esp_header_t *esp0; + + ip40 = vlib_buffer_get_current (b[0]); + + if (is_ip6) + { + ip60 = (ip6_header_t *) ip40; + esp0 = (esp_header_t *) (ip60 + 1); + hdr_sz0 = sizeof (ip6_header_t); + } + else + { + /* NAT UDP port 4500 case, don't advance any more */ + if (ip40->protocol == IP_PROTOCOL_UDP) + { + esp0 = + (esp_header_t *) ((u8 *) ip40 + ip4_header_bytes (ip40) + + sizeof (udp_header_t)); + hdr_sz0 = 0; + } + else + { + esp0 = (esp_header_t *) ((u8 *) ip40 + ip4_header_bytes (ip40)); + hdr_sz0 = ip4_header_bytes (ip40); + } + } + + /* stats for the tunnel include all the data after the IP header + just like a norml IP-IP tunnel */ + vlib_buffer_advance (b[0], hdr_sz0); + len0 = vlib_buffer_length_in_chain (vm, b[0]); + + if (is_ip6) + { + key60.remote_ip = ip60->src_address; + key60.spi = esp0->spi; + + if (memcmp (&key60, &last_key6, sizeof (last_key6)) == 0) + { + itr0 = last_result; + } + else + { + uword *p = hash_get_mem (im->tun6_protect_by_key, &key60); + if (p) + { + itr0.as_u64 = p[0]; + last_result = itr0; + clib_memcpy_fast (&last_key6, &key60, sizeof (key60)); + } + else + { + next[0] = ipsec_ip6_if_no_tunnel (node, b[0], esp0); + n_no_tunnel++; + goto trace00; + } + } + } + else + { + key40.remote_ip = ip40->src_address.as_u32; + key40.spi = esp0->spi; + + if (key40.as_u64 == last_key4.as_u64) + { + itr0 = last_result; + } + else + { + uword *p = hash_get (im->tun4_protect_by_key, key40.as_u64); + if (p) + { + itr0.as_u64 = p[0]; + last_result = itr0; + last_key4.as_u64 = key40.as_u64; + } + else + { + next[0] = ipsec_ip4_if_no_tunnel (node, b[0], esp0, ip40); + n_no_tunnel++; + goto trace00; + } + } + } + + itp0 = pool_elt_at_index (ipsec_protect_pool, itr0.tun_index); + vnet_buffer (b[0])->ipsec.sad_index = itr0.sa_index; + vnet_buffer (b[0])->ipsec.protect_index = itr0.tun_index; + + sw_if_index0 = itp0->itp_sw_if_index; + vnet_buffer (b[0])->sw_if_index[VLIB_RX] = sw_if_index0; + + if (PREDICT_FALSE (!vnet_sw_interface_is_admin_up (vnm, sw_if_index0))) + { + vlib_increment_combined_counter + (drop_counter, thread_index, sw_if_index0, 1, len0); + n_disabled++; + b[0]->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_DISABLED]; + next[0] = IPSEC_INPUT_NEXT_DROP; + goto trace00; + } + else + { + if (PREDICT_TRUE (sw_if_index0 == last_sw_if_index)) + { + n_packets++; + n_bytes += len0; + } + else + { + if (n_packets && !(itp0->itp_flags & IPSEC_PROTECT_ENCAPED)) + { + vlib_increment_combined_counter + (rx_counter, thread_index, last_sw_if_index, + n_packets, n_bytes); + } + + last_sw_if_index = sw_if_index0; + n_packets = 1; + n_bytes = len0; + } + + /* + * compare the packet's outer IP headers to that of the tunnels + */ + if (is_ip6) + { + if (PREDICT_FALSE + (!ip46_address_is_equal_v6 + (&itp0->itp_crypto.dst, &ip60->src_address) + || !ip46_address_is_equal_v6 (&itp0->itp_crypto.src, + &ip60->dst_address))) + { + b[0]->error = + node->errors + [IPSEC_TUN_PROTECT_INPUT_ERROR_TUNNEL_MISMATCH]; + next[0] = IPSEC_INPUT_NEXT_DROP; + goto trace00; + } + } + else + { + if (PREDICT_FALSE + (!ip46_address_is_equal_v4 + (&itp0->itp_crypto.dst, &ip40->src_address) + || !ip46_address_is_equal_v4 (&itp0->itp_crypto.src, + &ip40->dst_address))) + { + b[0]->error = + node->errors + [IPSEC_TUN_PROTECT_INPUT_ERROR_TUNNEL_MISMATCH]; + next[0] = IPSEC_INPUT_NEXT_DROP; + goto trace00; + } + } + + /* + * There are two encap possibilities + * 1) the tunnel and ths SA are prodiving encap, i.e. it's + * MAC | SA-IP | TUN-IP | ESP | PAYLOAD + * implying the SA is in tunnel mode (on a tunnel interface) + * 2) only the tunnel provides encap + * MAC | TUN-IP | ESP | PAYLOAD + * implying the SA is in transport mode. + * + * For 2) we need only strip the tunnel encap and we're good. + * since the tunnel and crypto ecnap (int the tun=protect + * object) are the same and we verified above that these match + * for 1) we need to strip the SA-IP outer headers, to + * reveal the tunnel IP and then check that this matches + * the configured tunnel. this we can;t do here since it + * involves a lookup in the per-tunnel-type DB - so ship + * the packet to the tunnel-types provided node to do that + */ + next[0] = IPSEC_TUN_PROTECT_NEXT_DECRYPT; + } + trace00: + if (PREDICT_FALSE (is_trace)) + { + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + ipsec_tun_protect_input_trace_t *tr = + vlib_add_trace (vm, node, b[0], sizeof (*tr)); + tr->spi = clib_host_to_net_u32 (esp0->spi); + tr->seq = clib_host_to_net_u32 (esp0->seq); + } + } + + /* next */ + b += 1; + next += 1; + n_left_from -= 1; + } + + if (n_packets && !(itp0->itp_flags & IPSEC_PROTECT_ENCAPED)) + { + vlib_increment_combined_counter (rx_counter, + thread_index, + last_sw_if_index, n_packets, n_bytes); + } + + vlib_node_increment_counter (vm, node->node_index, + IPSEC_TUN_PROTECT_INPUT_ERROR_RX, + from_frame->n_vectors - (n_disabled + + n_no_tunnel)); + + vlib_buffer_enqueue_to_next (vm, node, from, nexts, from_frame->n_vectors); + + return from_frame->n_vectors; +} + +VLIB_NODE_FN (ipsec4_tun_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return ipsec_tun_protect_input_inline (vm, node, from_frame, + 0 /* is_ip6 */ ); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ipsec4_tun_input_node) = { + .name = "ipsec4-tun-input", + .vector_size = sizeof (u32), + .format_trace = format_ipsec_tun_protect_input_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(ipsec_tun_protect_input_error_strings), + .error_strings = ipsec_tun_protect_input_error_strings, + .n_next_nodes = IPSEC_TUN_PROTECT_N_NEXT, + .next_nodes = { + [IPSEC_TUN_PROTECT_NEXT_DROP] = "ip4-drop", + [IPSEC_TUN_PROTECT_NEXT_PUNT] = "punt-dispatch", + [IPSEC_TUN_PROTECT_NEXT_DECRYPT] = "esp4-decrypt-tun", + } +}; +/* *INDENT-ON* */ + +VLIB_NODE_FN (ipsec6_tun_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return ipsec_tun_protect_input_inline (vm, node, from_frame, + 1 /* is_ip6 */ ); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ipsec6_tun_input_node) = { + .name = "ipsec6-tun-input", + .vector_size = sizeof (u32), + .format_trace = format_ipsec_tun_protect_input_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(ipsec_tun_protect_input_error_strings), + .error_strings = ipsec_tun_protect_input_error_strings, + .n_next_nodes = IPSEC_TUN_PROTECT_N_NEXT, + .next_nodes = { + [IPSEC_TUN_PROTECT_NEXT_DROP] = "ip6-drop", + [IPSEC_TUN_PROTECT_NEXT_PUNT] = "punt-dispatch", + [IPSEC_TUN_PROTECT_NEXT_DECRYPT] = "esp6-decrypt-tun", + } +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/vnet_all_api_h.h b/src/vnet/vnet_all_api_h.h index 31d9c3444c2..5dcdad4f987 100644 --- a/src/vnet/vnet_all_api_h.h +++ b/src/vnet/vnet_all_api_h.h @@ -48,7 +48,6 @@ #include #include #include -#include #include #include #include diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c index d754c3ade1c..494e1ab5cc8 100644 --- a/src/vpp/api/custom_dump.c +++ b/src/vpp/api/custom_dump.c @@ -3245,40 +3245,6 @@ static void *vl_api_ipsec_tunnel_if_add_del_t_print FINISH; } -static void *vl_api_ipsec_gre_tunnel_add_del_t_print - (vl_api_ipsec_gre_tunnel_add_del_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: ipsec_gre_tunnel_add_del "); - - s = format (s, "dst %U ", format_vl_api_ip4_address, mp->tunnel.dst); - - s = format (s, "src %U ", format_vl_api_ip4_address, mp->tunnel.src); - - s = format (s, "local_sa %d ", ntohl (mp->tunnel.local_sa_id)); - - s = format (s, "remote_sa %d ", ntohl (mp->tunnel.remote_sa_id)); - - if (mp->is_add == 0) - s = format (s, "del "); - - FINISH; -} - -static void *vl_api_ipsec_gre_tunnel_dump_t_print - (vl_api_ipsec_gre_tunnel_dump_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: ipsec_gre_tunnel_dump "); - - if (mp->sw_if_index != ~0) - s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index)); - - FINISH; -} - static void *vl_api_l2_interface_pbb_tag_rewrite_t_print (vl_api_l2_interface_pbb_tag_rewrite_t * mp, void *handle) { @@ -3843,8 +3809,6 @@ _(SHOW_LISP_MAP_REGISTER_STATE, show_lisp_map_register_state) \ _(LISP_RLOC_PROBE_ENABLE_DISABLE, lisp_rloc_probe_enable_disable) \ _(LISP_MAP_REGISTER_ENABLE_DISABLE, lisp_map_register_enable_disable) \ _(IPSEC_TUNNEL_IF_ADD_DEL, ipsec_tunnel_if_add_del) \ -_(IPSEC_GRE_TUNNEL_ADD_DEL, ipsec_gre_tunnel_add_del) \ -_(IPSEC_GRE_TUNNEL_DUMP, ipsec_gre_tunnel_dump) \ _(DELETE_SUBIF, delete_subif) \ _(L2_INTERFACE_PBB_TAG_REWRITE, l2_interface_pbb_tag_rewrite) \ _(SET_PUNT, set_punt) \ diff --git a/src/vpp/api/vpe.api b/src/vpp/api/vpe.api index 94732bbc236..735b31d3faa 100644 --- a/src/vpp/api/vpe.api +++ b/src/vpp/api/vpe.api @@ -38,7 +38,6 @@ option version = "1.1.0"; * L2TP APIs: see .../src/vnet/l2tp/{l2tp.api, l2tp_api.c} * BFD APIs: see .../src/vnet/bfd/{bfd.api, bfd_api.c} * IPSEC APIs: see .../src/vnet/ipsec/{ipsec.api, ipsec_api.c} - * IPSEC-GRE APIs: see .../src/vnet/ipsec-gre/{ipsec_gre.api, ipsec_gre_api.c} * LISP APIs: see .../src/vnet/lisp/{lisp.api, lisp_api.c} * LISP-GPE APIs: see .../src/vnet/lisp-gpe/{lisp_gpe.api, lisp_gpe_api.c} * SESSION APIs: .../vnet/session/{session.api session_api.c} diff --git a/test/template_ipsec.py b/test/template_ipsec.py index 87565edfdd7..d714a93d378 100644 --- a/test/template_ipsec.py +++ b/test/template_ipsec.py @@ -656,59 +656,85 @@ class IpsecTun4Tests(IpsecTun4): class IpsecTun6(object): """ verify methods for Tunnel v6 """ - def verify_counters6(self, p, count): - if (hasattr(p, "tun_sa_in")): - pkts = p.tun_sa_in.get_stats()['packets'] + def verify_counters6(self, p_in, p_out, count): + if (hasattr(p_in, "tun_sa_in")): + pkts = p_in.tun_sa_in.get_stats()['packets'] self.assertEqual(pkts, count, "incorrect SA in counts: expected %d != %d" % (count, pkts)) - pkts = p.tun_sa_out.get_stats()['packets'] + if (hasattr(p_out, "tun_sa_out")): + pkts = p_out.tun_sa_out.get_stats()['packets'] self.assertEqual(pkts, count, "incorrect SA out counts: expected %d != %d" % (count, pkts)) self.assert_packet_counter_equal(self.tun6_encrypt_node_name, count) self.assert_packet_counter_equal(self.tun6_decrypt_node_name, count) - def verify_tun_66(self, p, count=1): - """ ipsec 6o6 tunnel basic test """ + def verify_decrypted6(self, p, rxs): + for rx in rxs: + self.assert_equal(rx[IPv6].src, p.remote_tun_if_host) + self.assert_equal(rx[IPv6].dst, self.pg1.remote_ip6) + self.assert_packet_checksums_valid(rx) + + def verify_encrypted6(self, p, sa, rxs): + for rx in rxs: + self.assert_packet_checksums_valid(rx) + self.assertEqual(len(rx) - len(Ether()) - len(IPv6()), + rx[IPv6].plen) + try: + decrypt_pkt = p.vpp_tun_sa.decrypt(rx[IPv6]) + if not decrypt_pkt.haslayer(IPv6): + decrypt_pkt = IPv6(decrypt_pkt[Raw].load) + self.assert_packet_checksums_valid(decrypt_pkt) + self.assert_equal(decrypt_pkt.src, self.pg1.remote_ip6) + self.assert_equal(decrypt_pkt.dst, p.remote_tun_if_host) + except: + self.logger.debug(ppp("Unexpected packet:", rx)) + try: + self.logger.debug(ppp("Decrypted packet:", decrypt_pkt)) + except: + pass + raise + + def verify_drop_tun_66(self, p_in, count=1, payload_size=64): self.vapi.cli("clear errors") + self.vapi.cli("clear ipsec sa") + + config_tun_params(p_in, self.encryption_type, self.tun_if) + send_pkts = self.gen_encrypt_pkts6(p_in.scapy_tun_sa, self.tun_if, + src=p_in.remote_tun_if_host, + dst=self.pg1.remote_ip6, + count=count) + self.send_and_assert_no_replies(self.tun_if, send_pkts) + self.logger.info(self.vapi.cli("sh punt stats")) + + def verify_tun_66(self, p_in, p_out=None, count=1, payload_size=64): + self.vapi.cli("clear errors") + self.vapi.cli("clear ipsec sa") + if not p_out: + p_out = p_in try: - config_tun_params(p, self.encryption_type, self.tun_if) - send_pkts = self.gen_encrypt_pkts6(p.scapy_tun_sa, self.tun_if, - src=p.remote_tun_if_host, + config_tun_params(p_in, self.encryption_type, self.tun_if) + config_tun_params(p_out, self.encryption_type, self.tun_if) + send_pkts = self.gen_encrypt_pkts6(p_in.scapy_tun_sa, self.tun_if, + src=p_in.remote_tun_if_host, dst=self.pg1.remote_ip6, count=count) recv_pkts = self.send_and_expect(self.tun_if, send_pkts, self.pg1) - for recv_pkt in recv_pkts: - self.assert_equal(recv_pkt[IPv6].src, p.remote_tun_if_host) - self.assert_equal(recv_pkt[IPv6].dst, self.pg1.remote_ip6) - self.assert_packet_checksums_valid(recv_pkt) + self.verify_decrypted6(p_in, recv_pkts) + send_pkts = self.gen_pkts6(self.pg1, src=self.pg1.remote_ip6, - dst=p.remote_tun_if_host, - count=count) - recv_pkts = self.send_and_expect(self.pg1, send_pkts, self.tun_if) - for recv_pkt in recv_pkts: - self.assertEqual(len(recv_pkt) - len(Ether()) - len(IPv6()), - recv_pkt[IPv6].plen) - try: - decrypt_pkt = p.vpp_tun_sa.decrypt(recv_pkt[IPv6]) - if not decrypt_pkt.haslayer(IPv6): - decrypt_pkt = IPv6(decrypt_pkt[Raw].load) - self.assert_equal(decrypt_pkt.src, self.pg1.remote_ip6) - self.assert_equal(decrypt_pkt.dst, p.remote_tun_if_host) - self.assert_packet_checksums_valid(decrypt_pkt) - except: - self.logger.debug(ppp("Unexpected packet:", recv_pkt)) - try: - self.logger.debug( - ppp("Decrypted packet:", decrypt_pkt)) - except: - pass - raise + dst=p_out.remote_tun_if_host, + count=count, + payload_size=payload_size) + recv_pkts = self.send_and_expect(self.pg1, send_pkts, + self.tun_if) + self.verify_encrypted6(p_out, p_out.vpp_tun_sa, recv_pkts) + finally: self.logger.info(self.vapi.ppcli("show error")) self.logger.info(self.vapi.ppcli("show ipsec all")) - self.verify_counters6(p, count) + self.verify_counters6(p_in, p_out, count) def verify_tun_46(self, p, count=1): """ ipsec 4o6 tunnel basic test """ @@ -747,7 +773,7 @@ class IpsecTun6(object): finally: self.logger.info(self.vapi.ppcli("show error")) self.logger.info(self.vapi.ppcli("show ipsec all")) - self.verify_counters6(p, count) + self.verify_counters6(p, p, count) class IpsecTun6Tests(IpsecTun6): diff --git a/test/test_ipsec_esp.py b/test/test_ipsec_esp.py index 8ed80c3d8de..96787716fb4 100644 --- a/test/test_ipsec_esp.py +++ b/test/test_ipsec_esp.py @@ -3,7 +3,7 @@ import unittest from scapy.layers.ipsec import ESP from scapy.layers.inet import UDP -from framework import VppTestRunner +from framework import VppTestRunner, is_skip_aarch64_set, is_platform_aarch64 from template_ipsec import IpsecTra46Tests, IpsecTun46Tests, TemplateIpsec, \ IpsecTcpTests, IpsecTun4Tests, IpsecTra4Tests, config_tra_params, \ IPsecIPv4Params, IPsecIPv6Params, \ @@ -14,6 +14,8 @@ from vpp_ip_route import VppIpRoute, VppRoutePath from vpp_ip import DpoProto from vpp_papi import VppEnum +NUM_PKTS = 67 + class ConfigIpsecESP(TemplateIpsec): encryption_type = ESP @@ -350,6 +352,8 @@ class TestIpsecEspUdp(TemplateIpsecEspUdp, IpsecTra4Tests): pass +@unittest.skipIf(is_skip_aarch64_set and is_platform_aarch64, + "test doesn't work on aarch64") class TestIpsecEspAll(ConfigIpsecESP, IpsecTra4, IpsecTra6, IpsecTun4, IpsecTun6): @@ -470,10 +474,12 @@ class TestIpsecEspAll(ConfigIpsecESP, # An exhautsive 4o6, 6o4 is not necessary # for each algo # - self.verify_tra_basic6(count=17) - self.verify_tra_basic4(count=17) - self.verify_tun_66(self.params[socket.AF_INET6], 17) - self.verify_tun_44(self.params[socket.AF_INET], 17) + self.verify_tra_basic6(count=NUM_PKTS) + self.verify_tra_basic4(count=NUM_PKTS) + self.verify_tun_66(self.params[socket.AF_INET6], + count=NUM_PKTS) + self.verify_tun_44(self.params[socket.AF_INET], + count=NUM_PKTS) # # remove the SPDs, SAs, etc diff --git a/test/test_ipsec_tun_if_esp.py b/test/test_ipsec_tun_if_esp.py index 5ef0bdbb8c6..cc220d5a0a4 100644 --- a/test/test_ipsec_tun_if_esp.py +++ b/test/test_ipsec_tun_if_esp.py @@ -5,13 +5,15 @@ import copy from scapy.layers.ipsec import ESP from scapy.layers.l2 import Ether, Raw, GRE from scapy.layers.inet import IP, UDP +from scapy.layers.inet6 import IPv6 from framework import VppTestRunner, is_skip_aarch64_set, is_platform_aarch64 from template_ipsec import TemplateIpsec, IpsecTun4Tests, IpsecTun6Tests, \ IpsecTun4, IpsecTun6, IpsecTcpTests, config_tun_params -from vpp_ipsec_tun_interface import VppIpsecTunInterface, \ - VppIpsecGRETunInterface +from vpp_ipsec_tun_interface import VppIpsecTunInterface +from vpp_gre_interface import VppGreInterface +from vpp_ipip_tun_interface import VppIpIpTunInterface from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto -from vpp_ipsec import VppIpsecSA +from vpp_ipsec import VppIpsecSA, VppIpsecTunProtect from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort from util import ppp from vpp_papi import VppEnum @@ -58,8 +60,6 @@ class TemplateIpsec4TunIfEsp(TemplateIpsec): r.add_vpp_config() def tearDown(self): - if not self.vpp_dead: - self.vapi.cli("show hardware") super(TemplateIpsec4TunIfEsp, self).tearDown() @@ -131,8 +131,6 @@ class TemplateIpsec6TunIfEsp(TemplateIpsec): r.add_vpp_config() def tearDown(self): - if not self.vpp_dead: - self.vapi.cli("show hardware") super(TemplateIpsec6TunIfEsp, self).tearDown() @@ -198,8 +196,6 @@ class TestIpsec4MultiTunIfEsp(TemplateIpsec, IpsecTun4): 0xffffffff)]).add_vpp_config() def tearDown(self): - if not self.vpp_dead: - self.vapi.cli("show hardware") super(TestIpsec4MultiTunIfEsp, self).tearDown() def test_tun_44(self): @@ -442,8 +438,6 @@ class TestIpsec6MultiTunIfEsp(TemplateIpsec, IpsecTun6): r.add_vpp_config() def tearDown(self): - if not self.vpp_dead: - self.vapi.cli("show hardware") super(TestIpsec6MultiTunIfEsp, self).tearDown() def test_tun_66(self): @@ -456,9 +450,13 @@ class TestIpsec6MultiTunIfEsp(TemplateIpsec, IpsecTun6): self.assertEqual(c['packets'], 127) -class TemplateIpsecGRETunIfEsp(TemplateIpsec): - """ IPsec GRE tunnel interface tests """ - +@unittest.skipIf(is_skip_aarch64_set and is_platform_aarch64, + "test doesn't work on aarch64") +class TestIpsecGreTebIfEsp(TemplateIpsec, + IpsecTun4Tests): + """ Ipsec GRE TEB ESP - TUN tests """ + tun4_encrypt_node_name = "esp4-encrypt-tun" + tun4_decrypt_node_name = "esp4-decrypt-tun" encryption_type = ESP omac = "00:11:22:33:44:55" @@ -509,7 +507,7 @@ class TemplateIpsecGRETunIfEsp(TemplateIpsec): raise def setUp(self): - super(TemplateIpsecGRETunIfEsp, self).setUp() + super(TestIpsecGreTebIfEsp, self).setUp() self.tun_if = self.pg0 @@ -534,33 +532,634 @@ class TemplateIpsecGRETunIfEsp(TemplateIpsec): self.pg0.local_ip4) p.tun_sa_in.add_vpp_config() - self.tun = VppIpsecGRETunInterface(self, self.pg0, - p.tun_sa_out.id, - p.tun_sa_in.id) - + self.tun = VppGreInterface(self, + self.pg0.local_ip4, + self.pg0.remote_ip4, + type=(VppEnum.vl_api_gre_tunnel_type_t. + GRE_API_TUNNEL_TYPE_TEB)) self.tun.add_vpp_config() + + p.tun_protect = VppIpsecTunProtect(self, + self.tun, + p.tun_sa_out, + [p.tun_sa_in]) + + p.tun_protect.add_vpp_config() + self.tun.admin_up() self.tun.config_ip4() - VppIpRoute(self, p.remote_tun_if_host, 32, - [VppRoutePath(self.tun.remote_ip4, - 0xffffffff)]).add_vpp_config() VppBridgeDomainPort(self, bd1, self.tun).add_vpp_config() VppBridgeDomainPort(self, bd1, self.pg1).add_vpp_config() + self.vapi.cli("clear ipsec sa") + def tearDown(self): - if not self.vpp_dead: - self.vapi.cli("show hardware") self.tun.unconfig_ip4() - super(TemplateIpsecGRETunIfEsp, self).tearDown() + super(TestIpsecGreTebIfEsp, self).tearDown() -@unittest.skipIf(is_skip_aarch64_set and is_platform_aarch64, - "test doesn't work on aarch64") -class TestIpsecGRETunIfEsp1(TemplateIpsecGRETunIfEsp, IpsecTun4Tests): +class TestIpsecGreIfEsp(TemplateIpsec, + IpsecTun4Tests): """ Ipsec GRE ESP - TUN tests """ - tun4_encrypt_node_name = "esp4-encrypt" - tun4_decrypt_node_name = "esp4-decrypt" + tun4_encrypt_node_name = "esp4-encrypt-tun" + tun4_decrypt_node_name = "esp4-decrypt-tun" + encryption_type = ESP + + def gen_encrypt_pkts(self, sa, sw_intf, src, dst, count=1, + payload_size=100): + return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / + sa.encrypt(IP(src=self.pg0.remote_ip4, + dst=self.pg0.local_ip4) / + GRE() / + IP(src=self.pg1.local_ip4, + dst=self.pg1.remote_ip4) / + UDP(sport=1144, dport=2233) / + Raw('X' * payload_size)) + for i in range(count)] + + def gen_pkts(self, sw_intf, src, dst, count=1, + payload_size=100): + return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / + IP(src="1.1.1.1", dst="1.1.1.2") / + UDP(sport=1144, dport=2233) / + Raw('X' * payload_size) + for i in range(count)] + + def verify_decrypted(self, p, rxs): + for rx in rxs: + self.assert_equal(rx[Ether].dst, self.pg1.remote_mac) + self.assert_equal(rx[IP].dst, self.pg1.remote_ip4) + + def verify_encrypted(self, p, sa, rxs): + for rx in rxs: + try: + pkt = sa.decrypt(rx[IP]) + if not pkt.haslayer(IP): + pkt = IP(pkt[Raw].load) + self.assert_packet_checksums_valid(pkt) + self.assert_equal(pkt[IP].dst, self.pg0.remote_ip4) + self.assert_equal(pkt[IP].src, self.pg0.local_ip4) + self.assertTrue(pkt.haslayer(GRE)) + e = pkt[GRE] + self.assertEqual(e[IP].dst, "1.1.1.2") + except (IndexError, AssertionError): + self.logger.debug(ppp("Unexpected packet:", rx)) + try: + self.logger.debug(ppp("Decrypted packet:", pkt)) + except: + pass + raise + + def setUp(self): + super(TestIpsecGreIfEsp, self).setUp() + + self.tun_if = self.pg0 + + p = self.ipv4_params + + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + + p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi, + p.auth_algo_vpp_id, p.auth_key, + p.crypt_algo_vpp_id, p.crypt_key, + self.vpp_esp_protocol, + self.pg0.local_ip4, + self.pg0.remote_ip4) + p.tun_sa_out.add_vpp_config() + + p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi, + p.auth_algo_vpp_id, p.auth_key, + p.crypt_algo_vpp_id, p.crypt_key, + self.vpp_esp_protocol, + self.pg0.remote_ip4, + self.pg0.local_ip4) + p.tun_sa_in.add_vpp_config() + + self.tun = VppGreInterface(self, + self.pg0.local_ip4, + self.pg0.remote_ip4) + self.tun.add_vpp_config() + + p.tun_protect = VppIpsecTunProtect(self, + self.tun, + p.tun_sa_out, + [p.tun_sa_in]) + p.tun_protect.add_vpp_config() + + self.tun.admin_up() + self.tun.config_ip4() + + VppIpRoute(self, "1.1.1.2", 32, + [VppRoutePath(self.tun.remote_ip4, + 0xffffffff)]).add_vpp_config() + + def tearDown(self): + self.tun.unconfig_ip4() + super(TestIpsecGreIfEsp, self).tearDown() + + +class TemplateIpsec4TunProtect(object): + """ IPsec IPv4 Tunnel protect """ + + def config_sa_tra(self, p): + config_tun_params(p, self.encryption_type, self.tun_if) + + p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi, + p.auth_algo_vpp_id, p.auth_key, + p.crypt_algo_vpp_id, p.crypt_key, + self.vpp_esp_protocol) + p.tun_sa_out.add_vpp_config() + + p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi, + p.auth_algo_vpp_id, p.auth_key, + p.crypt_algo_vpp_id, p.crypt_key, + self.vpp_esp_protocol) + p.tun_sa_in.add_vpp_config() + + def config_sa_tun(self, p): + config_tun_params(p, self.encryption_type, self.tun_if) + + p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi, + p.auth_algo_vpp_id, p.auth_key, + p.crypt_algo_vpp_id, p.crypt_key, + self.vpp_esp_protocol, + self.tun_if.remote_addr[p.addr_type], + self.tun_if.local_addr[p.addr_type]) + p.tun_sa_out.add_vpp_config() + + p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi, + p.auth_algo_vpp_id, p.auth_key, + p.crypt_algo_vpp_id, p.crypt_key, + self.vpp_esp_protocol, + self.tun_if.remote_addr[p.addr_type], + self.tun_if.local_addr[p.addr_type]) + p.tun_sa_in.add_vpp_config() + + def config_protect(self, p): + p.tun_protect = VppIpsecTunProtect(self, + p.tun_if, + p.tun_sa_out, + [p.tun_sa_in]) + p.tun_protect.add_vpp_config() + + def config_network(self, p): + p.tun_if = VppIpIpTunInterface(self, self.pg0, + self.pg0.local_ip4, + self.pg0.remote_ip4) + p.tun_if.add_vpp_config() + p.tun_if.admin_up() + p.tun_if.config_ip4() + + p.route = VppIpRoute(self, p.remote_tun_if_host, 32, + [VppRoutePath(p.tun_if.remote_ip4, + 0xffffffff)]) + p.route.add_vpp_config() + + def unconfig_network(self, p): + p.route.remove_vpp_config() + p.tun_if.remove_vpp_config() + + def unconfig_protect(self, p): + p.tun_protect.remove_vpp_config() + + def unconfig_sa(self, p): + p.tun_sa_out.remove_vpp_config() + p.tun_sa_in.remove_vpp_config() + + +class TestIpsec4TunProtect(TemplateIpsec, + TemplateIpsec4TunProtect, + IpsecTun4): + """ IPsec IPv4 Tunnel protect - transport mode""" + + encryption_type = ESP + tun4_encrypt_node_name = "esp4-encrypt-tun" + tun4_decrypt_node_name = "esp4-decrypt-tun" + + def setUp(self): + super(TestIpsec4TunProtect, self).setUp() + + self.tun_if = self.pg0 + + def tearDown(self): + super(TestIpsec4TunProtect, self).tearDown() + + def test_tun_44(self): + """IPSEC tunnel protect""" + + p = self.ipv4_params + + self.config_network(p) + self.config_sa_tra(p) + self.config_protect(p) + + self.verify_tun_44(p, count=127) + c = p.tun_if.get_rx_stats() + self.assertEqual(c['packets'], 127) + c = p.tun_if.get_tx_stats() + self.assertEqual(c['packets'], 127) + + # rekey - create new SAs and update the tunnel protection + np = copy.copy(p) + np.crypt_key = 'X' + p.crypt_key[1:] + np.scapy_tun_spi += 100 + np.scapy_tun_sa_id += 1 + np.vpp_tun_spi += 100 + np.vpp_tun_sa_id += 1 + np.tun_if.local_spi = p.vpp_tun_spi + np.tun_if.remote_spi = p.scapy_tun_spi + + self.config_sa_tra(np) + self.config_protect(np) + self.unconfig_sa(p) + + self.verify_tun_44(np, count=127) + c = p.tun_if.get_rx_stats() + self.assertEqual(c['packets'], 254) + c = p.tun_if.get_tx_stats() + self.assertEqual(c['packets'], 254) + + # teardown + self.unconfig_protect(np) + self.unconfig_sa(np) + self.unconfig_network(p) + + +class TestIpsec4TunProtectTun(TemplateIpsec, + TemplateIpsec4TunProtect, + IpsecTun4): + """ IPsec IPv4 Tunnel protect - tunnel mode""" + + encryption_type = ESP + tun4_encrypt_node_name = "esp4-encrypt-tun" + tun4_decrypt_node_name = "esp4-decrypt-tun" + + def setUp(self): + super(TestIpsec4TunProtectTun, self).setUp() + + self.tun_if = self.pg0 + + def tearDown(self): + super(TestIpsec4TunProtectTun, self).tearDown() + + def gen_encrypt_pkts(self, sa, sw_intf, src, dst, count=1, + payload_size=100): + return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / + sa.encrypt(IP(src=sw_intf.remote_ip4, + dst=sw_intf.local_ip4) / + IP(src=src, dst=dst) / + UDP(sport=1144, dport=2233) / + Raw('X' * payload_size)) + for i in range(count)] + + def gen_pkts(self, sw_intf, src, dst, count=1, + payload_size=100): + return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / + IP(src=src, dst=dst) / + UDP(sport=1144, dport=2233) / + Raw('X' * payload_size) + for i in range(count)] + + def verify_decrypted(self, p, rxs): + for rx in rxs: + self.assert_equal(rx[IP].dst, self.pg1.remote_ip4) + self.assert_equal(rx[IP].src, p.remote_tun_if_host) + self.assert_packet_checksums_valid(rx) + + def verify_encrypted(self, p, sa, rxs): + for rx in rxs: + try: + pkt = sa.decrypt(rx[IP]) + if not pkt.haslayer(IP): + pkt = IP(pkt[Raw].load) + self.assert_packet_checksums_valid(pkt) + self.assert_equal(pkt[IP].dst, self.pg0.remote_ip4) + self.assert_equal(pkt[IP].src, self.pg0.local_ip4) + inner = pkt[IP].payload + self.assertEqual(inner[IP][IP].dst, p.remote_tun_if_host) + + except (IndexError, AssertionError): + self.logger.debug(ppp("Unexpected packet:", rx)) + try: + self.logger.debug(ppp("Decrypted packet:", pkt)) + except: + pass + raise + + def test_tun_44(self): + """IPSEC tunnel protect """ + + p = self.ipv4_params + + self.config_network(p) + self.config_sa_tun(p) + self.config_protect(p) + + self.verify_tun_44(p, count=127) + + c = p.tun_if.get_rx_stats() + self.assertEqual(c['packets'], 127) + c = p.tun_if.get_tx_stats() + self.assertEqual(c['packets'], 127) + + # rekey - create new SAs and update the tunnel protection + np = copy.copy(p) + np.crypt_key = 'X' + p.crypt_key[1:] + np.scapy_tun_spi += 100 + np.scapy_tun_sa_id += 1 + np.vpp_tun_spi += 100 + np.vpp_tun_sa_id += 1 + np.tun_if.local_spi = p.vpp_tun_spi + np.tun_if.remote_spi = p.scapy_tun_spi + + self.config_sa_tun(np) + self.config_protect(np) + self.unconfig_sa(p) + + self.verify_tun_44(np, count=127) + c = p.tun_if.get_rx_stats() + self.assertEqual(c['packets'], 254) + c = p.tun_if.get_tx_stats() + self.assertEqual(c['packets'], 254) + + # teardown + self.unconfig_protect(np) + self.unconfig_sa(np) + self.unconfig_network(p) + + +class TemplateIpsec6TunProtect(object): + """ IPsec IPv6 Tunnel protect """ + + def config_sa_tra(self, p): + config_tun_params(p, self.encryption_type, self.tun_if) + + p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi, + p.auth_algo_vpp_id, p.auth_key, + p.crypt_algo_vpp_id, p.crypt_key, + self.vpp_esp_protocol) + p.tun_sa_out.add_vpp_config() + + p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi, + p.auth_algo_vpp_id, p.auth_key, + p.crypt_algo_vpp_id, p.crypt_key, + self.vpp_esp_protocol) + p.tun_sa_in.add_vpp_config() + + def config_sa_tun(self, p): + config_tun_params(p, self.encryption_type, self.tun_if) + + p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi, + p.auth_algo_vpp_id, p.auth_key, + p.crypt_algo_vpp_id, p.crypt_key, + self.vpp_esp_protocol, + self.tun_if.remote_addr[p.addr_type], + self.tun_if.local_addr[p.addr_type]) + p.tun_sa_out.add_vpp_config() + + p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi, + p.auth_algo_vpp_id, p.auth_key, + p.crypt_algo_vpp_id, p.crypt_key, + self.vpp_esp_protocol, + self.tun_if.remote_addr[p.addr_type], + self.tun_if.local_addr[p.addr_type]) + p.tun_sa_in.add_vpp_config() + + def config_protect(self, p): + p.tun_protect = VppIpsecTunProtect(self, + p.tun_if, + p.tun_sa_out, + [p.tun_sa_in]) + p.tun_protect.add_vpp_config() + + def config_network(self, p): + p.tun_if = VppIpIpTunInterface(self, self.pg0, + self.pg0.local_ip6, + self.pg0.remote_ip6) + p.tun_if.add_vpp_config() + p.tun_if.admin_up() + p.tun_if.config_ip6() + + p.route = VppIpRoute(self, p.remote_tun_if_host, 128, + [VppRoutePath(p.tun_if.remote_ip6, + 0xffffffff, + proto=DpoProto.DPO_PROTO_IP6)], + is_ip6=1) + p.route.add_vpp_config() + + def unconfig_network(self, p): + p.route.remove_vpp_config() + p.tun_if.remove_vpp_config() + + def unconfig_protect(self, p): + p.tun_protect.remove_vpp_config() + + def unconfig_sa(self, p): + p.tun_sa_out.remove_vpp_config() + p.tun_sa_in.remove_vpp_config() + + +class TestIpsec6TunProtect(TemplateIpsec, + TemplateIpsec6TunProtect, + IpsecTun6): + """ IPsec IPv6 Tunnel protect - transport mode""" + + encryption_type = ESP + tun6_encrypt_node_name = "esp6-encrypt-tun" + tun6_decrypt_node_name = "esp6-decrypt-tun" + + def setUp(self): + super(TestIpsec6TunProtect, self).setUp() + + self.tun_if = self.pg0 + + def tearDown(self): + super(TestIpsec6TunProtect, self).tearDown() + + def test_tun_66(self): + """IPSEC tunnel protect""" + + p = self.ipv6_params + + self.config_network(p) + self.config_sa_tra(p) + self.config_protect(p) + + self.verify_tun_66(p, count=127) + c = p.tun_if.get_rx_stats() + self.assertEqual(c['packets'], 127) + c = p.tun_if.get_tx_stats() + self.assertEqual(c['packets'], 127) + + # rekey - create new SAs and update the tunnel protection + np = copy.copy(p) + np.crypt_key = 'X' + p.crypt_key[1:] + np.scapy_tun_spi += 100 + np.scapy_tun_sa_id += 1 + np.vpp_tun_spi += 100 + np.vpp_tun_sa_id += 1 + np.tun_if.local_spi = p.vpp_tun_spi + np.tun_if.remote_spi = p.scapy_tun_spi + + self.config_sa_tra(np) + self.config_protect(np) + self.unconfig_sa(p) + + self.verify_tun_66(np, count=127) + c = p.tun_if.get_rx_stats() + self.assertEqual(c['packets'], 254) + c = p.tun_if.get_tx_stats() + self.assertEqual(c['packets'], 254) + + # 3 phase rekey + # 1) add two input SAs [old, new] + # 2) swap output SA to [new] + # 3) use only [new] input SA + np3 = copy.copy(np) + np3.crypt_key = 'Z' + p.crypt_key[1:] + np3.scapy_tun_spi += 100 + np3.scapy_tun_sa_id += 1 + np3.vpp_tun_spi += 100 + np3.vpp_tun_sa_id += 1 + np3.tun_if.local_spi = p.vpp_tun_spi + np3.tun_if.remote_spi = p.scapy_tun_spi + + self.config_sa_tra(np3) + + # step 1; + p.tun_protect.update_vpp_config(np.tun_sa_out, + [np.tun_sa_in, np3.tun_sa_in]) + self.verify_tun_66(np, np, count=127) + self.verify_tun_66(np3, np, count=127) + + # step 2; + p.tun_protect.update_vpp_config(np3.tun_sa_out, + [np.tun_sa_in, np3.tun_sa_in]) + self.verify_tun_66(np, np3, count=127) + self.verify_tun_66(np3, np3, count=127) + + # step 1; + p.tun_protect.update_vpp_config(np3.tun_sa_out, + [np3.tun_sa_in]) + self.verify_tun_66(np3, np3, count=127) + self.verify_drop_tun_66(np, count=127) + + c = p.tun_if.get_rx_stats() + self.assertEqual(c['packets'], 127*7) + c = p.tun_if.get_tx_stats() + self.assertEqual(c['packets'], 127*7) + self.unconfig_sa(np) + + # teardown + self.unconfig_protect(np3) + self.unconfig_sa(np3) + self.unconfig_network(p) + + +class TestIpsec6TunProtectTun(TemplateIpsec, + TemplateIpsec6TunProtect, + IpsecTun6): + """ IPsec IPv6 Tunnel protect - tunnel mode""" + + encryption_type = ESP + tun6_encrypt_node_name = "esp6-encrypt-tun" + tun6_decrypt_node_name = "esp6-decrypt-tun" + + def setUp(self): + super(TestIpsec6TunProtectTun, self).setUp() + + self.tun_if = self.pg0 + + def tearDown(self): + super(TestIpsec6TunProtectTun, self).tearDown() + + def gen_encrypt_pkts6(self, sa, sw_intf, src, dst, count=1, + payload_size=100): + return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / + sa.encrypt(IPv6(src=sw_intf.remote_ip6, + dst=sw_intf.local_ip6) / + IPv6(src=src, dst=dst) / + UDP(sport=1166, dport=2233) / + Raw('X' * payload_size)) + for i in range(count)] + + def gen_pkts6(self, sw_intf, src, dst, count=1, + payload_size=100): + return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / + IPv6(src=src, dst=dst) / + UDP(sport=1166, dport=2233) / + Raw('X' * payload_size) + for i in range(count)] + + def verify_decrypted6(self, p, rxs): + for rx in rxs: + self.assert_equal(rx[IPv6].dst, self.pg1.remote_ip6) + self.assert_equal(rx[IPv6].src, p.remote_tun_if_host) + self.assert_packet_checksums_valid(rx) + + def verify_encrypted6(self, p, sa, rxs): + for rx in rxs: + try: + pkt = sa.decrypt(rx[IPv6]) + if not pkt.haslayer(IPv6): + pkt = IPv6(pkt[Raw].load) + self.assert_packet_checksums_valid(pkt) + self.assert_equal(pkt[IPv6].dst, self.pg0.remote_ip6) + self.assert_equal(pkt[IPv6].src, self.pg0.local_ip6) + inner = pkt[IPv6].payload + self.assertEqual(inner[IPv6][IPv6].dst, p.remote_tun_if_host) + + except (IndexError, AssertionError): + self.logger.debug(ppp("Unexpected packet:", rx)) + try: + self.logger.debug(ppp("Decrypted packet:", pkt)) + except: + pass + raise + + def test_tun_66(self): + """IPSEC tunnel protect """ + + p = self.ipv6_params + + self.config_network(p) + self.config_sa_tun(p) + self.config_protect(p) + + self.verify_tun_66(p, count=127) + + c = p.tun_if.get_rx_stats() + self.assertEqual(c['packets'], 127) + c = p.tun_if.get_tx_stats() + self.assertEqual(c['packets'], 127) + + # rekey - create new SAs and update the tunnel protection + np = copy.copy(p) + np.crypt_key = 'X' + p.crypt_key[1:] + np.scapy_tun_spi += 100 + np.scapy_tun_sa_id += 1 + np.vpp_tun_spi += 100 + np.vpp_tun_sa_id += 1 + np.tun_if.local_spi = p.vpp_tun_spi + np.tun_if.remote_spi = p.scapy_tun_spi + + self.config_sa_tun(np) + self.config_protect(np) + self.unconfig_sa(p) + + self.verify_tun_66(np, count=127) + c = p.tun_if.get_rx_stats() + self.assertEqual(c['packets'], 254) + c = p.tun_if.get_tx_stats() + self.assertEqual(c['packets'], 254) + + # teardown + self.unconfig_protect(np) + self.unconfig_sa(np) + self.unconfig_network(p) + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_ipip_tun_interface.py b/test/vpp_ipip_tun_interface.py new file mode 100644 index 00000000000..6e5ade6eb3d --- /dev/null +++ b/test/vpp_ipip_tun_interface.py @@ -0,0 +1,40 @@ +from vpp_tunnel_interface import VppTunnelInterface +from ipaddress import ip_address + + +class VppIpIpTunInterface(VppTunnelInterface): + """ + VPP IP-IP Tunnel interface + """ + + def __init__(self, test, parent_if, src, dst): + super(VppIpIpTunInterface, self).__init__(test, parent_if) + self.src = src + self.dst = dst + + def add_vpp_config(self): + r = self.test.vapi.ipip_add_tunnel( + tunnel={ + 'src': self.src, + 'dst': self.dst, + 'table_id': 0, + 'instance': 0xffffffff, + }) + self.set_sw_if_index(r.sw_if_index) + self.test.registry.register(self, self.test.logger) + + def remove_vpp_config(self): + self.test.vapi.ipip_del_tunnel(sw_if_index=self._sw_if_index) + + def query_vpp_config(self): + ts = self.test.vapi.ipip_tunnel_dump(sw_if_index=0xffffffff) + for t in ts: + if t.tunnel.sw_if_index == self._sw_if_index: + return True + return False + + def __str__(self): + return self.object_id() + + def object_id(self): + return "ipip-%d" % self._sw_if_index diff --git a/test/vpp_ipsec.py b/test/vpp_ipsec.py index 77a9d74edf3..0df8cb2a88a 100644 --- a/test/vpp_ipsec.py +++ b/test/vpp_ipsec.py @@ -247,3 +247,53 @@ class VppIpsecSA(VppObject): def get_stats(self): c = self.test.statistics.get_counter("/net/ipsec/sa") return c[0][self.stat_index] + + +class VppIpsecTunProtect(VppObject): + """ + VPP IPSEC tunnel protection + """ + + def __init__(self, test, itf, sa_out, sas_in): + self.test = test + self.itf = itf + self.sas_in = [] + for sa in sas_in: + self.sas_in.append(sa.id) + self.sa_out = sa_out.id + + def update_vpp_config(self, sa_out, sas_in): + self.sas_in = [] + for sa in sas_in: + self.sas_in.append(sa.id) + self.sa_out = sa_out.id + self.test.vapi.ipsec_tunnel_protect_update( + tunnel={ + 'sw_if_index': self.itf._sw_if_index, + 'n_sa_in': len(self.sas_in), + 'sa_out': self.sa_out, + 'sa_in': self.sas_in}) + + def object_id(self): + return "ipsec-tun-protect-%s" % self.itf + + def add_vpp_config(self): + self.test.vapi.ipsec_tunnel_protect_update( + tunnel={ + 'sw_if_index': self.itf._sw_if_index, + 'n_sa_in': len(self.sas_in), + 'sa_out': self.sa_out, + 'sa_in': self.sas_in}) + self.test.registry.register(self, self.test.logger) + + def remove_vpp_config(self): + self.test.vapi.ipsec_tunnel_protect_del( + sw_if_index=self.itf.sw_if_index) + + def query_vpp_config(self): + bs = self.test.vapi.ipsec_tunnel_protect_dump( + sw_if_index=self.itf.sw_if_index) + for b in bs: + if b.tun.sw_if_index == self.itf.sw_if_index: + return True + return False diff --git a/test/vpp_ipsec_tun_interface.py b/test/vpp_ipsec_tun_interface.py index 223ea4df154..97359b13281 100644 --- a/test/vpp_ipsec_tun_interface.py +++ b/test/vpp_ipsec_tun_interface.py @@ -51,48 +51,3 @@ class VppIpsecTunInterface(VppTunnelInterface): def object_id(self): return "ipsec-tun-if-%d" % self._sw_if_index - - -class VppIpsecGRETunInterface(VppTunnelInterface): - """ - VPP IPsec GRE Tunnel interface - this creates headers - IP / ESP / IP / GRE / payload - i.e. it's GRE over IPSEC, rather than IPSEC over GRE. - """ - - def __init__(self, test, parent_if, sa_out, sa_in): - super(VppIpsecGRETunInterface, self).__init__(test, parent_if) - self.sa_in = sa_in - self.sa_out = sa_out - - def add_vpp_config(self): - r = self.test.vapi.ipsec_gre_tunnel_add_del( - self.parent_if.local_ip4n, - self.parent_if.remote_ip4n, - self.sa_out, - self.sa_in) - self.set_sw_if_index(r.sw_if_index) - self.generate_remote_hosts() - self.test.registry.register(self, self.test.logger) - - def remove_vpp_config(self): - self.test.vapi.ipsec_gre_tunnel_add_del( - self.parent_if.local_ip4n, - self.parent_if.remote_ip4n, - self.sa_out, - self.sa_in, - is_add=0) - - def query_vpp_config(self): - ts = self.test.vapi.ipsec_gre_tunnel_dump(sw_if_index=0xffffffff) - for t in ts: - if t.tunnel.sw_if_index == self._sw_if_index: - return True - return False - - def __str__(self): - return self.object_id() - - def object_id(self): - return "ipsec-gre-tun-if-%d" % self._sw_if_index diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 038a3718dde..75ec57f8221 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -1968,19 +1968,6 @@ class VppPapiProvider(object): 'salt': salt }) - def ipsec_gre_tunnel_add_del(self, local_ip, remote_ip, - sa_out, sa_in, is_add=1): - return self.api(self.papi.ipsec_gre_tunnel_add_del, - { - 'is_add': is_add, - 'tunnel': { - 'src': local_ip, - 'dst': remote_ip, - 'local_sa_id': sa_out, - 'remote_sa_id': sa_in - } - }) - def ipsec_select_backend(self, protocol, index): return self.api(self.papi.ipsec_select_backend, {'protocol': protocol, 'index': index})