ipsec: fix padding/alignment for native IPsec encryption 89/28689/6
authorChristian Hopps <chopps@labn.net>
Sun, 3 Nov 2019 12:02:15 +0000 (07:02 -0500)
committerDamjan Marion <dmarion@me.com>
Mon, 7 Sep 2020 09:43:27 +0000 (09:43 +0000)
Not all ESP crypto algorithms require padding/alignment to be the same
as AES block/IV size. CCM, CTR and GCM all have no padding/alignment
requirements, and the RFCs indicate that no padding (beyond ESPs 4 octet
alignment requirement) should be used unless TFC (traffic flow
confidentiality) has been requested.

  CTR: https://tools.ietf.org/html/rfc3686#section-3.2
  GCM: https://tools.ietf.org/html/rfc4106#section-3.2
  CCM: https://tools.ietf.org/html/rfc4309#section-3.2

- VPP is incorrectly using the IV/AES block size to pad CTR and GCM.
These modes do not require padding (beyond ESPs 4 octet requirement), as
a result packets will have unnecessary padding, which will waste
bandwidth at least and possibly fail certain network configurations that
have finely tuned MTU configurations at worst.

Fix this as well as changing the field names from ".*block_size" to
".*block_align" to better represent their actual (and only) use. Rename
"block_sz" in esp_encrypt to "esp_align" and set it correctly as well.

test: ipsec: Add unit-test to test for RFC correct padding/alignment

test: patch scapy to not incorrectly pad ccm, ctr, gcm modes as well

- Scapy is also incorrectly using the AES block size of 16 to pad CCM,
CTR, and GCM cipher modes. A bug report has been opened with the
and acknowledged with the upstream scapy project as well:

  https://github.com/secdev/scapy/issues/2322

Ticket: VPP-1928
Type: fix
Signed-off-by: Christian Hopps <chopps@labn.net>
Change-Id: Iaa4d6a325a2e99fdcb2c375a3395bcfe7947770e

src/vnet/ipsec/esp_encrypt.c
src/vnet/ipsec/ipsec.c
src/vnet/ipsec/ipsec.h
src/vnet/ipsec/ipsec_sa.c
src/vnet/ipsec/ipsec_sa.h
test/patches/scapy-2.4.3/ipsec.patch
test/patches/scapy-2.4/ipsec.patch
test/template_ipsec.py
test/test_ipsec_esp.py

