X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Ftemplate_ipsec.py;h=57c84342cb71a6e1d20a94d4aebf3dfe7cd1ee0b;hb=b8e7a45d56be9f3e11b07b82fd899160e2af1bf1;hp=398a6bb0a230900617357a2bd490d9528220c7a8;hpb=02950406c49a743f631395ed52073921744e1afd;p=vpp.git diff --git a/test/template_ipsec.py b/test/template_ipsec.py index 398a6bb0a23..57c84342cb7 100644 --- a/test/template_ipsec.py +++ b/test/template_ipsec.py @@ -5,7 +5,7 @@ import struct from scapy.layers.inet import IP, ICMP, TCP, UDP from scapy.layers.ipsec import SecurityAssociation, ESP from scapy.layers.l2 import Ether -from scapy.packet import Raw +from scapy.packet import raw, Raw from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, IPv6ExtHdrHopByHop, \ IPv6ExtHdrFragment, IPv6ExtHdrDestOpt @@ -15,7 +15,7 @@ from util import ppp, reassemble4, fragment_rfc791, fragment_rfc8200 from vpp_papi import VppEnum -class IPsecIPv4Params(object): +class IPsecIPv4Params: addr_type = socket.AF_INET addr_any = "0.0.0.0" @@ -27,15 +27,20 @@ class IPsecIPv4Params(object): self.remote_tun_if_host = '1.1.1.1' self.remote_tun_if_host6 = '1111::1' - self.scapy_tun_sa_id = 10 - self.scapy_tun_spi = 1001 - self.vpp_tun_sa_id = 20 - self.vpp_tun_spi = 1000 + self.scapy_tun_sa_id = 100 + self.scapy_tun_spi = 1000 + self.vpp_tun_sa_id = 200 + self.vpp_tun_spi = 2000 - self.scapy_tra_sa_id = 30 - self.scapy_tra_spi = 2001 - self.vpp_tra_sa_id = 40 - self.vpp_tra_spi = 2000 + self.scapy_tra_sa_id = 300 + self.scapy_tra_spi = 3000 + self.vpp_tra_sa_id = 400 + self.vpp_tra_spi = 4000 + + self.outer_hop_limit = 64 + self.inner_hop_limit = 255 + self.outer_flow_label = 0 + self.inner_flow_label = 0x12345 self.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t. IPSEC_API_INTEG_ALG_SHA1_96) @@ -49,9 +54,13 @@ class IPsecIPv4Params(object): self.salt = 0 self.flags = 0 self.nat_header = None + self.tun_flags = (VppEnum.vl_api_tunnel_encap_decap_flags_t. + TUNNEL_API_ENCAP_DECAP_FLAG_NONE) + self.dscp = 0 + self.async_mode = False -class IPsecIPv6Params(object): +class IPsecIPv6Params: addr_type = socket.AF_INET6 addr_any = "0::0" @@ -63,16 +72,21 @@ class IPsecIPv6Params(object): self.remote_tun_if_host = '1111:1111:1111:1111:1111:1111:1111:1111' self.remote_tun_if_host4 = '1.1.1.1' - self.scapy_tun_sa_id = 50 + self.scapy_tun_sa_id = 500 self.scapy_tun_spi = 3001 - self.vpp_tun_sa_id = 60 + self.vpp_tun_sa_id = 600 self.vpp_tun_spi = 3000 - self.scapy_tra_sa_id = 70 + self.scapy_tra_sa_id = 700 self.scapy_tra_spi = 4001 - self.vpp_tra_sa_id = 80 + self.vpp_tra_sa_id = 800 self.vpp_tra_spi = 4000 + self.outer_hop_limit = 64 + self.inner_hop_limit = 255 + self.outer_flow_label = 0 + self.inner_flow_label = 0x12345 + self.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t. IPSEC_API_INTEG_ALG_SHA1_96) self.auth_algo = 'HMAC-SHA1-96' # scapy name @@ -85,10 +99,14 @@ class IPsecIPv6Params(object): self.salt = 0 self.flags = 0 self.nat_header = None + self.tun_flags = (VppEnum.vl_api_tunnel_encap_decap_flags_t. + TUNNEL_API_ENCAP_DECAP_FLAG_NONE) + self.dscp = 0 + self.async_mode = False def mk_scapy_crypt_key(p): - if p.crypt_algo == "AES-GCM": + if p.crypt_algo in ("AES-GCM", "AES-CTR"): return p.crypt_key + struct.pack("!I", p.salt) else: return p.crypt_key @@ -98,6 +116,8 @@ def config_tun_params(p, encryption_type, tun_if): ip_class_by_addr_type = {socket.AF_INET: IP, socket.AF_INET6: IPv6} esn_en = bool(p.flags & (VppEnum.vl_api_ipsec_sad_flags_t. IPSEC_API_SAD_FLAG_USE_ESN)) + p.tun_dst = tun_if.remote_addr[p.addr_type] + p.tun_src = tun_if.local_addr[p.addr_type] crypt_key = mk_scapy_crypt_key(p) p.scapy_tun_sa = SecurityAssociation( encryption_type, spi=p.vpp_tun_spi, @@ -105,8 +125,8 @@ def config_tun_params(p, encryption_type, tun_if): crypt_key=crypt_key, auth_algo=p.auth_algo, auth_key=p.auth_key, tunnel_header=ip_class_by_addr_type[p.addr_type]( - src=tun_if.remote_addr[p.addr_type], - dst=tun_if.local_addr[p.addr_type]), + src=p.tun_dst, + dst=p.tun_src), nat_t_header=p.nat_header, esn_en=esn_en) p.vpp_tun_sa = SecurityAssociation( @@ -115,8 +135,8 @@ def config_tun_params(p, encryption_type, tun_if): crypt_key=crypt_key, auth_algo=p.auth_algo, auth_key=p.auth_key, tunnel_header=ip_class_by_addr_type[p.addr_type]( - dst=tun_if.remote_addr[p.addr_type], - src=tun_if.local_addr[p.addr_type]), + dst=p.tun_dst, + src=p.tun_src), nat_t_header=p.nat_header, esn_en=esn_en) @@ -179,8 +199,10 @@ class TemplateIpsec(VppTestCase): super(TemplateIpsec, cls).tearDownClass() def setup_params(self): - self.ipv4_params = IPsecIPv4Params() - self.ipv6_params = IPsecIPv6Params() + if not hasattr(self, 'ipv4_params'): + self.ipv4_params = IPsecIPv4Params() + if not hasattr(self, 'ipv6_params'): + self.ipv6_params = IPsecIPv6Params() self.params = {self.ipv4_params.addr_type: self.ipv4_params, self.ipv6_params.addr_type: self.ipv6_params} @@ -222,17 +244,19 @@ class TemplateIpsec(VppTestCase): def show_commands_at_teardown(self): self.logger.info(self.vapi.cli("show hardware")) - def gen_encrypt_pkts(self, sa, sw_intf, src, dst, count=1, + def gen_encrypt_pkts(self, p, sa, sw_intf, src, dst, count=1, payload_size=54): return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / sa.encrypt(IP(src=src, dst=dst) / ICMP() / Raw(b'X' * payload_size)) for i in range(count)] - def gen_encrypt_pkts6(self, sa, sw_intf, src, dst, count=1, + def gen_encrypt_pkts6(self, p, sa, sw_intf, src, dst, count=1, payload_size=54): return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / - sa.encrypt(IPv6(src=src, dst=dst) / + sa.encrypt(IPv6(src=src, dst=dst, + hlim=p.inner_hop_limit, + fl=p.inner_flow_label) / ICMPv6EchoRequest(id=0, seq=1, data='X' * payload_size)) for i in range(count)] @@ -242,9 +266,10 @@ class TemplateIpsec(VppTestCase): IP(src=src, dst=dst) / ICMP() / Raw(b'X' * payload_size) for i in range(count)] - def gen_pkts6(self, sw_intf, src, dst, count=1, payload_size=54): + def gen_pkts6(self, p, sw_intf, src, dst, count=1, payload_size=54): return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / - IPv6(src=src, dst=dst) / + IPv6(src=src, dst=dst, + hlim=p.inner_hop_limit, fl=p.inner_flow_label) / ICMPv6EchoRequest(id=0, seq=1, data='X' * payload_size) for i in range(count)] @@ -272,28 +297,240 @@ class IpsecTcpTests(IpsecTcp): class IpsecTra4(object): """ verify methods for Transport v4 """ - def verify_tra_anti_replay(self): - p = self.params[socket.AF_INET] - esn_en = p.vpp_tra_sa.esn_en - - seq_cycle_node_name = ('/err/%s/sequence number cycled' % - self.tra4_encrypt_node_name) + def get_replay_counts(self, p): replay_node_name = ('/err/%s/SA replayed packet' % - self.tra4_decrypt_node_name) + self.tra4_decrypt_node_name[0]) + count = self.statistics.get_err_counter(replay_node_name) + + if p.async_mode: + replay_post_node_name = ('/err/%s/SA replayed packet' % + self.tra4_decrypt_node_name[p.async_mode]) + count += self.statistics.get_err_counter(replay_post_node_name) + + return count + + def get_hash_failed_counts(self, p): if ESP == self.encryption_type and p.crypt_algo == "AES-GCM": hash_failed_node_name = ('/err/%s/ESP decryption failed' % - self.tra4_decrypt_node_name) + self.tra4_decrypt_node_name[p.async_mode]) else: hash_failed_node_name = ('/err/%s/Integrity check failed' % - self.tra4_decrypt_node_name) - replay_count = self.statistics.get_err_counter(replay_node_name) - hash_failed_count = self.statistics.get_err_counter( - hash_failed_node_name) + self.tra4_decrypt_node_name[p.async_mode]) + count = self.statistics.get_err_counter(hash_failed_node_name) + + if p.async_mode: + count += self.statistics.get_err_counter( + '/err/crypto-dispatch/bad-hmac') + + return count + + def verify_hi_seq_num(self): + p = self.params[socket.AF_INET] + saf = VppEnum.vl_api_ipsec_sad_flags_t + esn_on = p.vpp_tra_sa.esn_en + ar_on = p.flags & saf.IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY + + seq_cycle_node_name = \ + ('/err/%s/sequence number cycled (packet dropped)' % + self.tra4_encrypt_node_name) + replay_count = self.get_replay_counts(p) + hash_failed_count = self.get_hash_failed_counts(p) + seq_cycle_count = self.statistics.get_err_counter(seq_cycle_node_name) + + # a few packets so we get the rx seq number above the window size and + # thus can simulate a wrap with an out of window packet + pkts = [(Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=seq)) + for seq in range(63, 80)] + recv_pkts = self.send_and_expect(self.tra_if, pkts, self.tra_if) + + # these 4 packets will all choose seq-num 0 to decrpyt since none + # are out of window when first checked. however, once #200 has + # decrypted it will move the window to 200 and has #81 is out of + # window. this packet should be dropped. + pkts = [(Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=200)), + (Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=81)), + (Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=201)), + (Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=202))] + + # if anti-replay is off then we won't drop #81 + n_rx = 3 if ar_on else 4 + self.send_and_expect(self.tra_if, pkts, self.tra_if, n_rx=n_rx) + # this packet is one before the wrap + pkts = [(Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=203))] + recv_pkts = self.send_and_expect(self.tra_if, pkts, self.tra_if) + + # move the window over half way to a wrap + pkts = [(Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=0x80000001))] + recv_pkts = self.send_and_expect(self.tra_if, pkts, self.tra_if) + + # anti-replay will drop old packets, no anti-replay will not + pkts = [(Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=0x44000001))] + + if ar_on: + self.send_and_assert_no_replies(self.tra_if, pkts) + else: + recv_pkts = self.send_and_expect(self.tra_if, pkts, self.tra_if) + + if esn_on: + # + # validate wrapping the ESN + # + + # wrap scapy's TX SA SN + p.scapy_tra_sa.seq_num = 0x100000005 + + # send a packet that wraps the window for both AR and no AR + pkts = [(Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=0x100000005))] + + rxs = self.send_and_expect(self.tra_if, pkts, self.tra_if) + for rx in rxs: + decrypted = p.vpp_tra_sa.decrypt(rx[0][IP]) + + # move the window forward to half way to the next wrap + pkts = [(Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=0x180000005))] + + rxs = self.send_and_expect(self.tra_if, pkts, self.tra_if) + + # a packet less than 2^30 from the current position is: + # - AR: out of window and dropped + # - non-AR: accepted + pkts = [(Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=0x170000005))] + + if ar_on: + self.send_and_assert_no_replies(self.tra_if, pkts) + else: + self.send_and_expect(self.tra_if, pkts, self.tra_if) + + # a packet more than 2^30 from the current position is: + # - AR: out of window and dropped + # - non-AR: considered a wrap, but since it's not a wrap + # it won't decrpyt and so will be dropped + pkts = [(Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=0x130000005))] + + self.send_and_assert_no_replies(self.tra_if, pkts) + + # a packet less than 2^30 from the current position and is a + # wrap; (the seq is currently at 0x180000005). + # - AR: out of window so considered a wrap, so accepted + # - non-AR: not considered a wrap, so won't decrypt + p.scapy_tra_sa.seq_num = 0x260000005 + pkts = [(Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=0x260000005))] + if ar_on: + self.send_and_expect(self.tra_if, pkts, self.tra_if) + else: + self.send_and_assert_no_replies(self.tra_if, pkts) + + # + # window positions are different now for AR/non-AR + # move non-AR forward + # + if not ar_on: + # a packet more than 2^30 from the current position and is a + # wrap; (the seq is currently at 0x180000005). + # - AR: accepted + # - non-AR: not considered a wrap, so won't decrypt + + pkts = [(Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=0x200000005)), + (Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=0x200000006))] + self.send_and_expect(self.tra_if, pkts, self.tra_if) + + pkts = [(Ether(src=self.tra_if.remote_mac, + dst=self.tra_if.local_mac) / + p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, + dst=self.tra_if.local_ip4) / + ICMP(), + seq_num=0x260000005))] + self.send_and_expect(self.tra_if, pkts, self.tra_if) + + def verify_tra_anti_replay(self): + p = self.params[socket.AF_INET] + esn_en = p.vpp_tra_sa.esn_en + + seq_cycle_node_name = \ + ('/err/%s/sequence number cycled (packet dropped)' % + self.tra4_encrypt_node_name) + replay_count = self.get_replay_counts(p) + hash_failed_count = self.get_hash_failed_counts(p) seq_cycle_count = self.statistics.get_err_counter(seq_cycle_node_name) if ESP == self.encryption_type: undersize_node_name = ('/err/%s/undersized packet' % - self.tra4_decrypt_node_name) + self.tra4_decrypt_node_name[0]) undersize_count = self.statistics.get_err_counter( undersize_node_name) @@ -315,14 +552,16 @@ class IpsecTra4(object): recv_pkts = self.send_and_expect(self.tra_if, pkts, self.tra_if) # replayed packets are dropped - self.send_and_assert_no_replies(self.tra_if, pkts) + self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2) replay_count += len(pkts) - self.assert_error_counter_equal(replay_node_name, replay_count) + self.assertEqual(self.get_replay_counts(p), replay_count) # # now send a batch of packets all with the same sequence number # the first packet in the batch is legitimate, the rest bogus # + self.vapi.cli("clear error") + self.vapi.cli("clear node counters") pkts = (Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac) / p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, @@ -332,11 +571,12 @@ class IpsecTra4(object): recv_pkts = self.send_and_expect(self.tra_if, pkts * 8, self.tra_if, n_rx=1) replay_count += 7 - self.assert_error_counter_equal(replay_node_name, replay_count) + self.assertEqual(self.get_replay_counts(p), replay_count) # # now move the window over to 257 (more than one byte) and into Case A # + self.vapi.cli("clear error") pkt = (Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac) / p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, @@ -346,9 +586,9 @@ class IpsecTra4(object): recv_pkts = self.send_and_expect(self.tra_if, [pkt], self.tra_if) # replayed packets are dropped - self.send_and_assert_no_replies(self.tra_if, pkt * 3) + self.send_and_assert_no_replies(self.tra_if, pkt * 3, timeout=0.2) replay_count += 3 - self.assert_error_counter_equal(replay_node_name, replay_count) + self.assertEqual(self.get_replay_counts(p), replay_count) # the window size is 64 packets # in window are still accepted @@ -373,11 +613,10 @@ class IpsecTra4(object): dst=self.tra_if.local_ip4) / ICMP(), seq_num=350)) - self.send_and_assert_no_replies(self.tra_if, pkt * 17) + self.send_and_assert_no_replies(self.tra_if, pkt * 17, timeout=0.2) hash_failed_count += 17 - self.assert_error_counter_equal(hash_failed_node_name, - hash_failed_count) + self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count) # a malformed 'runt' packet # created by a mis-constructed SA @@ -390,7 +629,7 @@ class IpsecTra4(object): dst=self.tra_if.local_ip4) / ICMP(), seq_num=350)) - self.send_and_assert_no_replies(self.tra_if, pkt * 17) + self.send_and_assert_no_replies(self.tra_if, pkt * 17, timeout=0.2) undersize_count += 17 self.assert_error_counter_equal(undersize_node_name, @@ -416,19 +655,17 @@ class IpsecTra4(object): dst=self.tra_if.local_ip4) / ICMP(), seq_num=17)) - self.send_and_assert_no_replies(self.tra_if, pkt * 17) + self.send_and_assert_no_replies(self.tra_if, pkt * 17, timeout=0.2) if esn_en: # an out of window error with ESN looks like a high sequence # wrap. but since it isn't then the verify will fail. hash_failed_count += 17 - self.assert_error_counter_equal(hash_failed_node_name, - hash_failed_count) + self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count) else: replay_count += 17 - self.assert_error_counter_equal(replay_node_name, - replay_count) + self.assertEqual(self.get_replay_counts(p), replay_count) # valid packet moves the window over to 258 pkt = (Ether(src=self.tra_if.remote_mac, @@ -482,6 +719,7 @@ class IpsecTra4(object): ICMP(), seq_num=0x100000005)) rx = self.send_and_expect(self.tra_if, [pkt], self.tra_if) + decrypted = p.vpp_tra_sa.decrypt(rx[0][IP]) # @@ -500,7 +738,7 @@ class IpsecTra4(object): # # While in case A we cannot wrap the high sequence number again - # becuase VPP will consider this packet to be one that moves the + # because VPP will consider this packet to be one that moves the # window forward # pkt = (Ether(src=self.tra_if.remote_mac, @@ -509,14 +747,14 @@ class IpsecTra4(object): dst=self.tra_if.local_ip4) / ICMP(), seq_num=0x200000999)) - self.send_and_assert_no_replies(self.tra_if, [pkt], self.tra_if) + self.send_and_assert_no_replies(self.tra_if, [pkt], self.tra_if, + timeout=0.2) hash_failed_count += 1 - self.assert_error_counter_equal(hash_failed_node_name, - hash_failed_count) + self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count) # - # but if we move the wondow forward to case B, then we can wrap + # but if we move the window forward to case B, then we can wrap # again # p.scapy_tra_sa.seq_num = 0x100000555 @@ -544,7 +782,7 @@ class IpsecTra4(object): # without ESN TX sequence numbers can't wrap and packets are # dropped from here on out. # - self.send_and_assert_no_replies(self.tra_if, pkts) + self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2) seq_cycle_count += len(pkts) self.assert_error_counter_equal(seq_cycle_node_name, seq_cycle_count) @@ -554,16 +792,17 @@ class IpsecTra4(object): p.scapy_tra_sa.seq_num = 351 p.vpp_tra_sa.seq_num = 351 - def verify_tra_basic4(self, count=1): + def verify_tra_basic4(self, count=1, payload_size=54): """ ipsec v4 transport basic test """ self.vapi.cli("clear errors") self.vapi.cli("clear ipsec sa") try: p = self.params[socket.AF_INET] - send_pkts = self.gen_encrypt_pkts(p.scapy_tra_sa, self.tra_if, + send_pkts = self.gen_encrypt_pkts(p, p.scapy_tra_sa, self.tra_if, src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4, - count=count) + count=count, + payload_size=payload_size) recv_pkts = self.send_and_expect(self.tra_if, send_pkts, self.tra_if) for rx in recv_pkts: @@ -589,13 +828,13 @@ class IpsecTra4(object): (count, pkts)) self.assert_packet_counter_equal(self.tra4_encrypt_node_name, count) - self.assert_packet_counter_equal(self.tra4_decrypt_node_name, count) + self.assert_packet_counter_equal(self.tra4_decrypt_node_name[0], count) class IpsecTra4Tests(IpsecTra4): """ UT test methods for Transport v4 """ def test_tra_anti_replay(self): - """ ipsec v4 transport anti-reply test """ + """ ipsec v4 transport anti-replay test """ self.verify_tra_anti_replay() def test_tra_basic(self, count=1): @@ -609,14 +848,16 @@ class IpsecTra4Tests(IpsecTra4): class IpsecTra6(object): """ verify methods for Transport v6 """ - def verify_tra_basic6(self, count=1): + def verify_tra_basic6(self, count=1, payload_size=54): self.vapi.cli("clear errors") + self.vapi.cli("clear ipsec sa") try: p = self.params[socket.AF_INET6] - send_pkts = self.gen_encrypt_pkts6(p.scapy_tra_sa, self.tra_if, + send_pkts = self.gen_encrypt_pkts6(p, p.scapy_tra_sa, self.tra_if, src=self.tra_if.remote_ip6, dst=self.tra_if.local_ip6, - count=count) + count=count, + payload_size=payload_size) recv_pkts = self.send_and_expect(self.tra_if, send_pkts, self.tra_if) for rx in recv_pkts: @@ -641,7 +882,7 @@ class IpsecTra6(object): "incorrect SA out counts: expected %d != %d" % (count, pkts)) self.assert_packet_counter_equal(self.tra6_encrypt_node_name, count) - self.assert_packet_counter_equal(self.tra6_decrypt_node_name, count) + self.assert_packet_counter_equal(self.tra6_decrypt_node_name[0], count) def gen_encrypt_pkts_ext_hdrs6(self, sa, sw_intf, src, dst, count=1, payload_size=54): @@ -785,12 +1026,12 @@ class IpsecTun4(object): "incorrect SA in counts: expected %d != %d" % (count, pkts)) pkts = p.tun_sa_out.get_stats(worker)['packets'] - self.assertEqual(pkts, count, + self.assertEqual(pkts, n_frags, "incorrect SA out counts: expected %d != %d" % (count, pkts)) self.assert_packet_counter_equal(self.tun4_encrypt_node_name, n_frags) - self.assert_packet_counter_equal(self.tun4_decrypt_node_name, count) + self.assert_packet_counter_equal(self.tun4_decrypt_node_name[0], count) def verify_decrypted(self, p, rxs): for rx in rxs: @@ -798,6 +1039,15 @@ class IpsecTun4(object): self.assert_equal(rx[IP].dst, self.pg1.remote_ip4) self.assert_packet_checksums_valid(rx) + def verify_esp_padding(self, sa, esp_payload, decrypt_pkt): + align = sa.crypt_algo.block_size + if align < 4: + align = 4 + exp_len = (len(decrypt_pkt) + 2 + (align - 1)) & ~(align - 1) + exp_len += sa.crypt_algo.iv_size + exp_len += sa.crypt_algo.icv_size or sa.auth_algo.icv_size + self.assertEqual(exp_len, len(esp_payload)) + def verify_encrypted(self, p, sa, rxs): decrypt_pkts = [] for rx in rxs: @@ -806,9 +1056,12 @@ class IpsecTun4(object): self.assert_packet_checksums_valid(rx) self.assertEqual(len(rx) - len(Ether()), rx[IP].len) try: - decrypt_pkt = p.vpp_tun_sa.decrypt(rx[IP]) + rx_ip = rx[IP] + decrypt_pkt = p.vpp_tun_sa.decrypt(rx_ip) if not decrypt_pkt.haslayer(IP): decrypt_pkt = IP(decrypt_pkt[Raw].load) + if rx_ip.proto == socket.IPPROTO_ESP: + self.verify_esp_padding(sa, rx_ip[ESP].data, decrypt_pkt) decrypt_pkts.append(decrypt_pkt) self.assert_equal(decrypt_pkt.src, self.pg1.remote_ip4) self.assert_equal(decrypt_pkt.dst, p.remote_tun_if_host) @@ -826,13 +1079,15 @@ class IpsecTun4(object): def verify_tun_44(self, p, count=1, payload_size=64, n_rx=None): self.vapi.cli("clear errors") self.vapi.cli("clear ipsec counters") + self.vapi.cli("clear ipsec sa") if not n_rx: n_rx = count try: - send_pkts = self.gen_encrypt_pkts(p.scapy_tun_sa, self.tun_if, + send_pkts = self.gen_encrypt_pkts(p, p.scapy_tun_sa, self.tun_if, src=p.remote_tun_if_host, dst=self.pg1.remote_ip4, - count=count) + count=count, + payload_size=payload_size) recv_pkts = self.send_and_expect(self.tun_if, send_pkts, self.pg1) self.verify_decrypted(p, recv_pkts) @@ -843,6 +1098,10 @@ class IpsecTun4(object): self.tun_if, n_rx) self.verify_encrypted(p, p.vpp_tun_sa, recv_pkts) + for rx in recv_pkts: + self.assertEqual(rx[IP].src, p.tun_src) + self.assertEqual(rx[IP].dst, p.tun_dst) + finally: self.logger.info(self.vapi.ppcli("show error")) self.logger.info(self.vapi.ppcli("show ipsec all")) @@ -851,40 +1110,25 @@ class IpsecTun4(object): self.logger.info(self.vapi.ppcli("show ipsec sa 4")) self.verify_counters4(p, count, n_rx) - """ verify methods for Transport v4 """ - def verify_tun_44_bad_packet_sizes(self, p): - # with a buffer size of 2048, 1989 bytes of payload - # means there isn't space to insert the ESP header - N_PKTS = 63 - for p_siz in [1989, 8500]: - send_pkts = self.gen_encrypt_pkts(p.scapy_tun_sa, self.tun_if, + def verify_tun_dropped_44(self, p, count=1, payload_size=64, n_rx=None): + self.vapi.cli("clear errors") + if not n_rx: + n_rx = count + try: + send_pkts = self.gen_encrypt_pkts(p, p.scapy_tun_sa, self.tun_if, src=p.remote_tun_if_host, dst=self.pg1.remote_ip4, - count=N_PKTS, - payload_size=p_siz) + count=count) self.send_and_assert_no_replies(self.tun_if, send_pkts) + send_pkts = self.gen_pkts(self.pg1, src=self.pg1.remote_ip4, - dst=p.remote_tun_if_host, count=N_PKTS, - payload_size=p_siz) - self.send_and_assert_no_replies(self.pg1, send_pkts, - self.tun_if) - - # both large packets on decrpyt count against chained buffers - # the 9000 bytes one does on encrypt - self.assertEqual(2 * N_PKTS, - self.statistics.get_err_counter( - '/err/%s/chained buffers (packet dropped)' % - self.tun4_decrypt_node_name)) - self.assertEqual(N_PKTS, - self.statistics.get_err_counter( - '/err/%s/chained buffers (packet dropped)' % - self.tun4_encrypt_node_name)) - - # on encrypt the 1989 size is no trailer space - self.assertEqual(N_PKTS, - self.statistics.get_err_counter( - '/err/%s/no trailer space (packet dropped)' % - self.tun4_encrypt_node_name)) + dst=p.remote_tun_if_host, count=count, + payload_size=payload_size) + self.send_and_assert_no_replies(self.pg1, send_pkts) + + finally: + self.logger.info(self.vapi.ppcli("show error")) + self.logger.info(self.vapi.ppcli("show ipsec all")) def verify_tun_reass_44(self, p): self.vapi.cli("clear errors") @@ -892,7 +1136,7 @@ class IpsecTun4(object): sw_if_index=self.tun_if.sw_if_index, enable_ip4=True) try: - send_pkts = self.gen_encrypt_pkts(p.scapy_tun_sa, self.tun_if, + send_pkts = self.gen_encrypt_pkts(p, p.scapy_tun_sa, self.tun_if, src=p.remote_tun_if_host, dst=self.pg1.remote_ip4, payload_size=1900, @@ -918,8 +1162,9 @@ class IpsecTun4(object): def verify_tun_64(self, p, count=1): self.vapi.cli("clear errors") + self.vapi.cli("clear ipsec sa") try: - send_pkts = self.gen_encrypt_pkts6(p.scapy_tun_sa, self.tun_if, + send_pkts = self.gen_encrypt_pkts6(p, p.scapy_tun_sa, self.tun_if, src=p.remote_tun_if_host6, dst=self.pg1.remote_ip6, count=count) @@ -928,7 +1173,7 @@ class IpsecTun4(object): self.assert_equal(recv_pkt[IPv6].src, p.remote_tun_if_host6) self.assert_equal(recv_pkt[IPv6].dst, self.pg1.remote_ip6) self.assert_packet_checksums_valid(recv_pkt) - send_pkts = self.gen_pkts6(self.pg1, src=self.pg1.remote_ip6, + send_pkts = self.gen_pkts6(p, self.pg1, src=self.pg1.remote_ip6, dst=p.remote_tun_if_host6, count=count) recv_pkts = self.send_and_expect(self.pg1, send_pkts, self.tun_if) for recv_pkt in recv_pkts: @@ -990,12 +1235,6 @@ class IpsecTun4Tests(IpsecTun4): self.verify_tun_44(self.params[socket.AF_INET], count=127) -class IpsecTunEsp4Tests(IpsecTun4): - def test_tun_bad_packet_sizes(self): - """ ipsec v4 tunnel bad packet size """ - self.verify_tun_44_bad_packet_sizes(self.params[socket.AF_INET]) - - class IpsecTun6(object): """ verify methods for Tunnel v6 """ def verify_counters6(self, p_in, p_out, count, worker=None): @@ -1010,7 +1249,7 @@ class IpsecTun6(object): "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) + self.assert_packet_counter_equal(self.tun6_decrypt_node_name[0], count) def verify_decrypted6(self, p, rxs): for rx in rxs: @@ -1023,6 +1262,9 @@ class IpsecTun6(object): self.assert_packet_checksums_valid(rx) self.assertEqual(len(rx) - len(Ether()) - len(IPv6()), rx[IPv6].plen) + self.assert_equal(rx[IPv6].hlim, p.outer_hop_limit) + if p.outer_flow_label: + self.assert_equal(rx[IPv6].fl, p.outer_flow_label) try: decrypt_pkt = p.vpp_tun_sa.decrypt(rx[IPv6]) if not decrypt_pkt.haslayer(IPv6): @@ -1030,6 +1272,8 @@ class IpsecTun6(object): self.assert_packet_checksums_valid(decrypt_pkt) self.assert_equal(decrypt_pkt.src, self.pg1.remote_ip6) self.assert_equal(decrypt_pkt.dst, p.remote_tun_if_host) + self.assert_equal(decrypt_pkt.hlim, p.inner_hop_limit - 1) + self.assert_equal(decrypt_pkt.fl, p.inner_flow_label) except: self.logger.debug(ppp("Unexpected packet:", rx)) try: @@ -1042,7 +1286,8 @@ class IpsecTun6(object): self.vapi.cli("clear errors") self.vapi.cli("clear ipsec sa") - send_pkts = self.gen_encrypt_pkts6(p_in.scapy_tun_sa, self.tun_if, + send_pkts = self.gen_encrypt_pkts6(p_in, p_in.scapy_tun_sa, + self.tun_if, src=p_in.remote_tun_if_host, dst=self.pg1.remote_ip6, count=count) @@ -1055,20 +1300,26 @@ class IpsecTun6(object): if not p_out: p_out = p_in try: - send_pkts = self.gen_encrypt_pkts6(p_in.scapy_tun_sa, self.tun_if, + send_pkts = self.gen_encrypt_pkts6(p_in, p_in.scapy_tun_sa, + self.tun_if, src=p_in.remote_tun_if_host, dst=self.pg1.remote_ip6, - count=count) + count=count, + payload_size=payload_size) recv_pkts = self.send_and_expect(self.tun_if, send_pkts, self.pg1) self.verify_decrypted6(p_in, recv_pkts) - send_pkts = self.gen_pkts6(self.pg1, src=self.pg1.remote_ip6, + send_pkts = self.gen_pkts6(p_in, self.pg1, src=self.pg1.remote_ip6, dst=p_out.remote_tun_if_host, count=count, payload_size=payload_size) recv_pkts = self.send_and_expect(self.pg1, send_pkts, self.tun_if) self.verify_encrypted6(p_out, p_out.vpp_tun_sa, recv_pkts) + for rx in recv_pkts: + self.assertEqual(rx[IPv6].src, p_out.tun_src) + self.assertEqual(rx[IPv6].dst, p_out.tun_dst) + finally: self.logger.info(self.vapi.ppcli("show error")) self.logger.info(self.vapi.ppcli("show ipsec all")) @@ -1080,7 +1331,7 @@ class IpsecTun6(object): sw_if_index=self.tun_if.sw_if_index, enable_ip6=True) try: - send_pkts = self.gen_encrypt_pkts6(p.scapy_tun_sa, self.tun_if, + send_pkts = self.gen_encrypt_pkts6(p, p.scapy_tun_sa, self.tun_if, src=p.remote_tun_if_host, dst=self.pg1.remote_ip6, count=1, @@ -1090,7 +1341,7 @@ class IpsecTun6(object): self.pg1, n_rx=1) self.verify_decrypted6(p, recv_pkts) - send_pkts = self.gen_pkts6(self.pg1, src=self.pg1.remote_ip6, + send_pkts = self.gen_pkts6(p, self.pg1, src=self.pg1.remote_ip6, dst=p.remote_tun_if_host, count=1, payload_size=64) @@ -1107,8 +1358,9 @@ class IpsecTun6(object): def verify_tun_46(self, p, count=1): """ ipsec 4o6 tunnel basic test """ self.vapi.cli("clear errors") + self.vapi.cli("clear ipsec sa") try: - send_pkts = self.gen_encrypt_pkts(p.scapy_tun_sa, self.tun_if, + send_pkts = self.gen_encrypt_pkts(p, p.scapy_tun_sa, self.tun_if, src=p.remote_tun_if_host4, dst=self.pg1.remote_ip4, count=count) @@ -1161,17 +1413,20 @@ class IpsecTun6Tests(IpsecTun6): class IpsecTun6HandoffTests(IpsecTun6): """ UT test methods for Tunnel v6 with multiple workers """ - worker_config = "workers 2" + vpp_worker_count = 2 def test_tun_handoff_66(self): """ ipsec 6o6 tunnel worker hand-off test """ + self.vapi.cli("clear errors") + self.vapi.cli("clear ipsec sa") + N_PKTS = 15 p = self.params[socket.AF_INET6] # inject alternately on worker 0 and 1. all counts on the SA # should be against worker 0 for worker in [0, 1, 0, 1]: - send_pkts = self.gen_encrypt_pkts6(p.scapy_tun_sa, self.tun_if, + send_pkts = self.gen_encrypt_pkts6(p, p.scapy_tun_sa, self.tun_if, src=p.remote_tun_if_host, dst=self.pg1.remote_ip6, count=N_PKTS) @@ -1179,7 +1434,7 @@ class IpsecTun6HandoffTests(IpsecTun6): self.pg1, worker=worker) self.verify_decrypted6(p, recv_pkts) - send_pkts = self.gen_pkts6(self.pg1, src=self.pg1.remote_ip6, + send_pkts = self.gen_pkts6(p, self.pg1, src=self.pg1.remote_ip6, dst=p.remote_tun_if_host, count=N_PKTS) recv_pkts = self.send_and_expect(self.pg1, send_pkts, @@ -1192,17 +1447,20 @@ class IpsecTun6HandoffTests(IpsecTun6): class IpsecTun4HandoffTests(IpsecTun4): """ UT test methods for Tunnel v4 with multiple workers """ - worker_config = "workers 2" + vpp_worker_count = 2 def test_tun_handooff_44(self): """ ipsec 4o4 tunnel worker hand-off test """ + self.vapi.cli("clear errors") + self.vapi.cli("clear ipsec sa") + N_PKTS = 15 p = self.params[socket.AF_INET] # inject alternately on worker 0 and 1. all counts on the SA # should be against worker 0 for worker in [0, 1, 0, 1]: - send_pkts = self.gen_encrypt_pkts(p.scapy_tun_sa, self.tun_if, + send_pkts = self.gen_encrypt_pkts(p, p.scapy_tun_sa, self.tun_if, src=p.remote_tun_if_host, dst=self.pg1.remote_ip4, count=N_PKTS)