From d7fc12f07313f9147159f2562f6fcc928af7a963 Mon Sep 17 00:00:00 2001 From: Filip Tehlar Date: Fri, 30 Oct 2020 04:47:44 +0000 Subject: [PATCH] ikev2: add option to disable NAT traversal Type: feature Ticket: VPP-1935 Change-Id: I705f84047b112279377590157a1c7b4a34f693d2 Signed-off-by: Filip Tehlar --- src/plugins/ikev2/ikev2.api | 14 ++++++++ src/plugins/ikev2/ikev2.c | 40 ++++++++++++++++------ src/plugins/ikev2/ikev2_api.c | 24 ++++++++++++- src/plugins/ikev2/ikev2_cli.c | 12 ++++++- src/plugins/ikev2/ikev2_priv.h | 19 ++++++++++- src/plugins/ikev2/ikev2_test.c | 65 +++++++++++++++++++++++++----------- src/plugins/ikev2/ikev2_types.api | 1 + src/plugins/ikev2/test/test_ikev2.py | 6 ++++ src/plugins/ikev2/test/vpp_ikev2.py | 7 ++++ 9 files changed, 154 insertions(+), 34 deletions(-) diff --git a/src/plugins/ikev2/ikev2.api b/src/plugins/ikev2/ikev2.api index a7aeff1fa44..9f44cba1661 100644 --- a/src/plugins/ikev2/ikev2.api +++ b/src/plugins/ikev2/ikev2.api @@ -251,6 +251,20 @@ autoreply define ikev2_profile_set_id option status="in_progress"; }; +/** \brief IKEv2: Disable NAT traversal + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param name - IKEv2 profile name +*/ +autoreply define ikev2_profile_disable_natt +{ + u32 client_index; + u32 context; + + string name[64]; + option status="in_progress"; +}; + /** \brief IKEv2: Set IKEv2 profile traffic selector parameters @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c index ae48b44f36b..ad727a9e355 100644 --- a/src/plugins/ikev2/ikev2.c +++ b/src/plugins/ikev2/ikev2.c @@ -105,7 +105,7 @@ typedef u32 ikev2_non_esp_marker; static_always_inline u16 ikev2_get_port (ikev2_sa_t * sa) { - return sa->natt ? IKEV2_PORT_NATT : IKEV2_PORT; + return ikev2_natt_active (sa) ? IKEV2_PORT_NATT : IKEV2_PORT; } static_always_inline int @@ -427,6 +427,7 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai) sa->profile_index = sai->profile_index; sa->tun_itf = sai->tun_itf; sa->is_tun_itf_set = sai->is_tun_itf_set; + sa->natt_state = sai->natt_state; sa->i_id.data = _(sai->i_id.data); sa->r_id.data = _(sai->r_id.data); sa->i_auth.method = sai->i_auth.method; @@ -743,7 +744,8 @@ ikev2_process_sa_init_req (vlib_main_t * vm, udp->src_port); if (clib_memcmp (src_sha, n->data, vec_len (src_sha))) { - sa->natt = 1; + if (sa->natt_state == IKEV2_NATT_ENABLED) + sa->natt_state = IKEV2_NATT_ACTIVE; ikev2_elog_uint (IKEV2_LOG_DEBUG, "ispi %lx initiator" " behind NAT", sa->ispi); } @@ -756,7 +758,8 @@ ikev2_process_sa_init_req (vlib_main_t * vm, udp->dst_port); if (clib_memcmp (dst_sha, n->data, vec_len (dst_sha))) { - sa->natt = 1; + if (sa->natt_state == IKEV2_NATT_ENABLED) + sa->natt_state = IKEV2_NATT_ACTIVE; ikev2_elog_uint (IKEV2_LOG_DEBUG, "ispi %lx responder" " (self) behind NAT", sa->ispi); } @@ -869,7 +872,8 @@ ikev2_process_sa_init_resp (vlib_main_t * vm, udp->dst_port); if (clib_memcmp (dst_sha, n->data, vec_len (dst_sha))) { - sa->natt = 1; + if (sa->natt_state == IKEV2_NATT_ENABLED) + sa->natt_state = IKEV2_NATT_ACTIVE; ikev2_elog_uint (IKEV2_LOG_DEBUG, "ispi %lx initiator" " (self) behind NAT", sa->ispi); } @@ -1924,7 +1928,7 @@ ikev2_create_tunnel_interface (vlib_main_t * vm, a.flags |= IPSEC_SA_FLAG_IS_TUNNEL; a.flags |= IPSEC_SA_FLAG_UDP_ENCAP; } - if (sa->natt) + if (ikev2_natt_active (sa)) a.flags |= IPSEC_SA_FLAG_UDP_ENCAP; a.is_rekey = is_rekey; @@ -2050,7 +2054,8 @@ ikev2_create_tunnel_interface (vlib_main_t * vm, a.salt_remote = child->salt_ei; a.salt_local = child->salt_er; } - a.dst_port = sa->natt ? sa->dst_port : sa->ipsec_over_udp_port; + a.dst_port = + ikev2_natt_active (sa) ? sa->dst_port : sa->ipsec_over_udp_port; a.src_port = sa->ipsec_over_udp_port; } @@ -3197,7 +3202,7 @@ ikev2_node_internal (vlib_main_t * vm, clib_net_to_host_u16 (ikev2_get_port (sa0)); if (udp0->dst_port == clib_net_to_host_u16 (IKEV2_PORT_NATT) - && sa0->natt) + && ikev2_natt_active (sa0)) { if (!has_non_esp_marker) slen = ikev2_insert_non_esp_marker (ike0, slen); @@ -3635,7 +3640,7 @@ ikev2_initiate_delete_ike_sa_internal (vlib_main_t * vm, if (~0 == len) return; - if (sa->natt) + if (ikev2_natt_active (sa)) len = ikev2_insert_non_esp_marker (ike0, len); if (sa->is_initiator) @@ -4153,6 +4158,8 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) sa.state = IKEV2_STATE_SA_INIT; sa.tun_itf = p->tun_itf; sa.udp_encap = p->udp_encap; + if (p->natt_disabled) + sa.natt_state = IKEV2_NATT_DISABLED; sa.ipsec_over_udp_port = p->ipsec_over_udp_port; sa.is_tun_itf_set = 1; sa.initial_contact = 1; @@ -4292,7 +4299,7 @@ ikev2_delete_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa, if (~0 == len) return; - if (sa->natt) + if (ikev2_natt_active (sa)) len = ikev2_insert_non_esp_marker (ike0, len); ikev2_send_ike (vm, &sa->iaddr, &sa->raddr, bi0, len, ikev2_get_port (sa), sa->dst_port, sa->sw_if_index); @@ -4417,7 +4424,7 @@ ikev2_rekey_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa, if (~0 == len) return; - if (sa->natt) + if (ikev2_natt_active (sa)) len = ikev2_insert_non_esp_marker (ike0, len); ikev2_send_ike (vm, &sa->iaddr, &sa->raddr, bi0, len, ikev2_get_port (sa), ikev2_get_port (sa), sa->sw_if_index); @@ -4720,6 +4727,17 @@ ikev2_set_liveness_params (u32 period, u32 max_retries) return 0; } +clib_error_t * +ikev2_profile_natt_disable (u8 * name) +{ + ikev2_profile_t *p = ikev2_profile_index_by_name (name); + if (!p) + return clib_error_return (0, "unknown profile %v", name); + + p->natt_disabled = 1; + return 0; +} + static void ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa) { @@ -4857,7 +4875,7 @@ ikev2_send_informational_request (ikev2_sa_t * sa) if (~0 == len) return; - if (sa->natt) + if (ikev2_natt_active (sa)) len = ikev2_insert_non_esp_marker (ike0, len); if (sa->is_initiator) diff --git a/src/plugins/ikev2/ikev2_api.c b/src/plugins/ikev2/ikev2_api.c index 33ff0853832..63d8760b043 100644 --- a/src/plugins/ikev2/ikev2_api.c +++ b/src/plugins/ikev2/ikev2_api.c @@ -163,7 +163,7 @@ send_profile (ikev2_profile_t * profile, vl_api_registration_t * reg, rmp->profile.udp_encap = profile->udp_encap; rmp->profile.tun_itf = profile->tun_itf; - + rmp->profile.natt_disabled = profile->natt_disabled; rmp->profile.ipsec_over_udp_port = profile->ipsec_over_udp_port; rmp->profile.lifetime = profile->lifetime; @@ -857,6 +857,28 @@ static void REPLY_MACRO (VL_API_IKEV2_INITIATE_DEL_CHILD_SA_REPLY); } +static void + vl_api_ikev2_profile_disable_natt_t_handler + (vl_api_ikev2_profile_disable_natt_t * mp) +{ + vl_api_ikev2_profile_disable_natt_reply_t *rmp; + int rv = 0; + +#if WITH_LIBSSL > 0 + clib_error_t *error; + + u8 *tmp = format (0, "%s", mp->name); + error = ikev2_profile_natt_disable (tmp); + vec_free (tmp); + if (error) + rv = VNET_API_ERROR_UNSPECIFIED; +#else + rv = VNET_API_ERROR_UNIMPLEMENTED; +#endif + + REPLY_MACRO (VL_API_IKEV2_PROFILE_DISABLE_NATT_REPLY); +} + static void vl_api_ikev2_initiate_rekey_child_sa_t_handler (vl_api_ikev2_initiate_rekey_child_sa_t * mp) diff --git a/src/plugins/ikev2/ikev2_cli.c b/src/plugins/ikev2/ikev2_cli.c index b0de9409a56..72f82b4e135 100644 --- a/src/plugins/ikev2/ikev2_cli.c +++ b/src/plugins/ikev2/ikev2_cli.c @@ -510,6 +510,12 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, r = clib_error_return (0, "Error: %U", format_vnet_api_errno, rv); goto done; } + else if (unformat (line_input, "set %U disable natt", + unformat_ikev2_token, &name)) + { + r = ikev2_profile_natt_disable (name); + goto done; + } else break; } @@ -541,7 +547,8 @@ VLIB_CLI_COMMAND (ikev2_profile_add_del_command, static) = { "ikev2 profile set ike-crypto-alg ike-integ-alg ike-dh \n" "ikev2 profile set esp-crypto-alg " "[esp-integ-alg ]\n" - "ikev2 profile set sa-lifetime ", + "ikev2 profile set sa-lifetime " + "ikev2 profile set disable natt\n", .function = ikev2_profile_add_del_command_fn, }; /* *INDENT-ON* */ @@ -626,6 +633,9 @@ show_ikev2_profile_command_fn (vlib_main_t * vm, if (p->udp_encap) vlib_cli_output(vm, " udp-encap"); + if (p->natt_disabled) + vlib_cli_output(vm, " NAT-T disabled"); + if (p->ipsec_over_udp_port != IPSEC_UDP_PORT_NONE) vlib_cli_output(vm, " ipsec-over-udp port %d", p->ipsec_over_udp_port); diff --git a/src/plugins/ikev2/ikev2_priv.h b/src/plugins/ikev2/ikev2_priv.h index ae0c2a4bae3..fa302dcf21a 100644 --- a/src/plugins/ikev2/ikev2_priv.h +++ b/src/plugins/ikev2/ikev2_priv.h @@ -347,8 +347,24 @@ typedef struct u32 tun_itf; u8 udp_encap; + u8 natt_disabled; } ikev2_profile_t; +typedef enum +{ + /* SA will switch to port 4500 when NAT is detected. + * This is the default. */ + IKEV2_NATT_ENABLED, + + /* Do nothing when NAT is detected */ + IKEV2_NATT_DISABLED, + + /* NAT was detected and port switched to 4500 */ + IKEV2_NATT_ACTIVE, +} ikev2_natt_state_t; + +#define ikev2_natt_active(_sa) ((_sa)->natt_state == IKEV2_NATT_ACTIVE) + typedef struct { ikev2_state_t state; @@ -428,7 +444,7 @@ typedef struct u32 sw_if_index; /* is NAT traversal mode */ - u8 natt; + ikev2_natt_state_t natt_state; u8 keys_generated; } ikev2_sa_t; @@ -575,6 +591,7 @@ ikev2_notify_t *ikev2_parse_notify_payload (ike_payload_header_t * ikep, int ikev2_set_log_level (ikev2_log_level_t log_level); u8 *ikev2_find_ike_notify_payload (ike_header_t * ike, u32 msg_type); void ikev2_disable_dpd (void); +clib_error_t *ikev2_profile_natt_disable (u8 * name); static_always_inline ikev2_main_per_thread_data_t * ikev2_get_per_thread_data () diff --git a/src/plugins/ikev2/ikev2_test.c b/src/plugins/ikev2/ikev2_test.c index 81a222c0971..d9f5e2256b6 100644 --- a/src/plugins/ikev2/ikev2_test.c +++ b/src/plugins/ikev2/ikev2_test.c @@ -46,6 +46,7 @@ typedef struct vat_main_t *vat_main; } ikev2_test_main_t; +static const char *valid_chars = "a-zA-Z0-9_"; ikev2_test_main_t ikev2_test_main; uword @@ -191,6 +192,47 @@ format_ikev2_sa_transform (u8 * s, va_list * args) return s; } +static int +api_ikev2_profile_disable_natt (vat_main_t * vam) +{ + unformat_input_t *i = vam->input; + vl_api_ikev2_profile_disable_natt_t *mp; + u8 *name = 0; + int ret; + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "%U", unformat_token, valid_chars, &name)) + vec_add1 (name, 0); + else + { + errmsg ("parse error '%U'", format_unformat_error, i); + return -99; + } + } + + if (!vec_len (name)) + { + errmsg ("profile name must be specified"); + return -99; + } + + if (vec_len (name) > 64) + { + errmsg ("profile name too long"); + return -99; + } + + M (IKEV2_PROFILE_DISABLE_NATT, mp); + + clib_memcpy (mp->name, name, vec_len (name)); + vec_free (name); + + S (mp); + W (ret); + return ret; +} + static int api_ikev2_profile_dump (vat_main_t * vam) { @@ -280,6 +322,9 @@ static void vl_api_ikev2_profile_details_t_handler if (p->udp_encap) fformat (vam->ofp, " udp-encap\n"); + if (p->natt_disabled) + fformat (vam->ofp, " NAT-T disabled\n"); + u32 ipsec_over_udp_port = clib_net_to_host_u16 (p->ipsec_over_udp_port); if (ipsec_over_udp_port != IPSEC_UDP_PORT_NONE) fformat (vam->ofp, " ipsec-over-udp port %d\n", ipsec_over_udp_port); @@ -674,8 +719,6 @@ api_ikev2_profile_add_del (vat_main_t * vam) u8 *name = 0; int ret; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "del")) @@ -723,8 +766,6 @@ api_ikev2_profile_set_auth (vat_main_t * vam) u8 is_hex = 0; int ret; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "name %U", unformat_token, valid_chars, &name)) @@ -794,8 +835,6 @@ api_ikev2_profile_set_id (vat_main_t * vam) ip_address_t ip; int ret; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "name %U", unformat_token, valid_chars, &name)) @@ -871,8 +910,6 @@ api_ikev2_profile_set_ts (vat_main_t * vam) u32 proto = 0, start_port = 0, end_port = (u32) ~ 0; ip_address_t start_addr, end_addr; u8 start_addr_set = 0, end_addr_set = 0; - - const char *valid_chars = "a-zA-Z0-9_"; int ret; while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) @@ -984,8 +1021,6 @@ api_ikev2_profile_set_udp_encap (vat_main_t * vam) int ret; u8 *name = 0; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "%U udp-encap", unformat_token, valid_chars, &name)) @@ -1035,8 +1070,6 @@ api_ikev2_set_responder (vat_main_t * vam) u32 sw_if_index = ~0; ip_address_t address; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat @@ -1084,8 +1117,6 @@ api_ikev2_set_ike_transforms (vat_main_t * vam) u8 *name = 0; u32 crypto_alg, crypto_key_size, integ_alg, dh_group; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "%U %d %d %d %d", unformat_token, valid_chars, &name, @@ -1134,8 +1165,6 @@ api_ikev2_set_esp_transforms (vat_main_t * vam) u8 *name = 0; u32 crypto_alg, crypto_key_size, integ_alg; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "%U %d %d %d", unformat_token, valid_chars, &name, @@ -1183,8 +1212,6 @@ api_ikev2_set_sa_lifetime (vat_main_t * vam) u64 lifetime, lifetime_maxdata; u32 lifetime_jitter, handover; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "%U %lu %u %u %lu", unformat_token, valid_chars, &name, @@ -1232,8 +1259,6 @@ api_ikev2_initiate_sa_init (vat_main_t * vam) int ret; u8 *name = 0; - const char *valid_chars = "a-zA-Z0-9_"; - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "%U", unformat_token, valid_chars, &name)) diff --git a/src/plugins/ikev2/ikev2_types.api b/src/plugins/ikev2/ikev2_types.api index 0f998b196c2..d39cf88c8d4 100644 --- a/src/plugins/ikev2/ikev2_types.api +++ b/src/plugins/ikev2/ikev2_types.api @@ -84,6 +84,7 @@ typedef ikev2_profile u16 ipsec_over_udp_port; u32 tun_itf; bool udp_encap; + bool natt_disabled; vl_api_ikev2_auth_t auth; }; diff --git a/src/plugins/ikev2/test/test_ikev2.py b/src/plugins/ikev2/test/test_ikev2.py index 91cec8e9a62..d065d46e8eb 100644 --- a/src/plugins/ikev2/test/test_ikev2.py +++ b/src/plugins/ikev2/test/test_ikev2.py @@ -1393,6 +1393,8 @@ class TestApi(VppTestCase): p.set_lifetime_data(cfg['lifetime_data']) if 'tun_itf' in cfg: p.set_tunnel_interface(cfg['tun_itf']) + if 'natt_disabled' in cfg and cfg['natt_disabled']: + p.disable_natt() p.add_vpp_config() return p @@ -1431,6 +1433,7 @@ class TestApi(VppTestCase): conf = { 'p1': { 'name': 'p1', + 'natt_disabled': True, 'loc_id': ('fqdn', b'vpp.home'), 'rem_id': ('fqdn', b'roadwarrior.example.com'), 'loc_ts': loc_ts4, @@ -1534,6 +1537,9 @@ class TestApi(VppTestCase): self.verify_ike_transforms(ap.ike_ts, cp['ike_ts']) self.verify_esp_transforms(ap.esp_ts, cp['esp_ts']) self.verify_auth(ap.auth, cp['auth']) + natt_dis = False if 'natt_disabled' not in cp else cp['natt_disabled'] + self.assertTrue(natt_dis == ap.natt_disabled) + if 'lifetime_data' in cp: self.verify_lifetime_data(ap, cp['lifetime_data']) self.assertEqual(ap.ipsec_over_udp_port, cp['ipsec_over_udp_port']) diff --git a/src/plugins/ikev2/test/vpp_ikev2.py b/src/plugins/ikev2/test/vpp_ikev2.py index 6ae30201450..dd1c3fc986e 100644 --- a/src/plugins/ikev2/test/vpp_ikev2.py +++ b/src/plugins/ikev2/test/vpp_ikev2.py @@ -27,6 +27,10 @@ class Profile(VppObject): self.vapi = test.vapi self.profile_name = profile_name self.udp_encap = False + self.natt = True + + def disable_natt(self): + self.natt = False def add_auth(self, method, data, is_hex=False): if isinstance(method, int): @@ -156,6 +160,9 @@ class Profile(VppObject): self.vapi.ikev2_set_tunnel_interface(name=self.profile_name, sw_if_index=self.tun_itf) + if not self.natt: + self.vapi.ikev2_profile_disable_natt(name=self.profile_name) + def query_vpp_config(self): res = self.vapi.ikev2_profile_dump() for r in res: -- 2.16.6