index 8b722ef..c0773e2 100644 (file)
@@ -112,7 +112,7 @@ format_esp_post_encrypt_trace (u8 * s, va_list * args)
 /* pad packet in input buffer */
 static_always_inline u8 *
 esp_add_footer_and_icv (vlib_main_t * vm, vlib_buffer_t ** last,
-                       u8 block_size, u8 icv_sz,
+                       u8 esp_align, u8 icv_sz,
                        u16 * next, vlib_node_runtime_t * node,
                        u16 buffer_data_size, uword total_len)
 {
@@ -122,7 +122,7 @@ esp_add_footer_and_icv (vlib_main_t * vm, vlib_buffer_t ** last,
   };
 
   u16 min_length = total_len + sizeof (esp_footer_t);
-  u16 new_length = round_pow2 (min_length, block_size);
+  u16 new_length = round_pow2 (min_length, esp_align);
   u8 pad_bytes = new_length - min_length;
   esp_footer_t *f = (esp_footer_t *) (vlib_buffer_get_current (last[0]) +
                                      last[0]->current_length + pad_bytes);
@@ -587,7 +587,7 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
   u16 buffer_data_size = vlib_buffer_get_default_data_size (vm);
   u32 current_sa_index = ~0, current_sa_packets = 0;
   u32 current_sa_bytes = 0, spi = 0;
-  u8 block_sz = 0, iv_sz = 0, icv_sz = 0;
+  u8 esp_align = 4, iv_sz = 0, icv_sz = 0;
   ipsec_sa_t *sa0 = 0;
   vlib_buffer_t *lb;
   vnet_crypto_op_t **crypto_ops = &ptd->crypto_ops;
@@ -648,7 +648,7 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          sa0 = pool_elt_at_index (im->sad, sa_index0);
          current_sa_index = sa_index0;
          spi = clib_net_to_host_u32 (sa0->spi);
-         block_sz = sa0->crypto_block_size;
+         esp_align = sa0->esp_block_align;
          icv_sz = sa0->integ_icv_size;
          iv_sz = sa0->crypto_iv_size;
 
@@ -713,7 +713,7 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
       if (ipsec_sa_is_set_IS_TUNNEL (sa0))
        {
          payload = vlib_buffer_get_current (b[0]);
-         next_hdr_ptr = esp_add_footer_and_icv (vm, &lb, block_sz, icv_sz,
+         next_hdr_ptr = esp_add_footer_and_icv (vm, &lb, esp_align, icv_sz,
                                                 next, node,
                                                 buffer_data_size,
                                                 vlib_buffer_length_in_chain
@@ -789,7 +789,7 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 
          vlib_buffer_advance (b[0], ip_len);
          payload = vlib_buffer_get_current (b[0]);
-         next_hdr_ptr = esp_add_footer_and_icv (vm, &lb, block_sz, icv_sz,
+         next_hdr_ptr = esp_add_footer_and_icv (vm, &lb, esp_align, icv_sz,
                                                 next, node,
                                                 buffer_data_size,
                                                 vlib_buffer_length_in_chain
index 0ef9067..55f69f5 100644 (file)
@@ -450,44 +450,44 @@ ipsec_init (vlib_main_t * vm)
   a->dec_op_id = VNET_CRYPTO_OP_NONE;
   a->alg = VNET_CRYPTO_ALG_NONE;
   a->iv_size = 0;
-  a->block_size = 1;
+  a->block_align = 1;
 
   a = im->crypto_algs + IPSEC_CRYPTO_ALG_DES_CBC;
   a->enc_op_id = VNET_CRYPTO_OP_DES_CBC_ENC;
   a->dec_op_id = VNET_CRYPTO_OP_DES_CBC_DEC;
   a->alg = VNET_CRYPTO_ALG_DES_CBC;
-  a->iv_size = a->block_size = 8;
+  a->iv_size = a->block_align = 8;
 
   a = im->crypto_algs + IPSEC_CRYPTO_ALG_3DES_CBC;
   a->enc_op_id = VNET_CRYPTO_OP_3DES_CBC_ENC;
   a->dec_op_id = VNET_CRYPTO_OP_3DES_CBC_DEC;
   a->alg = VNET_CRYPTO_ALG_3DES_CBC;
-  a->iv_size = a->block_size = 8;
+  a->iv_size = a->block_align = 8;
 
   a = im->crypto_algs + IPSEC_CRYPTO_ALG_AES_CBC_128;
   a->enc_op_id = VNET_CRYPTO_OP_AES_128_CBC_ENC;
   a->dec_op_id = VNET_CRYPTO_OP_AES_128_CBC_DEC;
   a->alg = VNET_CRYPTO_ALG_AES_128_CBC;
-  a->iv_size = a->block_size = 16;
+  a->iv_size = a->block_align = 16;
 
   a = im->crypto_algs + IPSEC_CRYPTO_ALG_AES_CBC_192;
   a->enc_op_id = VNET_CRYPTO_OP_AES_192_CBC_ENC;
   a->dec_op_id = VNET_CRYPTO_OP_AES_192_CBC_DEC;
   a->alg = VNET_CRYPTO_ALG_AES_192_CBC;
-  a->iv_size = a->block_size = 16;
+  a->iv_size = a->block_align = 16;
 
   a = im->crypto_algs + IPSEC_CRYPTO_ALG_AES_CBC_256;
   a->enc_op_id = VNET_CRYPTO_OP_AES_256_CBC_ENC;
   a->dec_op_id = VNET_CRYPTO_OP_AES_256_CBC_DEC;
   a->alg = VNET_CRYPTO_ALG_AES_256_CBC;
-  a->iv_size = a->block_size = 16;
+  a->iv_size = a->block_align = 16;
 
   a = im->crypto_algs + IPSEC_CRYPTO_ALG_AES_GCM_128;
   a->enc_op_id = VNET_CRYPTO_OP_AES_128_GCM_ENC;
   a->dec_op_id = VNET_CRYPTO_OP_AES_128_GCM_DEC;
   a->alg = VNET_CRYPTO_ALG_AES_128_GCM;
   a->iv_size = 8;
-  a->block_size = 16;
+  a->block_align = 1;
   a->icv_size = 16;
 
   a = im->crypto_algs + IPSEC_CRYPTO_ALG_AES_GCM_192;
@@ -495,7 +495,7 @@ ipsec_init (vlib_main_t * vm)
   a->dec_op_id = VNET_CRYPTO_OP_AES_192_GCM_DEC;
   a->alg = VNET_CRYPTO_ALG_AES_192_GCM;
   a->iv_size = 8;
-  a->block_size = 16;
+  a->block_align = 1;
   a->icv_size = 16;
 
   a = im->crypto_algs + IPSEC_CRYPTO_ALG_AES_GCM_256;
@@ -503,7 +503,7 @@ ipsec_init (vlib_main_t * vm)
   a->dec_op_id = VNET_CRYPTO_OP_AES_256_GCM_DEC;
   a->alg = VNET_CRYPTO_ALG_AES_256_GCM;
   a->iv_size = 8;
-  a->block_size = 16;
+  a->block_align = 1;
   a->icv_size = 16;
 
   vec_validate (im->integ_algs, IPSEC_INTEG_N_ALG - 1);
index 603bc33..3d84ad3 100644 (file)
@@ -78,7 +78,7 @@ typedef struct
   vnet_crypto_op_id_t dec_op_id;
   vnet_crypto_alg_t alg;
   u8 iv_size;
-  u8 block_size;
+  u8 block_align;
   u8 icv_size;
 } ipsec_main_crypto_alg_t;
 
index e5f01bf..2ff3bee 100644 (file)
@@ -99,12 +99,12 @@ ipsec_sa_set_crypto_alg (ipsec_sa_t * sa, ipsec_crypto_alg_t crypto_alg)
   ipsec_main_t *im = &ipsec_main;
   sa->crypto_alg = crypto_alg;
   sa->crypto_iv_size = im->crypto_algs[crypto_alg].iv_size;
-  sa->crypto_block_size = im->crypto_algs[crypto_alg].block_size;
+  sa->esp_block_align = clib_max (4, im->crypto_algs[crypto_alg].block_align);
   sa->sync_op_data.crypto_enc_op_id = im->crypto_algs[crypto_alg].enc_op_id;
   sa->sync_op_data.crypto_dec_op_id = im->crypto_algs[crypto_alg].dec_op_id;
   sa->crypto_calg = im->crypto_algs[crypto_alg].alg;
   ASSERT (sa->crypto_iv_size <= ESP_MAX_IV_SIZE);
-  ASSERT (sa->crypto_block_size <= ESP_MAX_BLOCK_SIZE);
+  ASSERT (sa->esp_block_align <= ESP_MAX_BLOCK_SIZE);
   if (IPSEC_CRYPTO_ALG_IS_GCM (crypto_alg))
     {
       sa->integ_icv_size = im->crypto_algs[crypto_alg].icv_size;
index 5950d6f..5d65238 100644 (file)
@@ -113,7 +113,7 @@ typedef struct
   ipsec_sa_flags_t flags;
 
   u8 crypto_iv_size;
-  u8 crypto_block_size;
+  u8 esp_block_align;
   u8 integ_icv_size;
   u32 encrypt_thread_index;
   u32 decrypt_thread_index;
index 7ee8316..d97de7f 100644 (file)
@@ -1,5 +1,5 @@
 diff --git a/scapy/layers/ipsec.py b/scapy/layers/ipsec.py
-index f8c601fa..f566d288 100644
+index ae057ee1..24a1f6ea 100644
 --- a/scapy/layers/ipsec.py
 +++ b/scapy/layers/ipsec.py
 @@ -138,6 +138,7 @@ bind_layers(IP, ESP, proto=socket.IPPROTO_ESP)
@@ -9,8 +9,8 @@ index f8c601fa..f566d288 100644
 +bind_layers(UDP, ESP, dport=4545)  # NAT-Traversal encapsulation - random port
  
  ###############################################################################
-
-@@ -359,11 +359,8 @@ class CryptAlgo(object):
+@@ -359,11 +360,8 @@ class CryptAlgo(object):
              encryptor = cipher.encryptor()
  
              if self.is_aead:
@@ -24,7 +24,7 @@ index f8c601fa..f566d288 100644
                  data = encryptor.update(data) + encryptor.finalize()
                  data += encryptor.tag[:self.icv_size]
              else:
-@@ -401,12 +398,7 @@ class CryptAlgo(object):
+@@ -400,12 +398,7 @@ class CryptAlgo(object):
  
              if self.is_aead:
                  # Tag value check is done during the finalize method
@@ -38,7 +38,31 @@ index f8c601fa..f566d288 100644
              try:
                  data = decryptor.update(data) + decryptor.finalize()
              except InvalidTag as err:
-@@ -545,7 +537,7 @@ class AuthAlgo(object):
+@@ -445,6 +438,7 @@ if algorithms:
+     CRYPT_ALGOS['AES-CTR'] = CryptAlgo('AES-CTR',
+                                        cipher=algorithms.AES,
+                                        mode=modes.CTR,
++                                       block_size=1,
+                                        iv_size=8,
+                                        salt_size=4,
+                                        format_mode_iv=_aes_ctr_format_mode_iv)
+@@ -452,6 +446,7 @@ if algorithms:
+     CRYPT_ALGOS['AES-GCM'] = CryptAlgo('AES-GCM',
+                                        cipher=algorithms.AES,
+                                        mode=modes.GCM,
++                                       block_size=1,
+                                        salt_size=4,
+                                        iv_size=8,
+                                        icv_size=16,
+@@ -460,6 +455,7 @@ if algorithms:
+         CRYPT_ALGOS['AES-CCM'] = CryptAlgo('AES-CCM',
+                                            cipher=algorithms.AES,
+                                            mode=modes.CCM,
++                                           block_size=1,
+                                            iv_size=8,
+                                            salt_size=3,
+                                            icv_size=16,
+@@ -544,7 +540,7 @@ class AuthAlgo(object):
          else:
              return self.mac(key, self.digestmod(), default_backend())
  
@@ -47,7 +71,7 @@ index f8c601fa..f566d288 100644
          """
          Sign an IPsec (ESP or AH) packet with this algo.
  
-@@ -561,16 +553,20 @@ class AuthAlgo(object):
+@@ -560,16 +556,20 @@ class AuthAlgo(object):
  
          if pkt.haslayer(ESP):
              mac.update(raw(pkt[ESP]))
@@ -69,7 +93,7 @@ index f8c601fa..f566d288 100644
          """
          Check that the integrity check value (icv) of a packet is valid.
  
-@@ -602,6 +598,8 @@ class AuthAlgo(object):
+@@ -600,6 +600,8 @@ class AuthAlgo(object):
              clone = zero_mutable_fields(pkt.copy(), sending=False)
  
          mac.update(raw(clone))
@@ -78,7 +102,7 @@ index f8c601fa..f566d288 100644
          computed_icv = mac.finalize()[:self.icv_size]
  
          # XXX: Cannot use mac.verify because the ICV can be truncated
-@@ -864,6 +862,23 @@ class SecurityAssociation(object):
+@@ -862,6 +864,23 @@ class SecurityAssociation(object):
                  raise TypeError('nat_t_header must be %s' % UDP.name)
          self.nat_t_header = nat_t_header
  
@@ -102,7 +126,7 @@ index f8c601fa..f566d288 100644
      def check_spi(self, pkt):
          if pkt.spi != self.spi:
              raise TypeError('packet spi=0x%x does not match the SA spi=0x%x' %
-@@ -877,7 +892,8 @@ class SecurityAssociation(object):
+@@ -875,7 +894,8 @@ class SecurityAssociation(object):
              if len(iv) != self.crypt_algo.iv_size:
                  raise TypeError('iv length must be %s' % self.crypt_algo.iv_size)  # noqa: E501
  
@@ -112,7 +136,7 @@ index f8c601fa..f566d288 100644
  
          if self.tunnel_header:
              tunnel = self.tunnel_header.copy()
-@@ -901,7 +917,7 @@ class SecurityAssociation(object):
+@@ -899,7 +919,7 @@ class SecurityAssociation(object):
                                        esn_en=esn_en or self.esn_en,
                                        esn=esn or self.esn)
  
@@ -121,7 +145,7 @@ index f8c601fa..f566d288 100644
  
          if self.nat_t_header:
              nat_t_header = self.nat_t_header.copy()
-@@ -928,7 +944,8 @@ class SecurityAssociation(object):
+@@ -926,7 +946,8 @@ class SecurityAssociation(object):
  
      def _encrypt_ah(self, pkt, seq_num=None):
  
@@ -131,7 +155,7 @@ index f8c601fa..f566d288 100644
                  icv=b"\x00" * self.auth_algo.icv_size)
  
          if self.tunnel_header:
-@@ -968,7 +985,8 @@ class SecurityAssociation(object):
+@@ -966,7 +987,8 @@ class SecurityAssociation(object):
          else:
              ip_header.plen = len(ip_header.payload) + len(ah) + len(payload)
  
@@ -141,7 +165,7 @@ index f8c601fa..f566d288 100644
  
          # sequence number must always change, unless specified by the user
          if seq_num is None:
-@@ -1005,11 +1023,12 @@ class SecurityAssociation(object):
+@@ -1003,11 +1025,12 @@ class SecurityAssociation(object):
  
      def _decrypt_esp(self, pkt, verify=True, esn_en=None, esn=None):
  
@@ -155,7 +179,7 @@ index f8c601fa..f566d288 100644
  
          esp = self.crypt_algo.decrypt(self, encrypted, self.crypt_key,
                                        self.crypt_algo.icv_size or
-@@ -1050,11 +1069,12 @@ class SecurityAssociation(object):
+@@ -1048,9 +1071,10 @@ class SecurityAssociation(object):
  
      def _decrypt_ah(self, pkt, verify=True):
  
@@ -167,5 +191,3 @@ index f8c601fa..f566d288 100644
  
          ah = pkt[AH]
          payload = ah.payload
-
index 083b0b9..7eb0123 100644 (file)
@@ -1,5 +1,5 @@
 diff --git a/scapy/layers/ipsec.py b/scapy/layers/ipsec.py
-index 69e7ae3b..3a1724b2 100644
+index 69e7ae3b..d9c1705b 100644
 --- a/scapy/layers/ipsec.py
 +++ b/scapy/layers/ipsec.py
 @@ -344,8 +344,7 @@ class CryptAlgo(object):
@@ -24,7 +24,31 @@ index 69e7ae3b..3a1724b2 100644
              try:
                  data = decryptor.update(data) + decryptor.finalize()
              except InvalidTag as err:
-@@ -518,12 +514,16 @@ class AuthAlgo(object):
+@@ -422,6 +418,7 @@ if algorithms:
+     CRYPT_ALGOS['AES-CTR'] = CryptAlgo('AES-CTR',
+                                        cipher=algorithms.AES,
+                                        mode=modes.CTR,
++                                       block_size=1,
+                                        iv_size=8,
+                                        salt_size=4,
+                                        format_mode_iv=_aes_ctr_format_mode_iv)
+@@ -429,6 +426,7 @@ if algorithms:
+     CRYPT_ALGOS['AES-GCM'] = CryptAlgo('AES-GCM',
+                                        cipher=algorithms.AES,
+                                        mode=modes.GCM,
++                                       block_size=1,
+                                        salt_size=4,
+                                        iv_size=8,
+                                        icv_size=16,
+@@ -437,6 +435,7 @@ if algorithms:
+         CRYPT_ALGOS['AES-CCM'] = CryptAlgo('AES-CCM',
+                                            cipher=algorithms.AES,
+                                            mode=modes.CCM,
++                                           block_size=1,
+                                            iv_size=8,
+                                            salt_size=3,
+                                            icv_size=16,
+@@ -518,12 +517,16 @@ class AuthAlgo(object):
          else:
              return self.mac(key, self.digestmod(), default_backend())
  
@@ -42,7 +66,7 @@ index 69e7ae3b..3a1724b2 100644
  
          @return: the signed packet
          """
-@@ -534,16 +534,20 @@ class AuthAlgo(object):
+@@ -534,16 +537,20 @@ class AuthAlgo(object):
  
          if pkt.haslayer(ESP):
              mac.update(raw(pkt[ESP]))
@@ -64,7 +88,7 @@ index 69e7ae3b..3a1724b2 100644
          """
          Check that the integrity check value (icv) of a packet is valid.
  
-@@ -574,6 +578,8 @@ class AuthAlgo(object):
+@@ -574,6 +581,8 @@ class AuthAlgo(object):
              clone = zero_mutable_fields(pkt.copy(), sending=False)
  
          mac.update(raw(clone))
@@ -73,7 +97,7 @@ index 69e7ae3b..3a1724b2 100644
          computed_icv = mac.finalize()[:self.icv_size]
  
          # XXX: Cannot use mac.verify because the ICV can be truncated
-@@ -757,7 +763,8 @@ class SecurityAssociation(object):
+@@ -757,7 +766,8 @@ class SecurityAssociation(object):
      SUPPORTED_PROTOS = (IP, IPv6)
  
      def __init__(self, proto, spi, seq_num=1, crypt_algo=None, crypt_key=None,
@@ -83,7 +107,7 @@ index 69e7ae3b..3a1724b2 100644
          """
          @param proto: the IPsec proto to use (ESP or AH)
          @param spi: the Security Parameters Index of this SA
-@@ -771,6 +778,7 @@ class SecurityAssociation(object):
+@@ -771,6 +781,7 @@ class SecurityAssociation(object):
                                to encapsulate the encrypted packets.
          @param nat_t_header: an instance of a UDP header that will be used
                               for NAT-Traversal.
@@ -91,7 +115,7 @@ index 69e7ae3b..3a1724b2 100644
          """
  
          if proto not in (ESP, AH, ESP.name, AH.name):
-@@ -782,6 +790,7 @@ class SecurityAssociation(object):
+@@ -782,6 +793,7 @@ class SecurityAssociation(object):
  
          self.spi = spi
          self.seq_num = seq_num
@@ -99,7 +123,7 @@ index 69e7ae3b..3a1724b2 100644
  
          if crypt_algo:
              if crypt_algo not in CRYPT_ALGOS:
-@@ -827,6 +836,23 @@ class SecurityAssociation(object):
+@@ -827,6 +839,23 @@ class SecurityAssociation(object):
              raise TypeError('packet spi=0x%x does not match the SA spi=0x%x' %
                              (pkt.spi, self.spi))
  
@@ -123,7 +147,7 @@ index 69e7ae3b..3a1724b2 100644
      def _encrypt_esp(self, pkt, seq_num=None, iv=None):
  
          if iv is None:
-@@ -835,7 +861,8 @@ class SecurityAssociation(object):
+@@ -835,7 +864,8 @@ class SecurityAssociation(object):
              if len(iv) != self.crypt_algo.iv_size:
                  raise TypeError('iv length must be %s' % self.crypt_algo.iv_size)
  
@@ -133,7 +157,7 @@ index 69e7ae3b..3a1724b2 100644
  
          if self.tunnel_header:
              tunnel = self.tunnel_header.copy()
-@@ -857,7 +884,7 @@ class SecurityAssociation(object):
+@@ -857,7 +887,7 @@ class SecurityAssociation(object):
          esp = self.crypt_algo.pad(esp)
          esp = self.crypt_algo.encrypt(self, esp, self.crypt_key)
  
@@ -142,7 +166,7 @@ index 69e7ae3b..3a1724b2 100644
  
          if self.nat_t_header:
              nat_t_header = self.nat_t_header.copy()
-@@ -884,7 +911,8 @@ class SecurityAssociation(object):
+@@ -884,7 +914,8 @@ class SecurityAssociation(object):
  
      def _encrypt_ah(self, pkt, seq_num=None):
  
@@ -152,7 +176,7 @@ index 69e7ae3b..3a1724b2 100644
                  icv = b"\x00" * self.auth_algo.icv_size)
  
          if self.tunnel_header:
-@@ -924,7 +952,8 @@ class SecurityAssociation(object):
+@@ -924,7 +955,8 @@ class SecurityAssociation(object):
          else:
              ip_header.plen = len(ip_header.payload) + len(ah) + len(payload)
  
@@ -162,7 +186,7 @@ index 69e7ae3b..3a1724b2 100644
  
          # sequence number must always change, unless specified by the user
          if seq_num is None:
-@@ -955,11 +984,12 @@ class SecurityAssociation(object):
+@@ -955,11 +987,12 @@ class SecurityAssociation(object):
  
      def _decrypt_esp(self, pkt, verify=True):
  
@@ -176,7 +200,7 @@ index 69e7ae3b..3a1724b2 100644
  
          esp = self.crypt_algo.decrypt(self, encrypted, self.crypt_key,
                                        self.crypt_algo.icv_size or
-@@ -998,9 +1028,11 @@ class SecurityAssociation(object):
+@@ -998,9 +1031,11 @@ class SecurityAssociation(object):
  
      def _decrypt_ah(self, pkt, verify=True):
  
index c3484e1..532a7a0 100644 (file)
@@ -803,6 +803,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:
@@ -811,9 +820,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)
index 7448df1..dbd21f1 100644 (file)
@@ -1,7 +1,7 @@
 import socket
 import unittest
 from scapy.layers.ipsec import ESP
-from scapy.layers.inet import UDP
+from scapy.layers.inet import IP, ICMP, UDP
 
 from parameterized import parameterized
 from framework import VppTestRunner
@@ -544,6 +544,8 @@ class RunTestIpsecEspAll(ConfigIpsecESP,
         self.run_a_test(self.engine, self.flag, self.algo)
 
     def run_a_test(self, engine, flag, algo, payload_size=None):
+        if engine == "ia32":
+            engine = "native"
         self.vapi.cli("set crypto handler all %s" % engine)
 
         self.ipv4_params = IPsecIPv4Params()
@@ -579,8 +581,16 @@ class RunTestIpsecEspAll(ConfigIpsecESP,
         self.verify_tra_basic4(count=NUM_PKTS)
         self.verify_tun_66(self.params[socket.AF_INET6],
                            count=NUM_PKTS)
+        #
+        # Use an odd-byte payload size to check for correct padding.
+        #
+        # 49 + 2 == 51 which should pad +1 to 52 for 4 byte alignment, +5
+        # to 56 for 8 byte alignment, and +13 to 64 for 64 byte alignment.
+        # This should catch bugs where the code is incorrectly over-padding
+        # for algorithms that don't require it
+        psz = 49 - len(IP()/ICMP()) if payload_size is None else payload_size
         self.verify_tun_44(self.params[socket.AF_INET],
-                           count=NUM_PKTS)
+                           count=NUM_PKTS, payload_size=psz)
 
         LARGE_PKT_SZ = [
             1970,  # results in 2 chained buffers entering decrypt node