From e5d34919b4561a5ee11e41dec6b0184537b39696 Mon Sep 17 00:00:00 2001 From: Filip Tehlar Date: Mon, 2 Mar 2020 15:17:37 +0000 Subject: [PATCH 1/1] ikev2: add support for custom ipsec-over-udp port Type: feature Change-Id: Ifee2b3dca85ea915067b9285e3636802bf0c19a8 Signed-off-by: Filip Tehlar --- src/plugins/ikev2/ikev2.api | 16 +++++++ src/plugins/ikev2/ikev2.c | 103 +++++++++++++++++++++++++++++++++++++++-- src/plugins/ikev2/ikev2.h | 3 ++ src/plugins/ikev2/ikev2_api.c | 24 ++++++++++ src/plugins/ikev2/ikev2_cli.c | 12 +++++ src/plugins/ikev2/ikev2_priv.h | 5 ++ src/plugins/ikev2/ikev2_test.c | 6 +++ src/vnet/api_errno.h | 4 +- src/vnet/ipsec/ipsec_api.c | 8 ++-- src/vnet/ipsec/ipsec_cli.c | 7 +-- src/vnet/ipsec/ipsec_sa.c | 15 ++++-- src/vnet/ipsec/ipsec_sa.h | 5 +- 12 files changed, 193 insertions(+), 15 deletions(-) diff --git a/src/plugins/ikev2/ikev2.api b/src/plugins/ikev2/ikev2.api index c872c6f0088..274d496a032 100644 --- a/src/plugins/ikev2/ikev2.api +++ b/src/plugins/ikev2/ikev2.api @@ -330,6 +330,22 @@ autoreply define ikev2_profile_set_udp_encap string name[64]; }; +/** \brief IKEv2: Set/unset custom ipsec-over-udp port + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_set - whether set or unset custom port + @port - port number + @param name - IKEv2 profile name +*/ +autoreply define ikev2_profile_set_ipsec_udp_port +{ + u32 client_index; + u32 context; + + u8 is_set; + u16 port; + string name[64]; +}; /* * Local Variables: * eval: (c-set-style "gnu") diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c index 2dd0819a485..ea99e375ea0 100644 --- a/src/plugins/ikev2/ikev2.c +++ b/src/plugins/ikev2/ikev2.c @@ -399,6 +399,7 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai) sa->last_sa_init_req_packet_data = _(sai->last_sa_init_req_packet_data); sa->childs = _(sai->childs); sa->udp_encap = sai->udp_encap; + sa->dst_port = sai->dst_port; #undef _ @@ -1379,8 +1380,10 @@ ikev2_sa_auth (ikev2_sa_t * sa) /* *INDENT-ON* */ if (sel_p) - sa->udp_encap = sel_p->udp_encap; - + { + sa->udp_encap = sel_p->udp_encap; + sa->dst_port = sel_p->dst_port; + } vec_free (authmsg); if (sa->state == IKEV2_STATE_AUTHENTICATED) @@ -1502,6 +1505,7 @@ typedef struct ipsec_key_t loc_ckey, rem_ckey, loc_ikey, rem_ikey; u8 is_rekey; u32 old_remote_sa_id; + u16 dst_port; } ikev2_add_ipsec_tunnel_args_t; static void @@ -1559,13 +1563,13 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a) IPSEC_PROTOCOL_ESP, a->encr_type, &a->loc_ckey, a->integ_type, &a->loc_ikey, a->flags, 0, a->salt_local, &a->local_ip, - &a->remote_ip, NULL); + &a->remote_ip, NULL, a->dst_port); rv |= ipsec_sa_add_and_lock (a->remote_sa_id, a->remote_spi, IPSEC_PROTOCOL_ESP, a->encr_type, &a->rem_ckey, a->integ_type, &a->rem_ikey, (a->flags | IPSEC_SA_FLAG_IS_INBOUND), 0, a->salt_remote, &a->remote_ip, - &a->local_ip, NULL); + &a->local_ip, NULL, a->dst_port); rv |= ipsec_tun_protect_update (sw_if_index, NULL, a->local_sa_id, sas_in); } @@ -1799,6 +1803,7 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm, child->remote_sa_id = a.remote_sa_id = remote_sa_id; a.sw_if_index = (sa->is_tun_itf_set ? sa->tun_itf : ~0); + a.dst_port = sa->dst_port; vl_api_rpc_call_main_thread (ikev2_add_tunnel_from_main, (u8 *) & a, sizeof (a)); @@ -2928,6 +2933,58 @@ ikev2_set_local_key (vlib_main_t * vm, u8 * file) return 0; } +static_always_inline vnet_api_error_t +ikev2_register_udp_port (ikev2_profile_t * p, u16 port) +{ + ikev2_main_t *km = &ikev2_main; + udp_dst_port_info_t *pi; + + uword *v = hash_get (km->udp_ports, port); + pi = udp_get_dst_port_info (&udp_main, port, UDP_IP4); + + if (v) + { + /* IKE already uses this port, only increment reference counter */ + ASSERT (pi); + v[0]++; + } + else + { + if (pi) + return VNET_API_ERROR_UDP_PORT_TAKEN; + + udp_register_dst_port (km->vlib_main, port, + ipsec4_tun_input_node.index, 1); + hash_set (km->udp_ports, port, 1); + } + p->dst_port = port; + return 0; +} + +static_always_inline void +ikev2_unregister_udp_port (ikev2_profile_t * p) +{ + ikev2_main_t *km = &ikev2_main; + uword *v; + + if (p->dst_port == IPSEC_UDP_PORT_NONE) + return; + + v = hash_get (km->udp_ports, p->dst_port); + if (!v) + return; + + v[0]--; + + if (v[0] == 0) + { + udp_unregister_dst_port (km->vlib_main, p->dst_port, 1); + hash_unset (km->udp_ports, p->dst_port); + } + + p->dst_port = IPSEC_UDP_PORT_NONE; +} + clib_error_t * ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add) { @@ -2942,6 +2999,7 @@ ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add) pool_get (km->profiles, p); clib_memset (p, 0, sizeof (*p)); p->name = vec_dup (name); + p->dst_port = IPSEC_UDP_PORT_NONE; p->responder.sw_if_index = ~0; p->tun_itf = ~0; uword index = p - km->profiles; @@ -2953,6 +3011,8 @@ ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add) if (!p) return clib_error_return (0, "policy %v does not exists", name); + ikev2_unregister_udp_port (p); + vec_free (p->name); pool_put (km->profiles, p); mhash_unset (&km->profile_index_by_name, name, 0); @@ -3161,6 +3221,39 @@ ikev2_set_profile_tunnel_interface (vlib_main_t * vm, return 0; } +vnet_api_error_t +ikev2_set_profile_ipsec_udp_port (vlib_main_t * vm, u8 * name, u16 port, + u8 is_set) +{ + ikev2_profile_t *p = ikev2_profile_index_by_name (name); + ikev2_main_t *km = &ikev2_main; + vnet_api_error_t rv = 0; + uword *v; + + if (!p) + return VNET_API_ERROR_INVALID_VALUE; + + if (is_set) + { + if (p->dst_port != IPSEC_UDP_PORT_NONE) + return VNET_API_ERROR_VALUE_EXIST; + + rv = ikev2_register_udp_port (p, port); + } + else + { + v = hash_get (km->udp_ports, port); + if (!v) + return VNET_API_ERROR_IKE_NO_PORT; + + if (p->dst_port == IPSEC_UDP_PORT_NONE) + return VNET_API_ERROR_INVALID_VALUE; + + ikev2_unregister_udp_port (p); + } + return rv; +} + clib_error_t * ikev2_set_profile_udp_encap (vlib_main_t * vm, u8 * name) { @@ -3262,6 +3355,7 @@ 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; + sa.dst_port = p->dst_port; sa.is_tun_itf_set = 1; sa.initial_contact = 1; ikev2_generate_sa_init_data (&sa); @@ -3606,6 +3700,7 @@ ikev2_init (vlib_main_t * vm) km->sa_by_ispi = hash_create (0, sizeof (uword)); km->sw_if_indices = hash_create (0, 0); + km->udp_ports = hash_create (0, sizeof (uword)); udp_register_dst_port (vm, 500, ikev2_node.index, 1); diff --git a/src/plugins/ikev2/ikev2.h b/src/plugins/ikev2/ikev2.h index c73df152909..d79a031c490 100644 --- a/src/plugins/ikev2/ikev2.h +++ b/src/plugins/ikev2/ikev2.h @@ -394,6 +394,9 @@ clib_error_t *ikev2_set_profile_sa_lifetime (vlib_main_t * vm, u8 * name, u32 handover, u64 maxdata); clib_error_t *ikev2_set_profile_tunnel_interface (vlib_main_t * vm, u8 * name, u32 sw_if_index); +vnet_api_error_t ikev2_set_profile_ipsec_udp_port (vlib_main_t * vm, + u8 * name, u16 port, + u8 is_set); clib_error_t *ikev2_set_profile_udp_encap (vlib_main_t * vm, u8 * name); clib_error_t *ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name); clib_error_t *ikev2_initiate_delete_child_sa (vlib_main_t * vm, u32 ispi); diff --git a/src/plugins/ikev2/ikev2_api.c b/src/plugins/ikev2/ikev2_api.c index f846bfab9f0..0064f57483f 100644 --- a/src/plugins/ikev2/ikev2_api.c +++ b/src/plugins/ikev2/ikev2_api.c @@ -311,6 +311,30 @@ vl_api_ikev2_set_sa_lifetime_t_handler (vl_api_ikev2_set_sa_lifetime_t * mp) REPLY_MACRO (VL_API_IKEV2_SET_SA_LIFETIME_REPLY); } +static void + vl_api_ikev2_profile_set_ipsec_udp_port_t_handler + (vl_api_ikev2_profile_set_ipsec_udp_port_t * mp) +{ + vl_api_ikev2_profile_set_ipsec_udp_port_reply_t *rmp; + int rv = 0; + +#if WITH_LIBSSL > 0 + vlib_main_t *vm = vlib_get_main (); + + u8 *tmp = format (0, "%s", mp->name); + + rv = + ikev2_set_profile_ipsec_udp_port (vm, tmp, + clib_net_to_host_u16 (mp->port), + mp->is_set); + vec_free (tmp); +#else + rv = VNET_API_ERROR_UNIMPLEMENTED; +#endif + + REPLY_MACRO (VL_API_IKEV2_PROFILE_SET_IPSEC_UDP_PORT_REPLY); +} + static void vl_api_ikev2_set_tunnel_interface_t_handler (vl_api_ikev2_set_tunnel_interface_t * mp) diff --git a/src/plugins/ikev2/ikev2_cli.c b/src/plugins/ikev2/ikev2_cli.c index 1acae91c777..03f9efd696f 100644 --- a/src/plugins/ikev2/ikev2_cli.c +++ b/src/plugins/ikev2/ikev2_cli.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -391,6 +392,14 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, r = ikev2_set_profile_udp_encap (vm, name); goto done; } + else if (unformat (line_input, "set %U ipsec-over-udp port %u", + unformat_token, valid_chars, &name, &tmp1)) + { + int rv = ikev2_set_profile_ipsec_udp_port (vm, name, tmp1, 1); + if (rv) + r = clib_error_return (0, "Error: %U", format_vnet_api_errno, rv); + goto done; + } else break; } @@ -502,6 +511,9 @@ show_ikev2_profile_command_fn (vlib_main_t * vm, format_vnet_sw_if_index_name, vnet_get_main(), p->tun_itf); if (p->udp_encap) vlib_cli_output(vm, " udp-encap"); + + if (p->dst_port != IPSEC_UDP_PORT_NONE) + vlib_cli_output(vm, " ipsec-over-udp port %d", p->dst_port); })); /* *INDENT-ON* */ diff --git a/src/plugins/ikev2/ikev2_priv.h b/src/plugins/ikev2/ikev2_priv.h index 1cae47b4290..d2f5f056917 100644 --- a/src/plugins/ikev2/ikev2_priv.h +++ b/src/plugins/ikev2/ikev2_priv.h @@ -358,6 +358,7 @@ typedef struct u64 lifetime_maxdata; u32 lifetime_jitter; u32 handover; + u16 dst_port; u32 tun_itf; u8 udp_encap; @@ -424,6 +425,7 @@ typedef struct u8 is_tun_itf_set; u32 tun_itf; u8 udp_encap; + u16 dst_port; f64 old_id_expiration; u32 current_remote_id_mask; @@ -481,6 +483,9 @@ typedef struct /* logging level */ ikev2_log_level_t log_level; + + /* custom ipsec-over-udp ports managed by ike */ + uword *udp_ports; } ikev2_main_t; extern ikev2_main_t ikev2_main; diff --git a/src/plugins/ikev2/ikev2_test.c b/src/plugins/ikev2/ikev2_test.c index 9ff4bbcc58c..8be42fef427 100644 --- a/src/plugins/ikev2/ikev2_test.c +++ b/src/plugins/ikev2/ikev2_test.c @@ -99,6 +99,12 @@ static void vl_api_ikev2_plugin_get_version_reply_t_handler vam->result_ready = 1; } +static int +api_ikev2_profile_set_ipsec_udp_port (vat_main_t * vam) +{ + return 0; +} + static int api_ikev2_profile_add_del (vat_main_t * vam) { diff --git a/src/vnet/api_errno.h b/src/vnet/api_errno.h index eb25f05a853..15b17a86d5d 100644 --- a/src/vnet/api_errno.h +++ b/src/vnet/api_errno.h @@ -153,7 +153,9 @@ _(FIB_PATH_UNSUPPORTED_NH_PROTO, -157, "Unsupported FIB Path protocol") \ _(API_ENDIAN_FAILED, -159, "Endian mismatch detected") \ _(NO_CHANGE, -160, "No change in table") \ _(MISSING_CERT_KEY, -161, "Missing certifcate or key") \ -_(LIMIT_EXCEEDED, -162, "limit exceeded") +_(LIMIT_EXCEEDED, -162, "limit exceeded") \ +_(IKE_NO_PORT, -163, "port not managed by IKE") \ +_(UDP_PORT_TAKEN, -164, "UDP port already taken") \ typedef enum { diff --git a/src/vnet/ipsec/ipsec_api.c b/src/vnet/ipsec/ipsec_api.c index c7ce3ef83d5..177300aeb9a 100644 --- a/src/vnet/ipsec/ipsec_api.c +++ b/src/vnet/ipsec/ipsec_api.c @@ -372,7 +372,7 @@ static void vl_api_ipsec_sad_entry_add_del_t_handler crypto_alg, &crypto_key, integ_alg, &integ_key, flags, 0, mp->entry.salt, &tun_src, &tun_dst, - &sa_index); + &sa_index, IPSEC_UDP_PORT_NONE); else rv = ipsec_sa_unlock_id (id); @@ -662,7 +662,8 @@ vl_api_ipsec_tunnel_if_add_del_t_handler (vl_api_ipsec_tunnel_if_add_del_t * &integ_key, (flags | IPSEC_SA_FLAG_IS_INBOUND), ntohl (mp->tx_table_id), - mp->salt, &remote_ip, &local_ip, NULL); + mp->salt, &remote_ip, &local_ip, NULL, + IPSEC_UDP_PORT_NONE); if (rv) goto done; @@ -676,7 +677,8 @@ vl_api_ipsec_tunnel_if_add_del_t_handler (vl_api_ipsec_tunnel_if_add_del_t * &integ_key, flags, ntohl (mp->tx_table_id), - mp->salt, &local_ip, &remote_ip, NULL); + mp->salt, &local_ip, &remote_ip, NULL, + IPSEC_UDP_PORT_NONE); if (rv) goto done; diff --git a/src/vnet/ipsec/ipsec_cli.c b/src/vnet/ipsec/ipsec_cli.c index 04061901ccc..7779e79f067 100644 --- a/src/vnet/ipsec/ipsec_cli.c +++ b/src/vnet/ipsec/ipsec_cli.c @@ -149,7 +149,8 @@ ipsec_sa_add_del_command_fn (vlib_main_t * vm, rv = ipsec_sa_add_and_lock (id, spi, proto, crypto_alg, &ck, integ_alg, &ik, flags, 0, clib_host_to_net_u32 (salt), - &tun_src, &tun_dst, NULL); + &tun_src, &tun_dst, NULL, + IPSEC_UDP_PORT_NONE); else rv = ipsec_sa_unlock_id (id); @@ -852,14 +853,14 @@ create_ipsec_tunnel_command_fn (vlib_main_t * vm, local_spi, IPSEC_PROTOCOL_ESP, crypto_alg, &lck, integ_alg, &lik, flags, table_id, clib_host_to_net_u32 (salt), &local_ip, - &remote_ip, NULL); + &remote_ip, NULL, IPSEC_UDP_PORT_NONE); rv |= ipsec_sa_add_and_lock (ipsec_tun_mk_remote_sa_id (sw_if_index), remote_spi, IPSEC_PROTOCOL_ESP, crypto_alg, &rck, integ_alg, &rik, (flags | IPSEC_SA_FLAG_IS_INBOUND), table_id, clib_host_to_net_u32 (salt), &remote_ip, - &local_ip, NULL); + &local_ip, NULL, IPSEC_UDP_PORT_NONE); rv |= ipsec_tun_protect_update_one (sw_if_index, &nh, ipsec_tun_mk_local_sa_id (sw_if_index), diff --git a/src/vnet/ipsec/ipsec_sa.c b/src/vnet/ipsec/ipsec_sa.c index 4401c2e3d74..0e1e63d0db4 100644 --- a/src/vnet/ipsec/ipsec_sa.c +++ b/src/vnet/ipsec/ipsec_sa.c @@ -135,7 +135,8 @@ ipsec_sa_add_and_lock (u32 id, u32 tx_table_id, u32 salt, const ip46_address_t * tun_src, - const ip46_address_t * tun_dst, u32 * sa_out_index) + const ip46_address_t * tun_dst, u32 * sa_out_index, + u16 dst_port) { vlib_main_t *vm = vlib_get_main (); ipsec_main_t *im = &ipsec_main; @@ -269,8 +270,16 @@ ipsec_sa_add_and_lock (u32 id, if (ipsec_sa_is_set_UDP_ENCAP (sa)) { - sa->udp_hdr.src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipsec); - sa->udp_hdr.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipsec); + if (dst_port == IPSEC_UDP_PORT_NONE) + { + sa->udp_hdr.src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipsec); + sa->udp_hdr.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipsec); + } + else + { + sa->udp_hdr.src_port = clib_host_to_net_u16 (dst_port); + sa->udp_hdr.dst_port = clib_host_to_net_u16 (dst_port); + } } hash_set (im->sa_index_by_sa_id, sa->id, sa_index); diff --git a/src/vnet/ipsec/ipsec_sa.h b/src/vnet/ipsec/ipsec_sa.h index e0d74e1309e..0997eb7d8bc 100644 --- a/src/vnet/ipsec/ipsec_sa.h +++ b/src/vnet/ipsec/ipsec_sa.h @@ -16,6 +16,7 @@ #define __IPSEC_SPD_SA_H__ #include +#include #include #include @@ -209,7 +210,7 @@ extern int ipsec_sa_add_and_lock (u32 id, u32 salt, const ip46_address_t * tunnel_src_addr, const ip46_address_t * tunnel_dst_addr, - u32 * sa_index); + u32 * sa_index, u16 dst_port); extern index_t ipsec_sa_find_and_lock (u32 id); extern int ipsec_sa_unlock_id (u32 id); extern void ipsec_sa_unlock (index_t sai); @@ -233,6 +234,8 @@ extern uword unformat_ipsec_integ_alg (unformat_input_t * input, va_list * args); extern uword unformat_ipsec_key (unformat_input_t * input, va_list * args); +#define IPSEC_UDP_PORT_NONE ((u16)~0) + /* * Anti Replay definitions */ -- 2.16.6