diff options
-rw-r--r-- | src/plugins/ikev2/ikev2.c | 6 | ||||
-rw-r--r-- | src/plugins/linux-cp/lcp_interface.c | 2 | ||||
-rw-r--r-- | src/vnet/ipsec/esp_encrypt.c | 24 | ||||
-rw-r--r-- | src/vnet/ipsec/ipsec.c | 42 | ||||
-rw-r--r-- | src/vnet/ipsec/ipsec.h | 4 | ||||
-rw-r--r-- | src/vnet/ipsec/ipsec_punt.h | 3 | ||||
-rw-r--r-- | src/vnet/ipsec/ipsec_sa.c | 6 | ||||
-rw-r--r-- | src/vnet/ipsec/ipsec_tun.c | 14 | ||||
-rw-r--r-- | src/vnet/ipsec/ipsec_tun_in.c | 51 | ||||
-rw-r--r-- | test/template_ipsec.py | 34 | ||||
-rw-r--r-- | test/test_ipsec_tun_if_esp.py | 126 |
11 files changed, 261 insertions, 51 deletions
diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c index c1b7efdc98e..32331ffa6e4 100644 --- a/src/plugins/ikev2/ikev2.c +++ b/src/plugins/ikev2/ikev2.c @@ -3857,7 +3857,8 @@ ikev2_set_local_key (vlib_main_t * vm, u8 * file) static vnet_api_error_t ikev2_register_udp_port (ikev2_profile_t *p, u16 port) { - ipsec_register_udp_port (port); + ipsec_register_udp_port (port, 0 /* is_ip4 */); + ipsec_register_udp_port (port, 1 /* is_ip4 */); p->ipsec_over_udp_port = port; return 0; } @@ -3868,7 +3869,8 @@ ikev2_unregister_udp_port (ikev2_profile_t *p) if (p->ipsec_over_udp_port == IPSEC_UDP_PORT_NONE) return; - ipsec_unregister_udp_port (p->ipsec_over_udp_port); + ipsec_unregister_udp_port (p->ipsec_over_udp_port, 0 /* is_ip4 */); + ipsec_unregister_udp_port (p->ipsec_over_udp_port, 1 /* is_ip4 */); p->ipsec_over_udp_port = IPSEC_UDP_PORT_NONE; } diff --git a/src/plugins/linux-cp/lcp_interface.c b/src/plugins/linux-cp/lcp_interface.c index 82675903ec6..4ca7c0ca631 100644 --- a/src/plugins/linux-cp/lcp_interface.c +++ b/src/plugins/linux-cp/lcp_interface.c @@ -1192,6 +1192,8 @@ lcp_interface_init (vlib_main_t *vm) /* punt IKE */ vlib_punt_register (punt_hdl, ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0], "linux-cp-punt"); + vlib_punt_register (punt_hdl, ipsec_punt_reason[IPSEC_PUNT_IP6_SPI_UDP_0], + "linux-cp-punt"); /* punt all unknown ports */ udp_punt_unknown (vm, 0, 1); diff --git a/src/vnet/ipsec/esp_encrypt.c b/src/vnet/ipsec/esp_encrypt.c index d28f4f5e425..4ed3bf72c3f 100644 --- a/src/vnet/ipsec/esp_encrypt.c +++ b/src/vnet/ipsec/esp_encrypt.c @@ -887,42 +887,40 @@ esp_encrypt_inline (vlib_main_t *vm, vlib_node_runtime_t *node, else l2_len = 0; + u16 len; + len = payload_len_total + hdr_len - l2_len; + if (VNET_LINK_IP6 == lt) { ip6_header_t *ip6 = (ip6_header_t *) (old_ip_hdr); if (PREDICT_TRUE (NULL == ext_hdr)) { *next_hdr_ptr = ip6->protocol; - ip6->protocol = IP_PROTOCOL_IPSEC_ESP; + ip6->protocol = + (udp) ? IP_PROTOCOL_UDP : IP_PROTOCOL_IPSEC_ESP; } else { *next_hdr_ptr = ext_hdr->next_hdr; - ext_hdr->next_hdr = IP_PROTOCOL_IPSEC_ESP; + ext_hdr->next_hdr = + (udp) ? IP_PROTOCOL_UDP : IP_PROTOCOL_IPSEC_ESP; } ip6->payload_length = - clib_host_to_net_u16 (payload_len_total + hdr_len - l2_len - - sizeof (ip6_header_t)); + clib_host_to_net_u16 (len - sizeof (ip6_header_t)); } else if (VNET_LINK_IP4 == lt) { - u16 len; ip4_header_t *ip4 = (ip4_header_t *) (old_ip_hdr); *next_hdr_ptr = ip4->protocol; - len = payload_len_total + hdr_len - l2_len; - if (udp) - { - esp_update_ip4_hdr (ip4, len, /* is_transport */ 1, 1); - udp_len = len - ip_len; - } - else - esp_update_ip4_hdr (ip4, len, /* is_transport */ 1, 0); + esp_update_ip4_hdr (ip4, len, /* is_transport */ 1, + (udp != NULL)); } clib_memcpy_le64 (ip_hdr, old_ip_hdr, ip_len); if (udp) { + udp_len = len - ip_len; esp_fill_udp_hdr (sa0, udp, udp_len); } diff --git a/src/vnet/ipsec/ipsec.c b/src/vnet/ipsec/ipsec.c index 3c22fbb87dd..2dd077a74a1 100644 --- a/src/vnet/ipsec/ipsec.c +++ b/src/vnet/ipsec/ipsec.c @@ -182,14 +182,24 @@ ipsec_add_node (vlib_main_t * vm, const char *node_name, *out_next_index = vlib_node_add_next (vm, prev_node->index, node->index); } +static inline uword +ipsec_udp_registration_key (u16 port, u8 is_ip4) +{ + uword key = (is_ip4) ? AF_IP4 : AF_IP6; + + key |= (uword) (port << 16); + return key; +} + void -ipsec_unregister_udp_port (u16 port) +ipsec_unregister_udp_port (u16 port, u8 is_ip4) { ipsec_main_t *im = &ipsec_main; u32 n_regs; - uword *p; + uword *p, key; - p = hash_get (im->udp_port_registrations, port); + key = ipsec_udp_registration_key (port, is_ip4); + p = hash_get (im->udp_port_registrations, key); ASSERT (p); @@ -197,33 +207,35 @@ ipsec_unregister_udp_port (u16 port) if (0 == --n_regs) { - udp_unregister_dst_port (vlib_get_main (), port, 1); - hash_unset (im->udp_port_registrations, port); + udp_unregister_dst_port (vlib_get_main (), port, is_ip4); + hash_unset (im->udp_port_registrations, key); } else { - hash_unset (im->udp_port_registrations, port); - hash_set (im->udp_port_registrations, port, n_regs); + hash_unset (im->udp_port_registrations, key); + hash_set (im->udp_port_registrations, key, n_regs); } } void -ipsec_register_udp_port (u16 port) +ipsec_register_udp_port (u16 port, u8 is_ip4) { ipsec_main_t *im = &ipsec_main; - u32 n_regs; - uword *p; + u32 n_regs, node_index; + uword *p, key; - p = hash_get (im->udp_port_registrations, port); + key = ipsec_udp_registration_key (port, is_ip4); + node_index = + (is_ip4) ? ipsec4_tun_input_node.index : ipsec6_tun_input_node.index; + p = hash_get (im->udp_port_registrations, key); n_regs = (p ? p[0] : 0); if (0 == n_regs++) - udp_register_dst_port (vlib_get_main (), port, - ipsec4_tun_input_node.index, 1); + udp_register_dst_port (vlib_get_main (), port, node_index, is_ip4); - hash_unset (im->udp_port_registrations, port); - hash_set (im->udp_port_registrations, port, n_regs); + hash_unset (im->udp_port_registrations, key); + hash_set (im->udp_port_registrations, key, n_regs); } u32 diff --git a/src/vnet/ipsec/ipsec.h b/src/vnet/ipsec/ipsec.h index fc7b6cd2454..06bb299988b 100644 --- a/src/vnet/ipsec/ipsec.h +++ b/src/vnet/ipsec/ipsec.h @@ -364,8 +364,8 @@ int ipsec_select_esp_backend (ipsec_main_t * im, u32 esp_backend_idx); clib_error_t *ipsec_rsc_in_use (ipsec_main_t * im); void ipsec_set_async_mode (u32 is_enabled); -extern void ipsec_register_udp_port (u16 udp_port); -extern void ipsec_unregister_udp_port (u16 udp_port); +extern void ipsec_register_udp_port (u16 udp_port, u8 is_ip4); +extern void ipsec_unregister_udp_port (u16 udp_port, u8 is_ip4); extern clib_error_t *ipsec_register_next_header (vlib_main_t *vm, u8 next_header, diff --git a/src/vnet/ipsec/ipsec_punt.h b/src/vnet/ipsec/ipsec_punt.h index afed908bffb..9b9fc803391 100644 --- a/src/vnet/ipsec/ipsec_punt.h +++ b/src/vnet/ipsec/ipsec_punt.h @@ -20,7 +20,8 @@ #define foreach_ipsec_punt_reason \ _ (IP4_SPI_UDP_0, "ipsec4-spi-o-udp-0", IP4_PACKET) \ _ (IP4_NO_SUCH_TUNNEL, "ipsec4-no-such-tunnel", IP4_PACKET) \ - _ (IP6_NO_SUCH_TUNNEL, "ipsec6-no-such-tunnel", IP6_PACKET) + _ (IP6_NO_SUCH_TUNNEL, "ipsec6-no-such-tunnel", IP6_PACKET) \ + _ (IP6_SPI_UDP_0, "ipsec6-spi-o-udp-0", IP6_PACKET) typedef enum ipsec_punt_reason_t_ { diff --git a/src/vnet/ipsec/ipsec_sa.c b/src/vnet/ipsec/ipsec_sa.c index 387d8a747a3..5c80545bb21 100644 --- a/src/vnet/ipsec/ipsec_sa.c +++ b/src/vnet/ipsec/ipsec_sa.c @@ -325,7 +325,8 @@ ipsec_sa_add_and_lock (u32 id, u32 spi, ipsec_protocol_t proto, sa->udp_hdr.src_port = clib_host_to_net_u16 (src_port); if (ipsec_sa_is_set_IS_INBOUND (sa)) - ipsec_register_udp_port (clib_host_to_net_u16 (sa->udp_hdr.dst_port)); + ipsec_register_udp_port (clib_host_to_net_u16 (sa->udp_hdr.dst_port), + !ipsec_sa_is_set_IS_TUNNEL_V6 (sa)); } hash_set (im->sa_index_by_sa_id, sa->id, sa_index); @@ -353,7 +354,8 @@ ipsec_sa_del (ipsec_sa_t * sa) if (ipsec_sa_is_set_IS_ASYNC (sa)) vnet_crypto_request_async_mode (0); if (ipsec_sa_is_set_UDP_ENCAP (sa) && ipsec_sa_is_set_IS_INBOUND (sa)) - ipsec_unregister_udp_port (clib_net_to_host_u16 (sa->udp_hdr.dst_port)); + ipsec_unregister_udp_port (clib_net_to_host_u16 (sa->udp_hdr.dst_port), + !ipsec_sa_is_set_IS_TUNNEL_V6 (sa)); if (ipsec_sa_is_set_IS_TUNNEL (sa) && !ipsec_sa_is_set_IS_INBOUND (sa)) dpo_reset (&sa->dpo); diff --git a/src/vnet/ipsec/ipsec_tun.c b/src/vnet/ipsec/ipsec_tun.c index 543be8a7faa..82f5a11d26f 100644 --- a/src/vnet/ipsec/ipsec_tun.c +++ b/src/vnet/ipsec/ipsec_tun.c @@ -101,14 +101,12 @@ ipsec_tun_register_nodes (ip_address_family_t af) if (0 == ipsec_tun_node_regs[af]++) { if (AF_IP4 == af) - { - ipsec_register_udp_port (UDP_DST_PORT_ipsec); - ip4_register_protocol (IP_PROTOCOL_IPSEC_ESP, - ipsec4_tun_input_node.index); - } + ip4_register_protocol (IP_PROTOCOL_IPSEC_ESP, + ipsec4_tun_input_node.index); else ip6_register_protocol (IP_PROTOCOL_IPSEC_ESP, ipsec6_tun_input_node.index); + ipsec_register_udp_port (UDP_DST_PORT_ipsec, (AF_IP4 == af)); } } @@ -119,12 +117,10 @@ ipsec_tun_unregister_nodes (ip_address_family_t af) if (0 == --ipsec_tun_node_regs[af]) { if (AF_IP4 == af) - { - ipsec_unregister_udp_port (UDP_DST_PORT_ipsec); - ip4_unregister_protocol (IP_PROTOCOL_IPSEC_ESP); - } + ip4_unregister_protocol (IP_PROTOCOL_IPSEC_ESP); else ip6_unregister_protocol (IP_PROTOCOL_IPSEC_ESP); + ipsec_unregister_udp_port (UDP_DST_PORT_ipsec, (AF_IP4 == af)); } } diff --git a/src/vnet/ipsec/ipsec_tun_in.c b/src/vnet/ipsec/ipsec_tun_in.c index 8e97fbcc740..eec03625906 100644 --- a/src/vnet/ipsec/ipsec_tun_in.c +++ b/src/vnet/ipsec/ipsec_tun_in.c @@ -86,11 +86,21 @@ ipsec_ip4_if_no_tunnel (vlib_node_runtime_t * node, } always_inline u16 -ipsec_ip6_if_no_tunnel (vlib_node_runtime_t * node, - vlib_buffer_t * b, const esp_header_t * esp) +ipsec_ip6_if_no_tunnel (vlib_node_runtime_t *node, vlib_buffer_t *b, + const esp_header_t *esp, const ip6_header_t *ip6) { - b->error = node->errors[IPSEC_TUN_ERROR_NO_TUNNEL]; - b->punt_reason = ipsec_punt_reason[IPSEC_PUNT_IP6_NO_SUCH_TUNNEL]; + if (PREDICT_FALSE (0 == esp->spi)) + { + b->error = node->errors[IPSEC_TUN_ERROR_SPI_0]; + b->punt_reason = ipsec_punt_reason[(ip6->protocol == IP_PROTOCOL_UDP ? + IPSEC_PUNT_IP6_SPI_UDP_0 : + IPSEC_PUNT_IP6_NO_SUCH_TUNNEL)]; + } + else + { + b->error = node->errors[IPSEC_TUN_ERROR_NO_TUNNEL]; + b->punt_reason = ipsec_punt_reason[IPSEC_PUNT_IP6_NO_SUCH_TUNNEL]; + } return VNET_DEVICE_INPUT_NEXT_PUNT; } @@ -164,8 +174,35 @@ ipsec_tun_protect_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, if (is_ip6) { ip60 = (ip6_header_t *) ip40; - esp0 = (esp_header_t *) (ip60 + 1); - buf_rewind0 = hdr_sz0 = sizeof (ip6_header_t); + if (ip60->protocol == IP_PROTOCOL_UDP) + { + /* NAT UDP port 4500 case, don't advance any more */ + esp0 = (esp_header_t *) ((u8 *) ip60 + sizeof (ip6_header_t) + + sizeof (udp_header_t)); + hdr_sz0 = 0; + buf_rewind0 = sizeof (ip6_header_t) + sizeof (udp_header_t); + + const udp_header_t *udp0 = + (udp_header_t *) ((u8 *) ip60 + sizeof (ip6_header_t)); + + /* length 9 = sizeof(udp_header) + 1 byte of special SPI */ + if (clib_net_to_host_u16 (udp0->length) == 9 && + esp0->spi_bytes[0] == 0xff) + { + b[0]->error = node->errors[IPSEC_TUN_ERROR_NAT_KEEPALIVE]; + + next[0] = VNET_DEVICE_INPUT_NEXT_IP6_DROP; + len0 = 0; + + vlib_buffer_advance (b[0], -buf_rewind0); + goto trace00; + } + } + else + { + esp0 = (esp_header_t *) (ip60 + 1); + buf_rewind0 = hdr_sz0 = sizeof (ip6_header_t); + } } else { @@ -240,7 +277,7 @@ ipsec_tun_protect_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, } else { - next[0] = ipsec_ip6_if_no_tunnel (node, b[0], esp0); + next[0] = ipsec_ip6_if_no_tunnel (node, b[0], esp0, ip60); n_no_tunnel++; goto trace00; } diff --git a/test/template_ipsec.py b/test/template_ipsec.py index ce188622a9e..def31cbebbd 100644 --- a/test/template_ipsec.py +++ b/test/template_ipsec.py @@ -1695,6 +1695,40 @@ class IpsecTun6(object): self.logger.info(self.vapi.ppcli("show ipsec all")) self.verify_counters6(p, p, count) + def verify_keepalive(self, p): + # the sizeof Raw is calculated to pad to the minimum ehternet + # frame size of 64 btyes + pkt = ( + Ether(src=self.tun_if.remote_mac, dst=self.tun_if.local_mac) + / IPv6(src=p.remote_tun_if_host, dst=self.tun_if.local_ip6) + / UDP(sport=333, dport=4500) + / Raw(b"\xff") + / Padding(0 * 1) + ) + self.send_and_assert_no_replies(self.tun_if, pkt * 31) + self.assert_error_counter_equal( + "/err/%s/nat_keepalive" % self.tun6_input_node, 31 + ) + + pkt = ( + Ether(src=self.tun_if.remote_mac, dst=self.tun_if.local_mac) + / IPv6(src=p.remote_tun_if_host, dst=self.tun_if.local_ip6) + / UDP(sport=333, dport=4500) + / Raw(b"\xfe") + ) + self.send_and_assert_no_replies(self.tun_if, pkt * 31) + self.assert_error_counter_equal("/err/%s/too_short" % self.tun6_input_node, 31) + + pkt = ( + Ether(src=self.tun_if.remote_mac, dst=self.tun_if.local_mac) + / IPv6(src=p.remote_tun_if_host, dst=self.tun_if.local_ip6) + / UDP(sport=333, dport=4500) + / Raw(b"\xfe") + / Padding(0 * 21) + ) + self.send_and_assert_no_replies(self.tun_if, pkt * 31) + self.assert_error_counter_equal("/err/%s/too_short" % self.tun6_input_node, 62) + class IpsecTun6Tests(IpsecTun6): """UT test methods for Tunnel v6""" diff --git a/test/test_ipsec_tun_if_esp.py b/test/test_ipsec_tun_if_esp.py index d10ad216bd6..61a66d40a4e 100644 --- a/test/test_ipsec_tun_if_esp.py +++ b/test/test_ipsec_tun_if_esp.py @@ -561,6 +561,132 @@ class TemplateIpsec6TunIfEsp(TemplateIpsec6TunProtect, TemplateIpsec): super(TemplateIpsec6TunIfEsp, self).tearDown() +class TemplateIpsec6TunIfEspUdp(TemplateIpsec6TunProtect, TemplateIpsec): + """IPsec6 UDP tunnel interface tests""" + + tun4_encrypt_node_name = "esp6-encrypt-tun" + tun4_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"] + encryption_type = ESP + + @classmethod + def setUpClass(cls): + super(TemplateIpsec6TunIfEspUdp, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TemplateIpsec6TunIfEspUdp, cls).tearDownClass() + + def verify_encrypted(self, p, sa, rxs): + for rx in rxs: + try: + # ensure the UDP ports are correct before we decrypt + # which strips them + self.assertTrue(rx.haslayer(UDP)) + self.assert_equal(rx[UDP].sport, p.nat_header.sport) + self.assert_equal(rx[UDP].dport, 4500) + + 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, "1111:1111:1111:1111:1111:1111:1111:1111" + ) + self.assert_equal(pkt[IP].src, self.pg1.remote_ip6) + except (IndexError, AssertionError): + self.logger.debug(ppp("Unexpected packet:", rx)) + try: + self.logger.debug(ppp("Decrypted packet:", pkt)) + except: + pass + raise + + def config_sa_tra(self, p): + config_tun_params(p, self.encryption_type, p.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, + flags=p.flags, + udp_src=p.nat_header.sport, + udp_dst=p.nat_header.dport, + ) + 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, + flags=p.flags, + udp_src=p.nat_header.sport, + udp_dst=p.nat_header.dport, + ) + p.tun_sa_in.add_vpp_config() + + def setUp(self): + super(TemplateIpsec6TunIfEspUdp, self).setUp() + + p = self.ipv6_params + p.flags = VppEnum.vl_api_ipsec_sad_flags_t.IPSEC_API_SAD_FLAG_UDP_ENCAP + p.nat_header = UDP(sport=5454, dport=4500) + + self.tun_if = self.pg0 + + self.config_network(p) + self.config_sa_tra(p) + self.config_protect(p) + + def tearDown(self): + super(TemplateIpsec6TunIfEspUdp, self).tearDown() + + +class TestIpsec6TunIfEspUdp(TemplateIpsec6TunIfEspUdp, IpsecTun6Tests): + """Ipsec ESP 6 UDP tests""" + + tun6_input_node = "ipsec6-tun-input" + tun6_encrypt_node_name = "esp6-encrypt-tun" + tun6_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"] + + def setUp(self): + super(TestIpsec6TunIfEspUdp, self).setUp() + + def test_keepalive(self): + """IPSEC6 NAT Keepalive""" + self.verify_keepalive(self.ipv6_params) + + +class TestIpsec6TunIfEspUdpGCM(TemplateIpsec6TunIfEspUdp, IpsecTun6Tests): + """Ipsec ESP 6 UDP GCM tests""" + + tun6_input_node = "ipsec6-tun-input" + tun6_encrypt_node_name = "esp6-encrypt-tun" + tun6_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"] + + def setUp(self): + super(TestIpsec6TunIfEspUdpGCM, self).setUp() + p = self.ipv6_params + p.auth_algo_vpp_id = VppEnum.vl_api_ipsec_integ_alg_t.IPSEC_API_INTEG_ALG_NONE + p.crypt_algo_vpp_id = ( + VppEnum.vl_api_ipsec_crypto_alg_t.IPSEC_API_CRYPTO_ALG_AES_GCM_256 + ) + p.crypt_algo = "AES-GCM" + p.auth_algo = "NULL" + p.crypt_key = b"JPjyOWBeVEQiMe7hJPjyOWBeVEQiMe7h" + p.salt = 0 + + class TestIpsec6TunIfEsp1(TemplateIpsec6TunIfEsp, IpsecTun6Tests): """Ipsec ESP - TUN tests""" |