From patchwork Wed May 8 10:47:15 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marcin Smoczynski X-Patchwork-Id: 53319 X-Patchwork-Delegate: gakhil@marvell.com Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 2EA3C378B; Wed, 8 May 2019 12:47:52 +0200 (CEST) Received: from mga06.intel.com (mga06.intel.com [134.134.136.31]) by dpdk.org (Postfix) with ESMTP id C68D0374C for ; Wed, 8 May 2019 12:47:50 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga104.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 08 May 2019 03:47:49 -0700 X-ExtLoop1: 1 Received: from msmoczyx-mobl.ger.corp.intel.com ([10.103.104.100]) by fmsmga001.fm.intel.com with ESMTP; 08 May 2019 03:47:46 -0700 From: Marcin Smoczynski To: marko.kovacevic@intel.com, orika@mellanox.com, bruce.richardson@intel.com, pablo.de.lara.guarch@intel.com, radu.nicolau@intel.com, akhil.goyal@nxp.com, tomasz.kantecki@intel.com, konstantin.ananyev@intel.com, bernard.iremonger@intel.com, olivier.matz@6wind.com Cc: dev@dpdk.org, Marcin Smoczynski Date: Wed, 8 May 2019 12:47:15 +0200 Message-Id: <20190508104717.13448-1-marcinx.smoczynski@intel.com> X-Mailer: git-send-email 2.21.0.windows.1 MIME-Version: 1.0 Subject: [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Introduce new function for IPv6 header extension parsing able to determine extension length and next protocol number. This function is helpful when implementing IPv6 header traversing. Signed-off-by: Marcin Smoczynski Acked-by: Konstantin Ananyev --- lib/librte_net/rte_ip.h | 49 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/lib/librte_net/rte_ip.h b/lib/librte_net/rte_ip.h index f9b909090..be64da662 100644 --- a/lib/librte_net/rte_ip.h +++ b/lib/librte_net/rte_ip.h @@ -425,6 +425,55 @@ rte_ipv6_udptcp_cksum(const struct ipv6_hdr *ipv6_hdr, const void *l4_hdr) return (uint16_t)cksum; } +/* IPv6 fragmentation header size */ +#define RTE_IPV6_FRAG_HDR_SIZE 8 + +/** + * Parse next IPv6 header extension + * + * This function checks if proto number is an IPv6 extensions and parses its + * data if so, providing information on next header and extension length. + * + * @param p + * Pointer to an extension raw data. + * @param proto + * Protocol number extracted from the "next header" field from + * the IPv6 header or the previous extension. + * @param ext_len + * Extension data length. + * @return + * next protocol number if proto is an IPv6 extension, -EINVAL otherwise + */ +static inline int __rte_experimental +rte_ipv6_get_next_ext(uint8_t *p, int proto, size_t *ext_len) +{ + int next_proto; + + switch (proto) { + case IPPROTO_AH: + next_proto = *p++; + *ext_len = (*p + 2) * sizeof(uint32_t); + break; + + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: + next_proto = *p++; + *ext_len = (*p + 1) * sizeof(uint64_t); + break; + + case IPPROTO_FRAGMENT: + next_proto = *p; + *ext_len = RTE_IPV6_FRAG_HDR_SIZE; + break; + + default: + return -EINVAL; + } + + return next_proto; +} + #ifdef __cplusplus } #endif From patchwork Wed May 8 10:47:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marcin Smoczynski X-Patchwork-Id: 53320 X-Patchwork-Delegate: gakhil@marvell.com Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id DC79649E0; Wed, 8 May 2019 12:48:06 +0200 (CEST) Received: from mga12.intel.com (mga12.intel.com [192.55.52.136]) by dpdk.org (Postfix) with ESMTP id 513874C77 for ; Wed, 8 May 2019 12:48:05 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 08 May 2019 03:48:04 -0700 X-ExtLoop1: 1 Received: from msmoczyx-mobl.ger.corp.intel.com ([10.103.104.100]) by fmsmga001.fm.intel.com with ESMTP; 08 May 2019 03:48:01 -0700 From: Marcin Smoczynski To: marko.kovacevic@intel.com, orika@mellanox.com, bruce.richardson@intel.com, pablo.de.lara.guarch@intel.com, radu.nicolau@intel.com, akhil.goyal@nxp.com, tomasz.kantecki@intel.com, konstantin.ananyev@intel.com, bernard.iremonger@intel.com, olivier.matz@6wind.com Cc: dev@dpdk.org, Marcin Smoczynski Date: Wed, 8 May 2019 12:47:16 +0200 Message-Id: <20190508104717.13448-2-marcinx.smoczynski@intel.com> X-Mailer: git-send-email 2.21.0.windows.1 In-Reply-To: <20190508104717.13448-1-marcinx.smoczynski@intel.com> References: <20190508104717.13448-1-marcinx.smoczynski@intel.com> MIME-Version: 1.0 Subject: [dpdk-dev] [PATCH 2/3] ipsec: fix transport mode for ipv6 with extensions X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Reconstructing IPv6 header after encryption or decryption requires updating 'next header' value in the preceding protocol header, which is determined by parsing IPv6 header and iteratively looking for next IPv6 header extension. It is required that 'l3_len' in the mbuf metadata contains a total length of the IPv6 header with header extensions up to ESP header. Signed-off-by: Marcin Smoczynski Acked-by: Konstantin Ananyev --- lib/Makefile | 3 ++- lib/librte_ipsec/iph.h | 55 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 791e0d991..3ad579f68 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -108,7 +108,8 @@ DEPDIRS-librte_gso += librte_mempool DIRS-$(CONFIG_RTE_LIBRTE_BPF) += librte_bpf DEPDIRS-librte_bpf := librte_eal librte_mempool librte_mbuf librte_ethdev DIRS-$(CONFIG_RTE_LIBRTE_IPSEC) += librte_ipsec -DEPDIRS-librte_ipsec := librte_eal librte_mbuf librte_cryptodev librte_security +DEPDIRS-librte_ipsec := librte_eal librte_mbuf librte_cryptodev librte_security \ + librte_net DIRS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += librte_telemetry DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev DIRS-$(CONFIG_RTE_LIBRTE_RCU) += librte_rcu diff --git a/lib/librte_ipsec/iph.h b/lib/librte_ipsec/iph.h index 58930cf18..082e4e73e 100644 --- a/lib/librte_ipsec/iph.h +++ b/lib/librte_ipsec/iph.h @@ -5,6 +5,8 @@ #ifndef _IPH_H_ #define _IPH_H_ +#include + /** * @file iph.h * Contains functions/structures/macros to manipulate IPv4/IPv6 headers @@ -40,24 +42,61 @@ static inline int update_trs_l3hdr(const struct rte_ipsec_sa *sa, void *p, uint32_t plen, uint32_t l2len, uint32_t l3len, uint8_t proto) { - struct ipv4_hdr *v4h; - struct ipv6_hdr *v6h; int32_t rc; + /* IPv4 */ if ((sa->type & RTE_IPSEC_SATP_IPV_MASK) == RTE_IPSEC_SATP_IPV4) { + struct ipv4_hdr *v4h; + v4h = p; rc = v4h->next_proto_id; v4h->next_proto_id = proto; v4h->total_length = rte_cpu_to_be_16(plen - l2len); - } else if (l3len == sizeof(*v6h)) { + /* IPv6 */ + } else { + struct ipv6_hdr *v6h; + uint8_t *next_proto_off; + v6h = p; - rc = v6h->proto; - v6h->proto = proto; + + /* basic IPv6 header with no extensions */ + if (l3len == sizeof(struct ipv6_hdr)) + next_proto_off = &v6h->proto; + + /* IPv6 with extensions */ + else { + size_t ext_len; + int nh; + uint8_t *pd, *plimit; + + /* locate last extension within l3len bytes */ + pd = (uint8_t *)p; + plimit = pd + l3len; + ext_len = sizeof(struct ipv6_hdr); + nh = v6h->proto; + while (pd + ext_len < plimit) { + pd += ext_len; + nh = rte_ipv6_get_next_ext(pd, nh, &ext_len); + if (unlikely(nh < 0)) + return -EINVAL; + } + + /* invalid l3len - extension exceeds header length */ + if (unlikely(pd + ext_len != plimit)) + return -EINVAL; + + /* save last extension offset */ + next_proto_off = pd; + } + + /* update header type; return original value */ + rc = *next_proto_off; + *next_proto_off = proto; + + /* fix packet length */ v6h->payload_len = rte_cpu_to_be_16(plen - l2len - sizeof(*v6h)); - /* need to add support for IPv6 with options */ - } else - rc = -ENOTSUP; + } return rc; } From patchwork Wed May 8 10:47:17 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marcin Smoczynski X-Patchwork-Id: 53321 X-Patchwork-Delegate: gakhil@marvell.com Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 9939F4C94; Wed, 8 May 2019 12:48:10 +0200 (CEST) Received: from mga12.intel.com (mga12.intel.com [192.55.52.136]) by dpdk.org (Postfix) with ESMTP id ACF154C93 for ; Wed, 8 May 2019 12:48:08 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 08 May 2019 03:48:08 -0700 X-ExtLoop1: 1 Received: from msmoczyx-mobl.ger.corp.intel.com ([10.103.104.100]) by fmsmga001.fm.intel.com with ESMTP; 08 May 2019 03:48:05 -0700 From: Marcin Smoczynski To: marko.kovacevic@intel.com, orika@mellanox.com, bruce.richardson@intel.com, pablo.de.lara.guarch@intel.com, radu.nicolau@intel.com, akhil.goyal@nxp.com, tomasz.kantecki@intel.com, konstantin.ananyev@intel.com, bernard.iremonger@intel.com, olivier.matz@6wind.com Cc: dev@dpdk.org, Marcin Smoczynski Date: Wed, 8 May 2019 12:47:17 +0200 Message-Id: <20190508104717.13448-3-marcinx.smoczynski@intel.com> X-Mailer: git-send-email 2.21.0.windows.1 In-Reply-To: <20190508104717.13448-1-marcinx.smoczynski@intel.com> References: <20190508104717.13448-1-marcinx.smoczynski@intel.com> MIME-Version: 1.0 Subject: [dpdk-dev] [PATCH 3/3] examples/ipsec-secgw: add support for ipv6 options X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Using transport with IPv6 and header extensions requires calculating total header length including extensions up to ESP header which is achieved with iteratively parsing extensions when preparing traffic for processing. Calculated l3_len is later used to determine SPI field offset for an inbound traffic and to reconstruct L3 header by librte_ipsec. A simple unittest script is provided to test various headers for the IPv6 transport mode. Within each test case a test packet is crafted with Scapy and sent as an inbound or outbound traffic. Application response is then checked with a set of assertions. Signed-off-by: Marcin Smoczynski --- examples/ipsec-secgw/ipsec-secgw.c | 33 +++- examples/ipsec-secgw/sa.c | 5 +- examples/ipsec-secgw/test/test-scapy.py | 231 ++++++++++++++++++++++++ 3 files changed, 260 insertions(+), 9 deletions(-) create mode 100755 examples/ipsec-secgw/test/test-scapy.py diff --git a/examples/ipsec-secgw/ipsec-secgw.c b/examples/ipsec-secgw/ipsec-secgw.c index 478dd80c2..1c49aa22c 100644 --- a/examples/ipsec-secgw/ipsec-secgw.c +++ b/examples/ipsec-secgw/ipsec-secgw.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "ipsec.h" #include "parser.h" @@ -248,16 +249,38 @@ prepare_one_packet(struct rte_mbuf *pkt, struct ipsec_traffic *t) pkt->l2_len = 0; pkt->l3_len = sizeof(struct ip); } else if (eth->ether_type == rte_cpu_to_be_16(ETHER_TYPE_IPv6)) { - nlp = (uint8_t *)rte_pktmbuf_adj(pkt, ETHER_HDR_LEN); - nlp = RTE_PTR_ADD(nlp, offsetof(struct ip6_hdr, ip6_nxt)); - if (*nlp == IPPROTO_ESP) + int next_proto; + size_t l3len, ext_len; + struct ipv6_hdr *v6h; + uint8_t *p; + + /* get protocol type */ + v6h = (struct ipv6_hdr *)rte_pktmbuf_adj(pkt, ETHER_HDR_LEN); + next_proto = v6h->proto; + + /* determine l3 header size up to ESP extension */ + l3len = sizeof(struct ip6_hdr); + p = rte_pktmbuf_mtod(pkt, uint8_t *); + while (next_proto != IPPROTO_ESP && l3len < pkt->data_len && + (next_proto = rte_ipv6_get_next_ext(p + l3len, + next_proto, &ext_len)) >= 0) + l3len += ext_len; + + /* drop packet when IPv6 header exceeds first segment length */ + if (unlikely(l3len > pkt->data_len)) { + rte_pktmbuf_free(pkt); + return; + } + + if (next_proto == IPPROTO_ESP) t->ipsec.pkts[(t->ipsec.num)++] = pkt; else { - t->ip6.data[t->ip6.num] = nlp; + t->ip6.data[t->ip6.num] = rte_pktmbuf_mtod_offset(pkt, + uint8_t *, offsetof(struct ipv6_hdr, proto)); t->ip6.pkts[(t->ip6.num)++] = pkt; } pkt->l2_len = 0; - pkt->l3_len = sizeof(struct ip6_hdr); + pkt->l3_len = l3len; } else { /* Unknown/Unsupported type, drop the packet */ RTE_LOG(ERR, IPSEC, "Unsupported packet type 0x%x\n", diff --git a/examples/ipsec-secgw/sa.c b/examples/ipsec-secgw/sa.c index b850e9839..607527d08 100644 --- a/examples/ipsec-secgw/sa.c +++ b/examples/ipsec-secgw/sa.c @@ -1228,10 +1228,7 @@ single_inbound_lookup(struct ipsec_sa *sadb, struct rte_mbuf *pkt, *sa_ret = NULL; ip = rte_pktmbuf_mtod(pkt, struct ip *); - if (ip->ip_v == IPVERSION) - esp = (struct esp_hdr *)(ip + 1); - else - esp = (struct esp_hdr *)(((struct ip6_hdr *)ip) + 1); + esp = rte_pktmbuf_mtod_offset(pkt, struct esp_hdr *, pkt->l3_len); if (esp->spi == INVALID_SPI) return; diff --git a/examples/ipsec-secgw/test/test-scapy.py b/examples/ipsec-secgw/test/test-scapy.py new file mode 100755 index 000000000..d7f66b734 --- /dev/null +++ b/examples/ipsec-secgw/test/test-scapy.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python3 + +# Run DPDK IPsec example with following arguments: +# ./dpdk-ipsec-secgw --log-level=31 -l 0 --vdev=crypto_openssl --vdev=net_tap0 --vdev=net_tap1 -- -P -p 0x3 -u 0x1 --config "(0,0,0),(1,0,0)" -f test-transport.cfg -l +# Two tap ports are expected: 0: unprotected (remote), 1: protected (local) + +# sample configuration: +# sp ipv6 out esp protect 5 pri 1 \ +# src 1111:0000:0000:0000:0000:0000:0000:0000/64 \ +# dst 2222:0000:0000:0000:0000:0000:0000:0000/64 \ +# sport 0:65535 dport 0:65535 +# +# sp ipv6 in esp protect 6 pri 1 \ +# src 2222:0000:0000:0000:0000:0000:0000:0000/64 \ +# dst 1111:0000:0000:0000:0000:0000:0000:0000/64 \ +# sport 0:65535 dport 0:65535 +# +# sa out 5 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \ +# auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \ +# mode transport +# +# sa in 6 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \ +# auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \ +# mode transport +# +# rt ipv6 dst 1111:0000:0000:0000:0000:0000:0000:0000/64 port 1 +# rt ipv6 dst 2222:0000:0000:0000:0000:0000:0000:0000/64 port 0 +# +# run tests with: +# python3 -m unittest test-scapy + + +import socket +import sys +import unittest +from scapy.all import * + + +SRC_ETHER = "52:54:00:00:00:01" +DST_ETHER = "52:54:00:00:00:02" +SRC_ADDR = "1111::1" +DST_ADDR = "2222::1" +LOCAL_IFACE = "dtap1" +REMOTE_IFACE = "dtap0" + + +class Interface(object): + ETH_P_ALL = 3 + MAX_PACKET_SIZE = 1280 + def __init__(self, ifname): + self.name = ifname + self.s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL)) + self.s.bind((self.name, 0, socket.PACKET_OTHERHOST)) + + def __del__(self): + self.s.close() + + def send_packet(self, pkt): + self.send_bytes(bytes(pkt)) + + def send_bytes(self, bytedata): + self.s.send(bytedata) + + def recv_packet(self): + return Ether(self.recv_bytes()) + + def recv_bytes(self): + return self.s.recv(Interface.MAX_PACKET_SIZE) + + +class TestTransportMode(unittest.TestCase): + # There is a bug in the IPsec Scapy implementation + # which causes invalid packet reconstruction after + # successful decryption. This method is a workaround. + @staticmethod + def decrypt(pkt, sa): + esp = pkt[ESP] + + # decrypt dummy packet with no extensions + d = sa.decrypt(IPv6()/esp) + + # fix 'next header' in the preceding header of the original + # packet and remove ESP + pkt[ESP].underlayer.nh = d[IPv6].nh + pkt[ESP].underlayer.remove_payload() + + # combine L3 header with decrypted payload + npkt = pkt/d[IPv6].payload + + # fix length + npkt[IPv6].plen = d[IPv6].plen + len(pkt[IPv6].payload) + + return npkt + + def setUp(self): + self.ilocal = Interface(LOCAL_IFACE) + self.iremote = Interface(REMOTE_IFACE) + self.outb_sa = SecurityAssociation(ESP, spi=5, crypt_algo='AES-CBC', crypt_key='\x00'*16, auth_algo='HMAC-SHA1-96', auth_key='\x00'*20) + self.inb_sa = SecurityAssociation(ESP, spi=6, crypt_algo='AES-CBC', crypt_key='\x00'*16, auth_algo='HMAC-SHA1-96', auth_key='\x00'*20) + + def test_outb_ipv6_noopt(self): + pkt = Ether(src=SRC_ETHER, dst=DST_ETHER) + pkt /= IPv6(src=SRC_ADDR, dst=DST_ADDR) + pkt /= UDP(sport=123,dport=456)/Raw(load="abc") + self.ilocal.send_packet(pkt) + + # check response + resp = self.iremote.recv_packet() + self.assertEqual(resp[IPv6].nh, socket.IPPROTO_ESP) + self.assertEqual(resp[ESP].spi, 5) + + # decrypt response, check packet after decryption + d = TestTransportMode.decrypt(resp[IPv6], self.outb_sa) + self.assertEqual(d[IPv6].nh, socket.IPPROTO_UDP) + self.assertEqual(d[UDP].sport, 123) + self.assertEqual(d[UDP].dport, 456) + self.assertEqual(bytes(d[UDP].payload), b'abc') + + def test_outb_ipv6_opt(self): + hoptions = [] + hoptions.append(RouterAlert(value=2)) + hoptions.append(Jumbo(jumboplen=5000)) + hoptions.append(Pad1()) + + doptions = [] + doptions.append(HAO(hoa="1234::4321")) + + pkt = Ether(src=SRC_ETHER, dst=DST_ETHER) + pkt /= IPv6(src=SRC_ADDR, dst=DST_ADDR) + pkt /= IPv6ExtHdrHopByHop(options=hoptions) + pkt /= IPv6ExtHdrRouting(addresses=["3333::3","4444::4"]) + pkt /= IPv6ExtHdrDestOpt(options=doptions) + pkt /= UDP(sport=123,dport=456)/Raw(load="abc") + self.ilocal.send_packet(pkt) + + # check response + resp = self.iremote.recv_packet() + self.assertEqual(resp[IPv6].nh, socket.IPPROTO_HOPOPTS) + + # check extensions + self.assertEqual(resp[IPv6ExtHdrHopByHop].nh, socket.IPPROTO_ROUTING) + self.assertEqual(resp[IPv6ExtHdrRouting].nh, socket.IPPROTO_DSTOPTS) + self.assertEqual(resp[IPv6ExtHdrDestOpt].nh, socket.IPPROTO_ESP) + + # check ESP + self.assertEqual(resp[ESP].spi, 5) + + # decrypt response, check packet after decryption + d = TestTransportMode.decrypt(resp[IPv6], self.outb_sa) + self.assertEqual(d[IPv6].nh, socket.IPPROTO_HOPOPTS) + self.assertEqual(d[IPv6ExtHdrHopByHop].nh, socket.IPPROTO_ROUTING) + self.assertEqual(d[IPv6ExtHdrRouting].nh, socket.IPPROTO_DSTOPTS) + self.assertEqual(d[IPv6ExtHdrDestOpt].nh, socket.IPPROTO_UDP) + + # check UDP + self.assertEqual(d[UDP].sport, 123) + self.assertEqual(d[UDP].dport, 456) + self.assertEqual(bytes(d[UDP].payload), b'abc') + + def test_inb_ipv6_noopt(self): + # encrypt and send raw UDP packet + pkt = IPv6(src=DST_ADDR, dst=SRC_ADDR) + pkt /= UDP(sport=123,dport=456)/Raw(load="abc") + e = self.inb_sa.encrypt(pkt) + e = Ether(src=DST_ETHER, dst=SRC_ETHER)/e + self.iremote.send_packet(e) + + # check response + resp = self.ilocal.recv_packet() + self.assertEqual(resp[IPv6].nh, socket.IPPROTO_UDP) + + # check UDP packet + self.assertEqual(resp[UDP].sport, 123) + self.assertEqual(resp[UDP].dport, 456) + self.assertEqual(bytes(resp[UDP].payload), b'abc') + + def test_inb_ipv6_opt(self): + hoptions = [] + hoptions.append(RouterAlert(value=2)) + hoptions.append(Jumbo(jumboplen=5000)) + hoptions.append(Pad1()) + + doptions = [] + doptions.append(HAO(hoa="1234::4321")) + + # prepare packet with options + pkt = IPv6(src=DST_ADDR, dst=SRC_ADDR) + pkt /= IPv6ExtHdrHopByHop(options=hoptions) + pkt /= IPv6ExtHdrRouting(addresses=["3333::3","4444::4"]) + pkt /= IPv6ExtHdrDestOpt(options=doptions) + pkt /= UDP(sport=123,dport=456)/Raw(load="abc") + + # encrypt and send packet + e = self.inb_sa.encrypt(pkt) + e = Ether(src=DST_ETHER, dst=SRC_ETHER)/e + self.iremote.send_packet(e) + + # check response + resp = self.ilocal.recv_packet() + self.assertEqual(resp[IPv6].nh, socket.IPPROTO_HOPOPTS) + self.assertEqual(resp[IPv6ExtHdrHopByHop].nh, socket.IPPROTO_ROUTING) + self.assertEqual(resp[IPv6ExtHdrRouting].nh, socket.IPPROTO_DSTOPTS) + self.assertEqual(resp[IPv6ExtHdrDestOpt].nh, socket.IPPROTO_UDP) + + # check UDP + self.assertEqual(resp[UDP].sport, 123) + self.assertEqual(resp[UDP].dport, 456) + self.assertEqual(bytes(resp[UDP].payload), b'abc') + + def test_inb_ipv6_frag(self): + # prepare ESP payload + pkt = UDP(sport=123,dport=456)/Raw(load="abc") + e = self.inb_sa.encrypt(IPv6()/pkt) + + # craft and send inbound packet + e = Ether(src=DST_ETHER, dst=SRC_ETHER)/IPv6(src=DST_ADDR, dst=SRC_ADDR)/IPv6ExtHdrFragment()/e[IPv6].payload + self.iremote.send_packet(e) + + # check response + resp = self.ilocal.recv_packet() + self.assertEqual(resp[IPv6].nh, socket.IPPROTO_FRAGMENT) + self.assertEqual(resp[IPv6ExtHdrFragment].nh, socket.IPPROTO_UDP) + + # check UDP + self.assertEqual(resp[UDP].sport, 123) + self.assertEqual(resp[UDP].dport, 456) + self.assertEqual(bytes(resp[UDP].payload), b'abc') + + +if __name__ == "__main__": + unittest.main()