From 6ba4e41d33ffda2596d9d4b3a1d7fdd3c9a6b870 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Mon, 19 Oct 2020 09:59:41 +0000 Subject: [PATCH] ipsec: support for multipoint on ipsec interfaces Type: feature Signed-off-by: Neale Ranns Change-Id: Iae9fe35cfbce4c675fa25e0800c0f4629a83e012 --- src/vnet/gre/interface.c | 2 +- src/vnet/ipsec/ipsec_format.c | 15 +++++ src/vnet/ipsec/ipsec_itf.c | 45 +++++++++++++-- src/vnet/ipsec/ipsec_itf.h | 4 ++ src/vnet/ipsec/ipsec_tun.c | 5 +- test/test_ipsec_tun_if_esp.py | 125 +++++++++++++++++++++++++++++++++++++++++- test/vpp_ipsec.py | 7 ++- 7 files changed, 192 insertions(+), 11 deletions(-) diff --git a/src/vnet/gre/interface.c b/src/vnet/gre/interface.c index 92e4f7469ac..ad0efde4345 100644 --- a/src/vnet/gre/interface.c +++ b/src/vnet/gre/interface.c @@ -202,7 +202,7 @@ gre_teib_mk_key (const gre_tunnel_t * t, } /** - * An NHRP entry has been added + * An TEIB entry has been added */ static void gre_teib_entry_added (const teib_entry_t * ne) diff --git a/src/vnet/ipsec/ipsec_format.c b/src/vnet/ipsec/ipsec_format.c index 8fdc844aec3..6781fe580c1 100644 --- a/src/vnet/ipsec/ipsec_format.c +++ b/src/vnet/ipsec/ipsec_format.c @@ -23,6 +23,7 @@ #include #include +#include u8 * format_ipsec_policy_action (u8 * s, va_list * args) @@ -427,6 +428,20 @@ format_ipsec6_tunnel_kv (u8 * s, va_list * args) return (s); } +u8 * +format_ipsec_itf (u8 * s, va_list * a) +{ + index_t ii = va_arg (*a, index_t); + ipsec_itf_t *itf; + + itf = ipsec_itf_get (ii); + s = format (s, "[%d] %U %U", + ii, format_vnet_sw_if_index_name, vnet_get_main (), + itf->ii_sw_if_index, format_tunnel_mode, itf->ii_mode); + + return (s); +} + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vnet/ipsec/ipsec_itf.c b/src/vnet/ipsec/ipsec_itf.c index 46095ce07fe..ff06a579f2e 100644 --- a/src/vnet/ipsec/ipsec_itf.c +++ b/src/vnet/ipsec/ipsec_itf.c @@ -29,6 +29,12 @@ static ipsec_itf_t *ipsec_itf_pool; static u32 *ipsec_itf_index_by_sw_if_index; +ipsec_itf_t * +ipsec_itf_get (index_t ii) +{ + return (pool_elt_at_index (ipsec_itf_pool, ii)); +} + static ipsec_itf_t * ipsec_itf_find_by_sw_if_index (u32 sw_if_index) { @@ -197,6 +203,11 @@ VNET_HW_INTERFACE_CLASS(ipsec_hw_interface_class) = { .update_adjacency = ipsec_itf_update_adj, .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P, }; +VNET_HW_INTERFACE_CLASS(ipsec_p2mp_hw_interface_class) = { + .name = "IPSec", + .build_rewrite = ipsec_itf_build_rewrite_i, + .update_adjacency = ipsec_itf_update_adj, +}; /* *INDENT-ON* */ /* @@ -276,9 +287,6 @@ ipsec_itf_create (u32 user_instance, tunnel_mode_t mode, u32 * sw_if_indexp) *sw_if_indexp = (u32) ~ 0; - if (mode != TUNNEL_MODE_P2P) - return VNET_API_ERROR_UNSUPPORTED; - /* * Allocate a ipsec_itf instance. Either select on dynamically * or try to use the desired user_instance number. @@ -298,7 +306,9 @@ ipsec_itf_create (u32 user_instance, tunnel_mode_t mode, u32 * sw_if_indexp) hw_if_index = vnet_register_interface (vnm, ipsec_itf_device_class.index, ipsec_itf->ii_user_instance, - ipsec_hw_interface_class.index, + (mode == TUNNEL_MODE_P2P ? + ipsec_hw_interface_class.index : + ipsec_p2mp_hw_interface_class.index), t_idx); hi = vnet_get_hw_interface (vnm, hw_if_index); @@ -450,6 +460,33 @@ VLIB_CLI_COMMAND (ipsec_itf_delete_command, static) = { }; /* *INDENT-ON* */ +static clib_error_t * +ipsec_interface_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + index_t ii; + + /* *INDENT-OFF* */ + pool_foreach_index (ii, ipsec_itf_pool, + ({ + vlib_cli_output (vm, "%U", format_ipsec_itf, ii); + })); + /* *INDENT-ON* */ + + return NULL; +} + +/** + * show IPSEC tunnel protection hash tables + */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ipsec_interface_show_node, static) = +{ + .path = "show ipsec interface", + .function = ipsec_interface_show, + .short_help = "show ipsec interface", +}; +/* *INDENT-ON* */ /* * fd.io coding-style-patch-verification: ON diff --git a/src/vnet/ipsec/ipsec_itf.h b/src/vnet/ipsec/ipsec_itf.h index 93e03f7b477..ab317e10332 100644 --- a/src/vnet/ipsec/ipsec_itf.h +++ b/src/vnet/ipsec/ipsec_itf.h @@ -106,6 +106,10 @@ extern int ipsec_itf_delete (u32 sw_if_index); extern void ipsec_itf_adj_stack (adj_index_t ai, u32 sai); extern void ipsec_itf_adj_unstack (adj_index_t ai); +extern u8 *format_ipsec_itf (u8 * s, va_list * a); + +extern ipsec_itf_t *ipsec_itf_get (index_t ii); + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vnet/ipsec/ipsec_tun.c b/src/vnet/ipsec/ipsec_tun.c index c4c8fa7d262..9116df5d7e3 100644 --- a/src/vnet/ipsec/ipsec_tun.c +++ b/src/vnet/ipsec/ipsec_tun.c @@ -732,8 +732,9 @@ ipsec_tun_protect_update (u32 sw_if_index, if (ip46_address_is_zero (&itp->itp_tun.src)) { - /* must be one of thos pesky ipsec interfaces that has no encap. - * the encap then MUST comefrom the tunnel mode SA. + /* + * must be one of those pesky ipsec interfaces that has no encap. + * the encap then MUST come from the tunnel mode SA. */ ipsec_sa_t *sa; diff --git a/test/test_ipsec_tun_if_esp.py b/test/test_ipsec_tun_if_esp.py index 9d01b93114e..fb31f221aed 100644 --- a/test/test_ipsec_tun_if_esp.py +++ b/test/test_ipsec_tun_if_esp.py @@ -1576,7 +1576,7 @@ class TestIpsecMGreIfEspTra4(TemplateIpsec, IpsecTun4): # setup some SAs for several next-hops on the interface self.multi_params = [] - for ii in range(1): + for ii in range(N_NHS): p = copy.copy(self.ipv4_params) p.remote_tun_if_host = "1.1.1.%d" % (ii + 1) @@ -2810,5 +2810,128 @@ class TestIpsecItf6(TemplateIpsec, self.unconfig_network(p) +class TestIpsecMIfEsp4(TemplateIpsec, IpsecTun4): + """ Ipsec P2MP ESP v4 tests """ + tun4_encrypt_node_name = "esp4-encrypt-tun" + tun4_decrypt_node_name = "esp4-decrypt-tun" + encryption_type = ESP + + def gen_encrypt_pkts(self, p, 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.pg1.local_ip4, + dst=self.pg1.remote_ip4) / + UDP(sport=1144, dport=2233) / + Raw(b'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=dst) / + UDP(sport=1144, dport=2233) / + Raw(b'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) + e = pkt[IP] + self.assertEqual(e[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 setUp(self): + super(TestIpsecMIfEsp4, self).setUp() + + N_NHS = 16 + self.tun_if = self.pg0 + p = self.ipv4_params + p.tun_if = VppIpsecInterface(self, + mode=(VppEnum.vl_api_tunnel_mode_t. + TUNNEL_API_MODE_MP)) + p.tun_if.add_vpp_config() + p.tun_if.admin_up() + p.tun_if.config_ip4() + p.tun_if.generate_remote_hosts(N_NHS) + self.pg0.generate_remote_hosts(N_NHS) + self.pg0.configure_ipv4_neighbors() + + # setup some SAs for several next-hops on the interface + self.multi_params = [] + + for ii in range(N_NHS): + p = copy.copy(self.ipv4_params) + + p.remote_tun_if_host = "1.1.1.%d" % (ii + 1) + p.scapy_tun_sa_id = p.scapy_tun_sa_id + ii + p.scapy_tun_spi = p.scapy_tun_spi + ii + p.vpp_tun_sa_id = p.vpp_tun_sa_id + ii + p.vpp_tun_spi = p.vpp_tun_spi + ii + + p.scapy_tra_sa_id = p.scapy_tra_sa_id + ii + p.scapy_tra_spi = p.scapy_tra_spi + ii + p.vpp_tra_sa_id = p.vpp_tra_sa_id + ii + p.vpp_tra_spi = p.vpp_tra_spi + ii + 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_hosts[ii].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_hosts[ii].ip4, + self.pg0.local_ip4) + p.tun_sa_in.add_vpp_config() + + p.tun_protect = VppIpsecTunProtect( + self, + p.tun_if, + p.tun_sa_out, + [p.tun_sa_in], + nh=p.tun_if.remote_hosts[ii].ip4) + p.tun_protect.add_vpp_config() + config_tun_params(p, self.encryption_type, None, + self.pg0.local_ip4, + self.pg0.remote_hosts[ii].ip4) + self.multi_params.append(p) + + VppIpRoute(self, p.remote_tun_if_host, 32, + [VppRoutePath(p.tun_if.remote_hosts[ii].ip4, + p.tun_if.sw_if_index)]).add_vpp_config() + + p.tun_dst = self.pg0.remote_hosts[ii].ip4 + + def tearDown(self): + p = self.ipv4_params + p.tun_if.unconfig_ip4() + super(TestIpsecMIfEsp4, self).tearDown() + + def test_tun_44(self): + """P2MP IPSEC 44""" + N_PKTS = 63 + for p in self.multi_params: + self.verify_tun_44(p, count=N_PKTS) + + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_ipsec.py b/test/vpp_ipsec.py index f9dcdf09f1a..479870621e0 100644 --- a/test/vpp_ipsec.py +++ b/test/vpp_ipsec.py @@ -379,9 +379,10 @@ class VppIpsecInterface(VppInterface): def __init__(self, test, mode=None, instance=0xffffffff): super(VppIpsecInterface, self).__init__(test) - # only p2p mode is supported currently - self.mode = (VppEnum.vl_api_tunnel_mode_t. - TUNNEL_API_MODE_P2P) + self.mode = mode + if not self.mode: + self.mode = (VppEnum.vl_api_tunnel_mode_t. + TUNNEL_API_MODE_P2P) self.instance = instance def add_vpp_config(self): -- 2.16.6