From 1ba5bc8d888d0164dd6e38857cbed09ab7ce1d8b Mon Sep 17 00:00:00 2001 From: Kingwel Xie Date: Wed, 20 Mar 2019 07:21:58 -0400 Subject: [PATCH] ipsec: add ipv6 support for ipsec tunnel interface Change-Id: I6a76907dc7bed2a81282b63669bea2219d6903c9 Signed-off-by: Kingwel Xie Signed-off-by: Neale Ranns --- src/vat/api_format.c | 14 +- src/vnet/ipsec/ipsec.api | 5 +- src/vnet/ipsec/ipsec.h | 6 +- src/vnet/ipsec/ipsec_api.c | 6 +- src/vnet/ipsec/ipsec_cli.c | 5 +- src/vnet/ipsec/ipsec_if.c | 180 ++++++++++++++++------ src/vnet/ipsec/ipsec_if.h | 30 ++++ src/vnet/ipsec/ipsec_if_in.c | 330 +++++++++++++++++++++++++++++----------- test/template_ipsec.py | 17 ++- test/test_ipsec_tun_if_esp.py | 53 ++++++- test/vpp_ipsec_tun_interface.py | 25 +-- test/vpp_papi_provider.py | 35 +++-- 12 files changed, 515 insertions(+), 191 deletions(-) diff --git a/src/vat/api_format.c b/src/vat/api_format.c index cef60e05725..46974d2d299 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -15028,8 +15028,8 @@ api_ipsec_tunnel_if_add_del (vat_main_t * vam) u32 crypto_alg = 0, integ_alg = 0; u8 *lck = NULL, *rck = NULL; u8 *lik = NULL, *rik = NULL; - ip4_address_t local_ip = { {0} }; - ip4_address_t remote_ip = { {0} }; + vl_api_address_t local_ip = { 0 }; + vl_api_address_t remote_ip = { 0 }; u8 is_add = 1; u8 esn = 0; u8 anti_replay = 0; @@ -15049,9 +15049,11 @@ api_ipsec_tunnel_if_add_del (vat_main_t * vam) ; else if (unformat (i, "remote_spi %d", &remote_spi)) ; - else if (unformat (i, "local_ip %U", unformat_ip4_address, &local_ip)) + else + if (unformat (i, "local_ip %U", unformat_vl_api_address, &local_ip)) ; - else if (unformat (i, "remote_ip %U", unformat_ip4_address, &remote_ip)) + else + if (unformat (i, "remote_ip %U", unformat_vl_api_address, &remote_ip)) ; else if (unformat (i, "local_crypto_key %U", unformat_hex_string, &lck)) ; @@ -15099,8 +15101,8 @@ api_ipsec_tunnel_if_add_del (vat_main_t * vam) mp->esn = esn; mp->anti_replay = anti_replay; - clib_memcpy (mp->local_ip, &local_ip, sizeof (ip4_address_t)); - clib_memcpy (mp->remote_ip, &remote_ip, sizeof (ip4_address_t)); + clib_memcpy (&mp->local_ip, &local_ip, sizeof (local_ip)); + clib_memcpy (&mp->remote_ip, &remote_ip, sizeof (remote_ip)); mp->local_spi = htonl (local_spi); mp->remote_spi = htonl (remote_spi); diff --git a/src/vnet/ipsec/ipsec.api b/src/vnet/ipsec/ipsec.api index 4e5337589a5..e6e1ce3667b 100644 --- a/src/vnet/ipsec/ipsec.api +++ b/src/vnet/ipsec/ipsec.api @@ -353,6 +353,7 @@ define ipsec_spd_interface_details { @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @param is_add - add IPsec tunnel interface if nonzero, else delete + @param is_ip6 - tunnel v6 or v4 @param esn - enable extended sequence numbers if nonzero, else disable @param anti_replay - enable anti replay check if nonzero, else disable @param local_ip - local IP address @@ -380,8 +381,8 @@ define ipsec_tunnel_if_add_del { u8 is_add; u8 esn; u8 anti_replay; - u8 local_ip[4]; - u8 remote_ip[4]; + vl_api_address_t local_ip; + vl_api_address_t remote_ip; u32 local_spi; u32 remote_spi; u8 crypto_alg; diff --git a/src/vnet/ipsec/ipsec.h b/src/vnet/ipsec/ipsec.h index e38a4a8771e..cfb096f69ba 100644 --- a/src/vnet/ipsec/ipsec.h +++ b/src/vnet/ipsec/ipsec.h @@ -100,7 +100,8 @@ typedef struct uword *spd_index_by_spd_id; uword *spd_index_by_sw_if_index; uword *sa_index_by_sa_id; - uword *ipsec_if_pool_index_by_key; + uword *ipsec4_if_pool_index_by_key; + uword *ipsec6_if_pool_index_by_key; uword *ipsec_if_real_dev_by_show_dev; /* node indices */ @@ -158,7 +159,8 @@ extern vlib_node_registration_t esp6_encrypt_node; extern vlib_node_registration_t esp6_decrypt_node; extern vlib_node_registration_t ah6_encrypt_node; extern vlib_node_registration_t ah6_decrypt_node; -extern vlib_node_registration_t ipsec_if_input_node; +extern vlib_node_registration_t ipsec4_if_input_node; +extern vlib_node_registration_t ipsec6_if_input_node; /* * functions diff --git a/src/vnet/ipsec/ipsec_api.c b/src/vnet/ipsec/ipsec_api.c index e6f5bd31428..4bb3a75b936 100644 --- a/src/vnet/ipsec/ipsec_api.c +++ b/src/vnet/ipsec/ipsec_api.c @@ -616,6 +616,7 @@ vl_api_ipsec_tunnel_if_add_del_t_handler (vl_api_ipsec_tunnel_if_add_del_t * ipsec_main_t *im = &ipsec_main; vnet_main_t *vnm = im->vnet_main; u32 sw_if_index = ~0; + ip46_type_t itype; int rv; #if WITH_LIBSSL > 0 @@ -636,8 +637,9 @@ vl_api_ipsec_tunnel_if_add_del_t_handler (vl_api_ipsec_tunnel_if_add_del_t * tun.remote_integ_key_len = mp->remote_integ_key_len; tun.udp_encap = mp->udp_encap; tun.tx_table_id = ntohl (mp->tx_table_id); - memcpy (&tun.local_ip.ip4, mp->local_ip, 4); - memcpy (&tun.remote_ip.ip4, mp->remote_ip, 4); + itype = ip_address_decode (&mp->local_ip, &tun.local_ip); + itype = ip_address_decode (&mp->remote_ip, &tun.remote_ip); + tun.is_ip6 = (IP46_TYPE_IP6 == itype); memcpy (&tun.local_crypto_key, &mp->local_crypto_key, mp->local_crypto_key_len); memcpy (&tun.remote_crypto_key, &mp->remote_crypto_key, diff --git a/src/vnet/ipsec/ipsec_cli.c b/src/vnet/ipsec/ipsec_cli.c index 2020e7909a3..0f47c7b8427 100644 --- a/src/vnet/ipsec/ipsec_cli.c +++ b/src/vnet/ipsec/ipsec_cli.c @@ -721,12 +721,11 @@ create_ipsec_tunnel_command_fn (vlib_main_t * vm, goto done; } - if (ipv6_set) - return clib_error_return (0, "currently only IPv4 supported"); - if (ipv4_set && ipv6_set) return clib_error_return (0, "both IPv4 and IPv6 addresses specified"); + a.is_ip6 = ipv6_set; + clib_memcpy (a.local_crypto_key, lck.data, lck.len); a.local_crypto_key_len = lck.len; clib_memcpy (a.remote_crypto_key, rck.data, rck.len); diff --git a/src/vnet/ipsec/ipsec_if.c b/src/vnet/ipsec/ipsec_if.c index 7a44456efb6..41089b6c2ab 100644 --- a/src/vnet/ipsec/ipsec_if.c +++ b/src/vnet/ipsec/ipsec_if.c @@ -272,14 +272,25 @@ ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm, u32 hw_if_index = ~0; uword *p; u32 dev_instance; - u32 slot; ipsec_key_t crypto_key, integ_key; ipsec_sa_flags_t flags; int rv; + int is_ip6 = args->is_ip6; + ipsec4_tunnel_key_t key4; + ipsec6_tunnel_key_t key6; - u64 key = ((u64) args->remote_ip.ip4.as_u32 << 32 | - (u64) clib_host_to_net_u32 (args->remote_spi)); - p = hash_get (im->ipsec_if_pool_index_by_key, key); + if (!is_ip6) + { + key4.remote_ip = args->remote_ip.ip4.as_u32; + key4.spi = clib_host_to_net_u32 (args->remote_spi); + p = hash_get (im->ipsec4_if_pool_index_by_key, key4.as_u64); + } + else + { + key6.remote_ip = args->remote_ip.ip6; + key6.spi = clib_host_to_net_u32 (args->remote_spi); + p = hash_get_mem (im->ipsec6_if_pool_index_by_key, &key6); + } if (args->is_add) { @@ -305,6 +316,8 @@ ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm, dev_instance); flags = IPSEC_SA_FLAG_IS_TUNNEL; + if (args->is_ip6) + flags |= IPSEC_SA_FLAG_IS_TUNNEL_V6; if (args->udp_encap) flags |= IPSEC_SA_FLAG_UDP_ENCAP; if (args->esn) @@ -352,8 +365,13 @@ ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm, if (rv) return VNET_API_ERROR_UNIMPLEMENTED; - hash_set (im->ipsec_if_pool_index_by_key, key, - t - im->tunnel_interfaces); + /* copy the key */ + if (is_ip6) + hash_set_mem_alloc (&im->ipsec6_if_pool_index_by_key, &key6, + t - im->tunnel_interfaces); + else + hash_set (im->ipsec4_if_pool_index_by_key, key4.as_u64, + t - im->tunnel_interfaces); hw_if_index = vnet_register_interface (vnm, ipsec_device_class.index, t - im->tunnel_interfaces, @@ -361,23 +379,30 @@ ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm, t - im->tunnel_interfaces); hi = vnet_get_hw_interface (vnm, hw_if_index); - /* add esp4 as the next-node-index of this tx-node */ - slot = vlib_node_add_next_with_slot - (vnm->vlib_main, hi->tx_node_index, im->esp4_encrypt_node_index, 0); + /* add esp4/6 as the next-node-index of this tx-node */ + uword slot = vlib_node_add_next_with_slot + (vnm->vlib_main, hi->tx_node_index, + is_ip6 ? im->esp6_encrypt_node_index : im->esp4_encrypt_node_index, + 0); ASSERT (slot == 0); t->hw_if_index = hw_if_index; t->sw_if_index = hi->sw_if_index; - vnet_feature_enable_disable ("interface-output", "ipsec-if-output", - hi->sw_if_index, 1, 0, 0); - + vnet_feature_enable_disable ("interface-output", + args->is_ip6 ? "ipsec6-if-output" : + "ipsec4-if-output", hi->sw_if_index, 1, 0, + 0); /*1st interface, register protocol */ if (pool_elts (im->tunnel_interfaces) == 1) - ip4_register_protocol (IP_PROTOCOL_IPSEC_ESP, - ipsec_if_input_node.index); + { + ip4_register_protocol (IP_PROTOCOL_IPSEC_ESP, + ipsec4_if_input_node.index); + ip6_register_protocol (IP_PROTOCOL_IPSEC_ESP, + ipsec6_if_input_node.index); + } } else @@ -393,12 +418,17 @@ ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm, hi = vnet_get_hw_interface (vnm, t->hw_if_index); vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 0); /* admin down */ - vnet_feature_enable_disable ("interface-output", "ipsec-if-output", - hi->sw_if_index, 0, 0, 0); - + vnet_feature_enable_disable ("interface-output", + args->is_ip6 ? "ipsec6-if-output" : + "ipsec4-if-output", hi->sw_if_index, 0, 0, + 0); vnet_delete_hw_interface (vnm, t->hw_if_index); - hash_unset (im->ipsec_if_pool_index_by_key, key); + if (is_ip6) + hash_unset_mem_free (&im->ipsec6_if_pool_index_by_key, &key6); + else + hash_unset (im->ipsec4_if_pool_index_by_key, key4.as_u64); + hash_unset (im->ipsec_if_real_dev_by_show_dev, t->show_instance); pool_put (im->tunnel_interfaces, t); @@ -422,7 +452,7 @@ ipsec_add_del_ipsec_gre_tunnel (vnet_main_t * vnm, ipsec_main_t *im = &ipsec_main; uword *p; ipsec_sa_t *sa; - u64 key; + ipsec4_tunnel_key_t key; u32 isa, osa; p = hash_get (im->sa_index_by_sa_id, args->local_sa_id); @@ -437,13 +467,17 @@ ipsec_add_del_ipsec_gre_tunnel (vnet_main_t * vnm, sa = pool_elt_at_index (im->sad, p[0]); if (sa->is_tunnel) - key = ((u64) sa->tunnel_dst_addr.ip4.as_u32 << 32 | - (u64) clib_host_to_net_u32 (sa->spi)); + { + key.remote_ip = sa->tunnel_dst_addr.ip4.as_u32; + key.spi = clib_host_to_net_u32 (sa->spi); + } else - key = ((u64) args->remote_ip.as_u32 << 32 | - (u64) clib_host_to_net_u32 (sa->spi)); + { + key.remote_ip = args->remote_ip.as_u32; + key.spi = clib_host_to_net_u32 (sa->spi); + } - p = hash_get (im->ipsec_if_pool_index_by_key, key); + p = hash_get (im->ipsec4_if_pool_index_by_key, key.as_u64); if (args->is_add) { @@ -457,13 +491,20 @@ ipsec_add_del_ipsec_gre_tunnel (vnet_main_t * vnm, t->input_sa_index = isa; t->output_sa_index = osa; t->hw_if_index = ~0; - hash_set (im->ipsec_if_pool_index_by_key, key, + 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, - ipsec_if_input_node.index); + { + 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 { @@ -472,7 +513,7 @@ ipsec_add_del_ipsec_gre_tunnel (vnet_main_t * vnm, return VNET_API_ERROR_INVALID_VALUE; t = pool_elt_at_index (im->tunnel_interfaces, p[0]); - hash_unset (im->ipsec_if_pool_index_by_key, key); + hash_unset (im->ipsec4_if_pool_index_by_key, key.as_u64); pool_put (im->tunnel_interfaces, t); } return 0; @@ -552,36 +593,70 @@ ipsec_set_interface_sa (vnet_main_t * vnm, u32 hw_if_index, u32 sa_id, } sa = pool_elt_at_index (im->sad, sa_index); - if (sa->is_tunnel_ip6) - { - clib_warning ("IPsec interface not supported with IPv6 endpoints"); - return VNET_API_ERROR_UNIMPLEMENTED; - } if (!is_outbound) { - u64 key; - old_sa_index = t->input_sa_index; old_sa = pool_elt_at_index (im->sad, old_sa_index); - /* unset old inbound hash entry. packets should stop arriving */ - key = ((u64) old_sa->tunnel_src_addr.ip4.as_u32 << 32 | - (u64) clib_host_to_net_u32 (old_sa->spi)); - p = hash_get (im->ipsec_if_pool_index_by_key, key); - if (p) - hash_unset (im->ipsec_if_pool_index_by_key, key); + if (sa->is_tunnel_ip6 ^ old_sa->is_tunnel_ip6) + { + clib_warning ("IPsec interface SA endpoints type can't be changed"); + return VNET_API_ERROR_INVALID_VALUE; + } + + if (sa->is_tunnel_ip6) + { + ipsec6_tunnel_key_t key; + + /* unset old inbound hash entry. packets should stop arriving */ + key.remote_ip = old_sa->tunnel_src_addr.ip6; + key.spi = clib_host_to_net_u32 (old_sa->spi); + + p = hash_get_mem (im->ipsec6_if_pool_index_by_key, &key); + if (p) + hash_unset_mem_free (&im->ipsec6_if_pool_index_by_key, &key); + + /* set new inbound SA, then set new hash entry */ + t->input_sa_index = sa_index; + key.remote_ip = sa->tunnel_src_addr.ip6; + key.spi = clib_host_to_net_u32 (sa->spi); + + hash_set_mem_alloc (&im->ipsec6_if_pool_index_by_key, &key, + hi->dev_instance); + } + else + { + ipsec4_tunnel_key_t key; + + /* unset old inbound hash entry. packets should stop arriving */ + key.remote_ip = old_sa->tunnel_src_addr.ip4.as_u32; + key.spi = clib_host_to_net_u32 (old_sa->spi); + + p = hash_get (im->ipsec4_if_pool_index_by_key, key.as_u64); + if (p) + hash_unset (im->ipsec4_if_pool_index_by_key, key.as_u64); - /* set new inbound SA, then set new hash entry */ - t->input_sa_index = sa_index; - key = ((u64) sa->tunnel_src_addr.ip4.as_u32 << 32 | - (u64) clib_host_to_net_u32 (sa->spi)); - hash_set (im->ipsec_if_pool_index_by_key, key, hi->dev_instance); + /* set new inbound SA, then set new hash entry */ + t->input_sa_index = sa_index; + key.remote_ip = sa->tunnel_src_addr.ip4.as_u32; + key.spi = clib_host_to_net_u32 (sa->spi); + + hash_set (im->ipsec4_if_pool_index_by_key, key.as_u64, + hi->dev_instance); + } } else { old_sa_index = t->output_sa_index; old_sa = pool_elt_at_index (im->sad, old_sa_index); + + if (sa->is_tunnel_ip6 ^ old_sa->is_tunnel_ip6) + { + clib_warning ("IPsec interface SA endpoints type can't be changed"); + return VNET_API_ERROR_INVALID_VALUE; + } + t->output_sa_index = sa_index; } @@ -599,17 +674,24 @@ ipsec_set_interface_sa (vnet_main_t * vnm, u32 hw_if_index, u32 sa_id, return 0; } + clib_error_t * ipsec_tunnel_if_init (vlib_main_t * vm) { ipsec_main_t *im = &ipsec_main; - im->ipsec_if_pool_index_by_key = hash_create (0, sizeof (uword)); + /* initialize the ipsec-if ip4 hash */ + im->ipsec4_if_pool_index_by_key = + hash_create (0, sizeof (ipsec4_tunnel_key_t)); + /* initialize the ipsec-if ip6 hash */ + im->ipsec6_if_pool_index_by_key = hash_create_mem (0, + sizeof + (ipsec6_tunnel_key_t), + sizeof (uword)); im->ipsec_if_real_dev_by_show_dev = hash_create (0, sizeof (uword)); - udp_register_dst_port (vm, UDP_DST_PORT_ipsec, ipsec_if_input_node.index, + udp_register_dst_port (vm, UDP_DST_PORT_ipsec, ipsec4_if_input_node.index, 1); - return 0; } diff --git a/src/vnet/ipsec/ipsec_if.h b/src/vnet/ipsec/ipsec_if.h index 3d042f0af01..7f0eb08b24c 100644 --- a/src/vnet/ipsec/ipsec_if.h +++ b/src/vnet/ipsec/ipsec_if.h @@ -41,6 +41,7 @@ typedef struct typedef struct { u8 is_add; + u8 is_ip6; u8 esn; u8 anti_replay; ip46_address_t local_ip, remote_ip; @@ -62,6 +63,35 @@ typedef struct u32 tx_table_id; } ipsec_add_del_tunnel_args_t; +/* *INDENT-OFF* */ +typedef CLIB_PACKED +(struct { + /* + * Key fields: remote ip and spi on incoming packet + * all fields in NET byte order + */ + union { + struct { + u32 remote_ip; + u32 spi; + }; + u64 as_u64; + }; +}) ipsec4_tunnel_key_t; +/* *INDENT-ON* */ + +/* *INDENT-OFF* */ +typedef CLIB_PACKED +(struct { + /* + * Key fields: remote ip and spi on incoming packet + * all fields in NET byte order + */ + ip6_address_t remote_ip; + u32 spi; +}) ipsec6_tunnel_key_t; +/* *INDENT-ON* */ + typedef struct { u8 is_add; diff --git a/src/vnet/ipsec/ipsec_if_in.c b/src/vnet/ipsec/ipsec_if_in.c index b12e36c4fbc..63d463bd633 100644 --- a/src/vnet/ipsec/ipsec_if_in.c +++ b/src/vnet/ipsec/ipsec_if_in.c @@ -63,8 +63,8 @@ format_ipsec_if_input_trace (u8 * s, va_list * args) always_inline uword -ipsec_if_input_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * from_frame) +ipsec_if_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; @@ -91,24 +91,32 @@ ipsec_if_input_inline (vlib_main_t * vm, u32 last_sw_if_index = ~0; u32 last_tunnel_id = ~0; - u64 last_key = ~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; + 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 >= 2) { u32 sw_if_index0, sw_if_index1; - ip4_header_t *ip0, *ip1; + ip4_header_t *ip40, *ip41; + ip6_header_t *ip60, *ip61; esp_header_t *esp0, *esp1; u32 len0, len1; u16 buf_adv0, buf_adv1; u32 tid0, tid1; ipsec_tunnel_if_t *t0, *t1; - u64 key0, key1; + ipsec4_tunnel_key_t key40, key41; + ipsec6_tunnel_key_t key60, key61; if (n_left_from >= 4) { @@ -118,33 +126,48 @@ ipsec_if_input_inline (vlib_main_t * vm, CLIB_PREFETCH (b[3]->data, CLIB_CACHE_LINE_BYTES, LOAD); } - ip0 = (ip4_header_t *) (b[0]->data + vnet_buffer (b[0])->l3_hdr_offset); - ip1 = (ip4_header_t *) (b[1]->data + vnet_buffer (b[1])->l3_hdr_offset); + ip40 = + (ip4_header_t *) (b[0]->data + vnet_buffer (b[0])->l3_hdr_offset); + ip41 = + (ip4_header_t *) (b[1]->data + vnet_buffer (b[1])->l3_hdr_offset); - /* NAT UDP port 4500 case, don't advance any more */ - if (ip0->protocol == IP_PROTOCOL_UDP) - { - esp0 = - (esp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0) + - sizeof (udp_header_t)); - buf_adv0 = 0; - } - else - { - esp0 = (esp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0)); - buf_adv0 = ip4_header_bytes (ip0); - } - if (ip1->protocol == IP_PROTOCOL_UDP) + if (is_ip6) { - esp1 = - (esp_header_t *) ((u8 *) ip1 + ip4_header_bytes (ip1) + - sizeof (udp_header_t)); - buf_adv1 = 0; + ip60 = (ip6_header_t *) ip40; + ip61 = (ip6_header_t *) ip41; + esp0 = (esp_header_t *) ((u8 *) ip60 + sizeof (ip6_header_t)); + esp1 = (esp_header_t *) ((u8 *) ip61 + sizeof (ip6_header_t)); + buf_adv0 = sizeof (ip6_header_t); + buf_adv1 = sizeof (ip6_header_t); } else { - esp1 = (esp_header_t *) ((u8 *) ip1 + ip4_header_bytes (ip1)); - buf_adv1 = ip4_header_bytes (ip1); + /* 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)); + buf_adv0 = 0; + } + else + { + esp0 = (esp_header_t *) ((u8 *) ip40 + ip4_header_bytes (ip40)); + buf_adv0 = ip4_header_bytes (ip40); + } + /* NAT UDP port 4500 case, don't advance any more */ + if (ip41->protocol == IP_PROTOCOL_UDP) + { + esp1 = + (esp_header_t *) ((u8 *) ip41 + ip4_header_bytes (ip41) + + sizeof (udp_header_t)); + buf_adv1 = 0; + } + else + { + esp1 = (esp_header_t *) ((u8 *) ip41 + ip4_header_bytes (ip41)); + buf_adv1 = ip4_header_bytes (ip41); + } } vlib_buffer_advance (b[0], buf_adv0); @@ -153,27 +176,58 @@ ipsec_if_input_inline (vlib_main_t * vm, len0 = vlib_buffer_length_in_chain (vm, b[0]); len1 = vlib_buffer_length_in_chain (vm, b[1]); - key0 = (u64) ip0->src_address.as_u32 << 32 | (u64) esp0->spi; - key1 = (u64) ip1->src_address.as_u32 << 32 | (u64) esp1->spi; - - if (key0 == last_key) + if (is_ip6) { - tid0 = last_tunnel_id; + key60.remote_ip = ip60->src_address; + key60.spi = esp0->spi; + + if (memcmp (&key60, &last_key6, sizeof (last_key6)) == 0) + { + tid0 = last_tunnel_id; + } + else + { + uword *p = + hash_get_mem (im->ipsec6_if_pool_index_by_key, &key60); + if (p) + { + tid0 = p[0]; + last_tunnel_id = tid0; + clib_memcpy_fast (&last_key6, &key60, sizeof (key60)); + } + else + { + n_no_tunnel++; + next[0] = IPSEC_INPUT_NEXT_DROP; + goto pkt1; + } + } } - else + else /* !is_ip6 */ { - uword *p = hash_get (im->ipsec_if_pool_index_by_key, key0); - if (p) + key40.remote_ip = ip40->src_address.as_u32; + key40.spi = esp0->spi; + + if (key40.as_u64 == last_key4.as_u64) { - tid0 = p[0]; - last_tunnel_id = tid0; - last_key = key0; + tid0 = last_tunnel_id; } else { - n_no_tunnel++; - next[0] = IPSEC_INPUT_NEXT_DROP; - goto pkt1; + uword *p = + hash_get (im->ipsec4_if_pool_index_by_key, key40.as_u64); + if (p) + { + tid0 = p[0]; + last_tunnel_id = tid0; + last_key4.as_u64 = key40.as_u64; + } + else + { + n_no_tunnel++; + next[0] = IPSEC_INPUT_NEXT_DROP; + goto pkt1; + } } } @@ -220,24 +274,58 @@ ipsec_if_input_inline (vlib_main_t * vm, } pkt1: - if (key1 == last_key) + if (is_ip6) { - tid1 = last_tunnel_id; + key61.remote_ip = ip61->src_address; + key61.spi = esp1->spi; + + if (memcmp (&key61, &last_key6, sizeof (last_key6)) == 0) + { + tid1 = last_tunnel_id; + } + else + { + uword *p = + hash_get_mem (im->ipsec6_if_pool_index_by_key, &key61); + if (p) + { + tid1 = p[0]; + last_tunnel_id = tid1; + clib_memcpy_fast (&last_key6, &key61, sizeof (key61)); + } + else + { + n_no_tunnel++; + next[1] = IPSEC_INPUT_NEXT_DROP; + goto trace1; + } + } } - else + else /* !is_ip6 */ { - uword *p = hash_get (im->ipsec_if_pool_index_by_key, key1); - if (p) + key41.remote_ip = ip41->src_address.as_u32; + key41.spi = esp1->spi; + + if (key41.as_u64 == last_key4.as_u64) { - tid1 = p[0]; - last_tunnel_id = tid1; - last_key = key1; + tid1 = last_tunnel_id; } else { - n_no_tunnel++; - next[1] = IPSEC_INPUT_NEXT_DROP; - goto trace1; + uword *p = + hash_get (im->ipsec4_if_pool_index_by_key, key41.as_u64); + if (p) + { + tid1 = p[0]; + last_tunnel_id = tid1; + last_key4.as_u64 = key41.as_u64; + } + else + { + n_no_tunnel++; + next[1] = IPSEC_INPUT_NEXT_DROP; + goto trace1; + } } } @@ -284,16 +372,16 @@ ipsec_if_input_inline (vlib_main_t * vm, } trace1: - if (is_trace) + if (PREDICT_FALSE (is_trace)) { - if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED)) + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) { ipsec_if_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); } - if (PREDICT_FALSE (b[1]->flags & VLIB_BUFFER_IS_TRACED)) + if (b[1]->flags & VLIB_BUFFER_IS_TRACED) { ipsec_if_input_trace_t *tr = vlib_add_trace (vm, node, b[1], sizeof (*tr)); @@ -310,28 +398,40 @@ ipsec_if_input_inline (vlib_main_t * vm, while (n_left_from > 0) { u32 sw_if_index0; - ip4_header_t *ip0; + ip4_header_t *ip40; + ip6_header_t *ip60; esp_header_t *esp0; u32 len0; u16 buf_adv0; u32 tid0; ipsec_tunnel_if_t *t0; - u64 key0; + ipsec4_tunnel_key_t key40; + ipsec6_tunnel_key_t key60; - ip0 = (ip4_header_t *) (b[0]->data + vnet_buffer (b[0])->l3_hdr_offset); + ip40 = + (ip4_header_t *) (b[0]->data + vnet_buffer (b[0])->l3_hdr_offset); - /* NAT UDP port 4500 case, don't advance any more */ - if (ip0->protocol == IP_PROTOCOL_UDP) + if (is_ip6) { - esp0 = - (esp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0) + - sizeof (udp_header_t)); - buf_adv0 = 0; + ip60 = (ip6_header_t *) ip40; + esp0 = (esp_header_t *) ((u8 *) ip60 + sizeof (ip6_header_t)); + buf_adv0 = sizeof (ip6_header_t); } else { - esp0 = (esp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0)); - buf_adv0 = ip4_header_bytes (ip0); + /* 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)); + buf_adv0 = 0; + } + else + { + esp0 = (esp_header_t *) ((u8 *) ip40 + ip4_header_bytes (ip40)); + buf_adv0 = ip4_header_bytes (ip40); + } } /* stats for the tunnel include all the data after the IP header @@ -339,25 +439,58 @@ ipsec_if_input_inline (vlib_main_t * vm, vlib_buffer_advance (b[0], buf_adv0); len0 = vlib_buffer_length_in_chain (vm, b[0]); - key0 = (u64) ip0->src_address.as_u32 << 32 | (u64) esp0->spi; - if (key0 == last_key) + if (is_ip6) { - tid0 = last_tunnel_id; + key60.remote_ip = ip60->src_address; + key60.spi = esp0->spi; + + if (memcmp (&key60, &last_key6, sizeof (last_key6)) == 0) + { + tid0 = last_tunnel_id; + } + else + { + uword *p = + hash_get_mem (im->ipsec6_if_pool_index_by_key, &key60); + if (p) + { + tid0 = p[0]; + last_tunnel_id = tid0; + clib_memcpy_fast (&last_key6, &key60, sizeof (key60)); + } + else + { + n_no_tunnel++; + next[0] = IPSEC_INPUT_NEXT_DROP; + goto trace00; + } + } } - else + else /* !is_ip6 */ { - uword *p = hash_get (im->ipsec_if_pool_index_by_key, key0); - if (p) + key40.remote_ip = ip40->src_address.as_u32; + key40.spi = esp0->spi; + + if (key40.as_u64 == last_key4.as_u64) { - tid0 = p[0]; - last_tunnel_id = tid0; - last_key = key0; + tid0 = last_tunnel_id; } else { - n_no_tunnel++; - next[0] = IPSEC_INPUT_NEXT_DROP; - goto trace00; + uword *p = + hash_get (im->ipsec4_if_pool_index_by_key, key40.as_u64); + if (p) + { + tid0 = p[0]; + last_tunnel_id = tid0; + last_key4.as_u64 = key40.as_u64; + } + else + { + n_no_tunnel++; + next[0] = IPSEC_INPUT_NEXT_DROP; + goto trace00; + } } } @@ -404,9 +537,9 @@ ipsec_if_input_inline (vlib_main_t * vm, } trace00: - if (is_trace) + if (PREDICT_FALSE (is_trace)) { - if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED)) + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) { ipsec_if_input_trace_t *tr = vlib_add_trace (vm, node, b[0], sizeof (*tr)); @@ -428,12 +561,12 @@ ipsec_if_input_inline (vlib_main_t * vm, last_sw_if_index, n_packets, n_bytes); } - vlib_node_increment_counter (vm, ipsec_if_input_node.index, + vlib_node_increment_counter (vm, node->node_index, IPSEC_IF_INPUT_ERROR_RX, from_frame->n_vectors - n_disabled); - vlib_node_increment_counter (vm, ipsec_if_input_node.index, + vlib_node_increment_counter (vm, node->node_index, IPSEC_IF_INPUT_ERROR_DISABLED, n_disabled); - vlib_node_increment_counter (vm, ipsec_if_input_node.index, + vlib_node_increment_counter (vm, node->node_index, IPSEC_IF_INPUT_ERROR_NO_TUNNEL, n_no_tunnel); vlib_buffer_enqueue_to_next (vm, node, from, nexts, from_frame->n_vectors); @@ -441,16 +574,16 @@ ipsec_if_input_inline (vlib_main_t * vm, return from_frame->n_vectors; } -VLIB_NODE_FN (ipsec_if_input_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (ipsec4_if_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) { - return ipsec_if_input_inline (vm, node, from_frame); + return ipsec_if_input_inline (vm, node, from_frame, 0 /* is_ip6 */ ); } /* *INDENT-OFF* */ -VLIB_REGISTER_NODE (ipsec_if_input_node) = { - .name = "ipsec-if-input", +VLIB_REGISTER_NODE (ipsec4_if_input_node) = { + .name = "ipsec4-if-input", .vector_size = sizeof (u32), .format_trace = format_ipsec_if_input_trace, .type = VLIB_NODE_TYPE_INTERNAL, @@ -460,6 +593,25 @@ VLIB_REGISTER_NODE (ipsec_if_input_node) = { }; /* *INDENT-ON* */ +VLIB_NODE_FN (ipsec6_if_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return ipsec_if_input_inline (vm, node, from_frame, 1 /* is_ip6 */ ); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ipsec6_if_input_node) = { + .name = "ipsec6-if-input", + .vector_size = sizeof (u32), + .format_trace = format_ipsec_if_input_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(ipsec_if_input_error_strings), + .error_strings = ipsec_if_input_error_strings, + .sibling_of = "ipsec6-input-feature", +}; +/* *INDENT-ON* */ + /* * fd.io coding-style-patch-verification: ON * diff --git a/test/template_ipsec.py b/test/template_ipsec.py index 68f1183fbc2..483699c2eac 100644 --- a/test/template_ipsec.py +++ b/test/template_ipsec.py @@ -477,14 +477,15 @@ class IpsecTun6Tests(object): self.logger.info(self.vapi.ppcli("show error")) self.logger.info(self.vapi.ppcli("show ipsec")) - pkts = p.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'] - self.assertEqual(pkts, count, - "incorrect SA out counts: expected %d != %d" % - (count, pkts)) + if (hasattr(p, "tun_sa_in")): + pkts = p.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'] + 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) diff --git a/test/test_ipsec_tun_if_esp.py b/test/test_ipsec_tun_if_esp.py index 2734908e7a1..06d2c89ebe9 100644 --- a/test/test_ipsec_tun_if_esp.py +++ b/test/test_ipsec_tun_if_esp.py @@ -2,18 +2,19 @@ import unittest import socket from scapy.layers.ipsec import ESP from framework import VppTestRunner -from template_ipsec import TemplateIpsec, IpsecTun4Tests, IpsecTcpTests +from template_ipsec import TemplateIpsec, IpsecTun4Tests, IpsecTun6Tests, \ + IpsecTcpTests from vpp_ipsec_tun_interface import VppIpsecTunInterface -from vpp_ip_route import VppIpRoute, VppRoutePath +from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto -class TemplateIpsecTunIfEsp(TemplateIpsec): +class TemplateIpsec4TunIfEsp(TemplateIpsec): """ IPsec tunnel interface tests """ encryption_type = ESP def setUp(self): - super(TemplateIpsecTunIfEsp, self).setUp() + super(TemplateIpsec4TunIfEsp, self).setUp() self.tun_if = self.pg0 @@ -34,19 +35,57 @@ class TemplateIpsecTunIfEsp(TemplateIpsec): def tearDown(self): if not self.vpp_dead: self.vapi.cli("show hardware") - super(TemplateIpsecTunIfEsp, self).tearDown() + super(TemplateIpsec4TunIfEsp, self).tearDown() -class TestIpsecTunIfEsp1(TemplateIpsecTunIfEsp, IpsecTun4Tests): +class TestIpsec4TunIfEsp1(TemplateIpsec4TunIfEsp, IpsecTun4Tests): """ Ipsec ESP - TUN tests """ tun4_encrypt_node_name = "esp4-encrypt" tun4_decrypt_node_name = "esp4-decrypt" -class TestIpsecTunIfEsp2(TemplateIpsecTunIfEsp, IpsecTcpTests): +class TestIpsec4TunIfEsp2(TemplateIpsec4TunIfEsp, IpsecTcpTests): """ Ipsec ESP - TCP tests """ pass +class TemplateIpsec6TunIfEsp(TemplateIpsec): + """ IPsec tunnel interface tests """ + + encryption_type = ESP + + def setUp(self): + super(TemplateIpsec6TunIfEsp, self).setUp() + + self.tun_if = self.pg0 + + p = self.ipv6_params + tun_if = VppIpsecTunInterface(self, self.pg0, p.vpp_tun_spi, + p.scapy_tun_spi, p.crypt_algo_vpp_id, + p.crypt_key, p.crypt_key, + p.auth_algo_vpp_id, p.auth_key, + p.auth_key, is_ip6=True) + tun_if.add_vpp_config() + tun_if.admin_up() + tun_if.config_ip6() + + VppIpRoute(self, p.remote_tun_if_host, 32, + [VppRoutePath(tun_if.remote_ip6, + 0xffffffff, + proto=DpoProto.DPO_PROTO_IP6)], + is_ip6=1).add_vpp_config() + + def tearDown(self): + if not self.vpp_dead: + self.vapi.cli("show hardware") + super(TemplateIpsec6TunIfEsp, self).tearDown() + + +class TestIpsec6TunIfEsp1(TemplateIpsec6TunIfEsp, IpsecTun6Tests): + """ Ipsec ESP - TUN tests """ + tun6_encrypt_node_name = "esp6-encrypt" + tun6_decrypt_node_name = "esp6-decrypt" + + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_ipsec_tun_interface.py b/test/vpp_ipsec_tun_interface.py index bd635417f02..5c014ea282b 100644 --- a/test/vpp_ipsec_tun_interface.py +++ b/test/vpp_ipsec_tun_interface.py @@ -8,7 +8,7 @@ class VppIpsecTunInterface(VppTunnelInterface): def __init__(self, test, parent_if, local_spi, remote_spi, crypto_alg, local_crypto_key, remote_crypto_key, - integ_alg, local_integ_key, remote_integ_key): + integ_alg, local_integ_key, remote_integ_key, is_ip6=False): super(VppIpsecTunInterface, self).__init__(test, parent_if) self.local_spi = local_spi self.remote_spi = remote_spi @@ -18,23 +18,30 @@ class VppIpsecTunInterface(VppTunnelInterface): self.integ_alg = integ_alg self.local_integ_key = local_integ_key self.remote_integ_key = remote_integ_key + if is_ip6: + self.local_ip = self.parent_if.local_ip6 + self.remote_ip = self.parent_if.remote_ip6 + else: + self.local_ip = self.parent_if.local_ip4 + self.remote_ip = self.parent_if.remote_ip4 def add_vpp_config(self): r = self.test.vapi.ipsec_tunnel_if_add_del( - self.parent_if.local_ip4n, self.parent_if.remote_ip4n, - self.remote_spi, self.local_spi, self.crypto_alg, - self.local_crypto_key, self.remote_crypto_key, self.integ_alg, - self.local_integ_key, self.remote_integ_key) + self.local_ip, self.remote_ip, + self.remote_spi, self.local_spi, + self.crypto_alg, self.local_crypto_key, self.remote_crypto_key, + self.integ_alg, self.local_integ_key, self.remote_integ_key) 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_tunnel_if_add_del( - self.parent_if.local_ip4n, self.parent_if.remote_ip4n, - self.remote_spi, self.local_spi, self.crypto_alg, - self.local_crypto_key, self.remote_crypto_key, self.integ_alg, - self.local_integ_key, self.remote_integ_key, is_add=0) + self.local_ip, self.remote_ip, + self.remote_spi, self.local_spi, + self.crypto_alg, self.local_crypto_key, self.remote_crypto_key, + self.integ_alg, self.local_integ_key, self.remote_integ_key, + is_add=0) def __str__(self): return self.object_id() diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 9133683d402..d393ce14c25 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -2485,20 +2485,27 @@ class VppPapiProvider(object): anti_replay=1, renumber=0, show_instance=0): return self.api( self.papi.ipsec_tunnel_if_add_del, - {'local_ip': local_ip, 'remote_ip': remote_ip, - 'local_spi': local_spi, 'remote_spi': remote_spi, - 'crypto_alg': crypto_alg, - 'local_crypto_key_len': len(local_crypto_key), - 'local_crypto_key': local_crypto_key, - 'remote_crypto_key_len': len(remote_crypto_key), - 'remote_crypto_key': remote_crypto_key, 'integ_alg': integ_alg, - 'local_integ_key_len': len(local_integ_key), - 'local_integ_key': local_integ_key, - 'remote_integ_key_len': len(remote_integ_key), - 'remote_integ_key': remote_integ_key, 'is_add': is_add, - 'esn': esn, 'anti_replay': anti_replay, 'renumber': renumber, - 'show_instance': show_instance - }) + { + 'local_ip': local_ip, + 'remote_ip': remote_ip, + 'local_spi': local_spi, + 'remote_spi': remote_spi, + 'crypto_alg': crypto_alg, + 'local_crypto_key_len': len(local_crypto_key), + 'local_crypto_key': local_crypto_key, + 'remote_crypto_key_len': len(remote_crypto_key), + 'remote_crypto_key': remote_crypto_key, + 'integ_alg': integ_alg, + 'local_integ_key_len': len(local_integ_key), + 'local_integ_key': local_integ_key, + 'remote_integ_key_len': len(remote_integ_key), + 'remote_integ_key': remote_integ_key, + 'is_add': is_add, + 'esn': esn, + 'anti_replay': anti_replay, + 'renumber': renumber, + 'show_instance': show_instance + }) def ipsec_select_backend(self, protocol, index): return self.api(self.papi.ipsec_select_backend, -- 2.16.6