ipsec: Fix decap of IPSEC/GRE in transport mode 44/24044/1
authorNeale Ranns <nranns@cisco.com>
Wed, 18 Dec 2019 05:54:40 +0000 (05:54 +0000)
committerNeale Ranns <nranns@cisco.com>
Wed, 18 Dec 2019 05:54:40 +0000 (05:54 +0000)
Type: fix

in transport mode the header sequence is:
  MAC - IP (tun) - ESP - GRE - L2
so popping the GRE header is done in the ESP decrypt node.

Change-Id: Ia125eb65b9300368617d2bffca09683851e43be0
Signed-off-by: Neale Ranns <nranns@cisco.com>
src/vnet/ipsec/esp_decrypt.c
test/test_ipsec_tun_if_esp.py

index a56a784..8afea9b 100644 (file)
 #include <vnet/ipsec/ipsec_io.h>
 #include <vnet/ipsec/ipsec_tun.h>
 
+#include <vnet/gre/gre.h>
+
 #define foreach_esp_decrypt_next                \
 _(DROP, "error-drop")                           \
 _(IP4_INPUT, "ip4-input-no-checksum")           \
 _(IP6_INPUT, "ip6-input")                       \
+_(L2_INPUT, "l2-input")                         \
 _(HANDOFF, "handoff")
 
 #define _(v, s) ESP_DECRYPT_NEXT_##v,
@@ -487,9 +490,40 @@ esp_decrypt_inline (vlib_main_t * vm,
            }
          else
            {
-             next[0] = ESP_DECRYPT_NEXT_DROP;
-             b[0]->error = node->errors[ESP_DECRYPT_ERROR_DECRYPTION_FAILED];
-             goto trace;
+             if (is_tun && f->next_header == IP_PROTOCOL_GRE)
+               {
+                 gre_header_t *gre;
+
+                 b[0]->current_data = pd->current_data + adv;
+                 b[0]->current_length = pd->current_length - adv - tail;
+
+                 gre = vlib_buffer_get_current (b[0]);
+
+                 vlib_buffer_advance (b[0], sizeof (*gre));
+
+                 switch (clib_net_to_host_u16 (gre->protocol))
+                   {
+                   case GRE_PROTOCOL_teb:
+                     next[0] = ESP_DECRYPT_NEXT_L2_INPUT;
+                     break;
+                   case GRE_PROTOCOL_ip4:
+                     next[0] = ESP_DECRYPT_NEXT_IP4_INPUT;
+                     break;
+                   case GRE_PROTOCOL_ip6:
+                     next[0] = ESP_DECRYPT_NEXT_IP6_INPUT;
+                     break;
+                   default:
+                     next[0] = ESP_DECRYPT_NEXT_DROP;
+                     break;
+                   }
+               }
+             else
+               {
+                 next[0] = ESP_DECRYPT_NEXT_DROP;
+                 b[0]->error =
+                   node->errors[ESP_DECRYPT_ERROR_DECRYPTION_FAILED];
+                 goto trace;
+               }
            }
          if (is_tun)
            {
@@ -501,9 +535,9 @@ esp_decrypt_inline (vlib_main_t * vm,
                   */
                  const ipsec_tun_protect_t *itp;
 
-                 itp =
-                   ipsec_tun_protect_get (vnet_buffer (b[0])->
-                                          ipsec.protect_index);
+                 itp = ipsec_tun_protect_get
+                   (vnet_buffer (b[0])->ipsec.protect_index);
+
                  if (PREDICT_TRUE (f->next_header == IP_PROTOCOL_IP_IN_IP))
                    {
                      const ip4_header_t *ip4;
@@ -614,6 +648,7 @@ VLIB_REGISTER_NODE (esp4_decrypt_node) = {
     [ESP_DECRYPT_NEXT_DROP] = "ip4-drop",
     [ESP_DECRYPT_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
     [ESP_DECRYPT_NEXT_IP6_INPUT] = "ip6-input",
+    [ESP_DECRYPT_NEXT_L2_INPUT] = "l2-input",
     [ESP_DECRYPT_NEXT_HANDOFF] = "esp4-decrypt-handoff",
   },
 };
@@ -632,6 +667,7 @@ VLIB_REGISTER_NODE (esp6_decrypt_node) = {
     [ESP_DECRYPT_NEXT_DROP] = "ip6-drop",
     [ESP_DECRYPT_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
     [ESP_DECRYPT_NEXT_IP6_INPUT] = "ip6-input",
+    [ESP_DECRYPT_NEXT_L2_INPUT] = "l2-input",
     [ESP_DECRYPT_NEXT_HANDOFF]=  "esp6-decrypt-handoff",
   },
 };
@@ -648,6 +684,7 @@ VLIB_REGISTER_NODE (esp4_decrypt_tun_node) = {
     [ESP_DECRYPT_NEXT_DROP] = "ip4-drop",
     [ESP_DECRYPT_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
     [ESP_DECRYPT_NEXT_IP6_INPUT] = "ip6-input",
+    [ESP_DECRYPT_NEXT_L2_INPUT] = "l2-input",
     [ESP_DECRYPT_NEXT_HANDOFF] = "esp4-decrypt-handoff",
   },
 };
@@ -664,6 +701,7 @@ VLIB_REGISTER_NODE (esp6_decrypt_tun_node) = {
     [ESP_DECRYPT_NEXT_DROP] = "ip6-drop",
     [ESP_DECRYPT_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
     [ESP_DECRYPT_NEXT_IP6_INPUT] = "ip6-input",
+    [ESP_DECRYPT_NEXT_L2_INPUT] = "l2-input",
     [ESP_DECRYPT_NEXT_HANDOFF]=  "esp6-decrypt-handoff",
   },
 };
index 3e578fa..68d6b58 100644 (file)
@@ -47,6 +47,25 @@ def config_tun_params(p, encryption_type, tun_if):
         esn_en=esn_en)
 
 
+def config_tra_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))
+    crypt_key = mk_scapy_crypt_key(p)
+    p.scapy_tun_sa = SecurityAssociation(
+        encryption_type, spi=p.vpp_tun_spi,
+        crypt_algo=p.crypt_algo,
+        crypt_key=crypt_key,
+        auth_algo=p.auth_algo, auth_key=p.auth_key,
+        esn_en=esn_en)
+    p.vpp_tun_sa = SecurityAssociation(
+        encryption_type, spi=p.scapy_tun_spi,
+        crypt_algo=p.crypt_algo,
+        crypt_key=crypt_key,
+        auth_algo=p.auth_algo, auth_key=p.auth_key,
+        esn_en=esn_en)
+
+
 class TemplateIpsec4TunIfEsp(TemplateIpsec):
     """ IPsec tunnel interface tests """
 
