ipsec: fix padding/alignment for native IPsec encryption

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
This commit is contained in:
Christian Hopps
2019-11-03 07:02:15 -05:00
committed by Damjan Marion
parent dce44e4e23
commit fb7e7ed2cd
9 changed files with 120 additions and 52 deletions

View 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)