@@ -657,6 +676,111 @@ class TestIpsecGreTebIfEsp(TemplateIpsec,
         super(TestIpsecGreTebIfEsp, self).tearDown()
 
 
+class TestIpsecGreTebIfEspTra(TemplateIpsec,
+                              IpsecTun4Tests):
+    """ Ipsec GRE TEB ESP - Tra tests """
+    tun4_encrypt_node_name = "esp4-encrypt-tun"
+    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    encryption_type = ESP
+    omac = "00:11:22:33:44:55"
+
+    def gen_encrypt_pkts(self, 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.pg0.remote_ip4,
+                              dst=self.pg0.local_ip4) /
+                           GRE() /
+                           Ether(dst=self.omac) /
+                           IP(src="1.1.1.1", dst="1.1.1.2") /
+                           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(dst=self.omac) /
+                IP(src="1.1.1.1", dst="1.1.1.2") /
+                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.omac)
+            self.assert_equal(rx[IP].dst, "1.1.1.2")
+
+    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)
+                self.assert_equal(pkt[IP].dst, self.pg0.remote_ip4)
+                self.assert_equal(pkt[IP].src, self.pg0.local_ip4)
+                self.assertTrue(pkt.haslayer(GRE))
+                e = pkt[Ether]
+                self.assertEqual(e[Ether].dst, self.omac)
+                self.assertEqual(e[IP].dst, "1.1.1.2")
+            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(TestIpsecGreTebIfEspTra, self).setUp()
+
+        self.tun_if = self.pg0
+
+        p = self.ipv4_params
+
+        bd1 = VppBridgeDomain(self, 1)
+        bd1.add_vpp_config()
+
+        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)
+        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)
+        p.tun_sa_in.add_vpp_config()
+
+        p.tun_if = VppGreInterface(self,
+                                   self.pg0.local_ip4,
+                                   self.pg0.remote_ip4,
+                                   type=(VppEnum.vl_api_gre_tunnel_type_t.
+                                         GRE_API_TUNNEL_TYPE_TEB))
+        p.tun_if.add_vpp_config()
+
+        p.tun_protect = VppIpsecTunProtect(self,
+                                           p.tun_if,
+                                           p.tun_sa_out,
+                                           [p.tun_sa_in])
+
+        p.tun_protect.add_vpp_config()
+
+        p.tun_if.admin_up()
+        p.tun_if.config_ip4()
+        config_tra_params(p, self.encryption_type, p.tun_if)
+
+        VppBridgeDomainPort(self, bd1, p.tun_if).add_vpp_config()
+        VppBridgeDomainPort(self, bd1, self.pg1).add_vpp_config()
+
+        self.vapi.cli("clear ipsec sa")
+
+    def tearDown(self):
+        p = self.ipv4_params
+        p.tun_if.unconfig_ip4()
+        super(TestIpsecGreTebIfEspTra, self).tearDown()
+
+
 class TestIpsecGreIfEsp(TemplateIpsec,
                         IpsecTun4Tests):
     """ Ipsec GRE ESP - TUN tests """
@@ -845,7 +969,7 @@ class TestIpsecGreIfEspTra(TemplateIpsec,
 
         p.tun_if.admin_up()
         p.tun_if.config_ip4()
-        config_tun_params(p, self.encryption_type, p.tun_if)
+        config_tra_params(p, self.encryption_type, p.tun_if)
 
         VppIpRoute(self, "1.1.1.2", 32,
                    [VppRoutePath(p.tun_if.remote_ip4,