From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by dpdk.space (Postfix) with ESMTP id 5BD24A00E6 for ; Tue, 14 May 2019 14:51:23 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 7F8FCD18F; Tue, 14 May 2019 14:51:22 +0200 (CEST) Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by dpdk.org (Postfix) with ESMTP id 746237D05 for ; Tue, 14 May 2019 14:51:20 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by orsmga105.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 14 May 2019 05:51:19 -0700 X-ExtLoop1: 1 Received: from irsmsx151.ger.corp.intel.com ([163.33.192.59]) by FMSMGA003.fm.intel.com with ESMTP; 14 May 2019 05:51:17 -0700 Received: from irsmsx105.ger.corp.intel.com ([169.254.7.155]) by IRSMSX151.ger.corp.intel.com ([169.254.4.155]) with mapi id 14.03.0415.000; Tue, 14 May 2019 13:51:17 +0100 From: "Ananyev, Konstantin" To: "Smoczynski, MarcinX" , "Kovacevic, Marko" , "orika@mellanox.com" , "Richardson, Bruce" , "De Lara Guarch, Pablo" , "Nicolau, Radu" , "akhil.goyal@nxp.com" , "Kantecki, Tomasz" , "Iremonger, Bernard" , "olivier.matz@6wind.com" CC: "dev@dpdk.org" Thread-Topic: [PATCH 3/3] examples/ipsec-secgw: add support for ipv6 options Thread-Index: AQHVBYuOTrs8avqdW0W85ML539BbsKZqm9lA Date: Tue, 14 May 2019 12:51:16 +0000 Message-ID: <2601191342CEEE43887BDE71AB9772580161632A52@irsmsx105.ger.corp.intel.com> References: <20190508104717.13448-1-marcinx.smoczynski@intel.com> <20190508104717.13448-3-marcinx.smoczynski@intel.com> In-Reply-To: <20190508104717.13448-3-marcinx.smoczynski@intel.com> Accept-Language: en-IE, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-titus-metadata-40: eyJDYXRlZ29yeUxhYmVscyI6IiIsIk1ldGFkYXRhIjp7Im5zIjoiaHR0cDpcL1wvd3d3LnRpdHVzLmNvbVwvbnNcL0ludGVsMyIsImlkIjoiZGRiYzkzNTYtZTdhYS00NmJhLWIwZDktM2I4ZGI0ZDI5MjNlIiwicHJvcHMiOlt7Im4iOiJDVFBDbGFzc2lmaWNhdGlvbiIsInZhbHMiOlt7InZhbHVlIjoiQ1RQX05UIn1dfV19LCJTdWJqZWN0TGFiZWxzIjpbXSwiVE1DVmVyc2lvbiI6IjE3LjEwLjE4MDQuNDkiLCJUcnVzdGVkTGFiZWxIYXNoIjoiNTNRWVwvQmlDN0FwWGdZSjYwaFVpN1RUeDk4UXRtUGtpUXFQRjFQY2xLMnBIYUhmVTQxUGIyOEMzeWxcL0FkRGJEIn0= x-ctpclassification: CTP_NT dlp-product: dlpe-windows dlp-version: 11.0.600.7 dlp-reaction: no-action x-originating-ip: [163.33.239.182] Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Subject: Re: [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" Message-ID: <20190514125116.DOzqOryttDfnzl-g4hVwOAvz8BhpoizYAO0KoQiUeF8@z> >=20 > 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. >=20 > 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. ipsec-secgw changes itself looks good to me. One comment I have - would be good to incorporate your test script into run_test.sh, so user can run all tests in one go. Konstantin=20 >=20 > 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 >=20 > diff --git a/examples/ipsec-secgw/ipsec-secgw.c b/examples/ipsec-secgw/ip= sec-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 >=20 > #include "ipsec.h" > #include "parser.h" > @@ -248,16 +249,38 @@ prepare_one_packet(struct rte_mbuf *pkt, struct ips= ec_traffic *t) > pkt->l2_len =3D 0; > pkt->l3_len =3D sizeof(struct ip); > } else if (eth->ether_type =3D=3D rte_cpu_to_be_16(ETHER_TYPE_IPv6)) { > - nlp =3D (uint8_t *)rte_pktmbuf_adj(pkt, ETHER_HDR_LEN); > - nlp =3D RTE_PTR_ADD(nlp, offsetof(struct ip6_hdr, ip6_nxt)); > - if (*nlp =3D=3D IPPROTO_ESP) > + int next_proto; > + size_t l3len, ext_len; > + struct ipv6_hdr *v6h; > + uint8_t *p; > + > + /* get protocol type */ > + v6h =3D (struct ipv6_hdr *)rte_pktmbuf_adj(pkt, ETHER_HDR_LEN); > + next_proto =3D v6h->proto; > + > + /* determine l3 header size up to ESP extension */ > + l3len =3D sizeof(struct ip6_hdr); > + p =3D rte_pktmbuf_mtod(pkt, uint8_t *); > + while (next_proto !=3D IPPROTO_ESP && l3len < pkt->data_len && > + (next_proto =3D rte_ipv6_get_next_ext(p + l3len, > + next_proto, &ext_len)) >=3D 0) > + l3len +=3D 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 =3D=3D IPPROTO_ESP) > t->ipsec.pkts[(t->ipsec.num)++] =3D pkt; > else { > - t->ip6.data[t->ip6.num] =3D nlp; > + t->ip6.data[t->ip6.num] =3D rte_pktmbuf_mtod_offset(pkt, > + uint8_t *, offsetof(struct ipv6_hdr, proto)); > t->ip6.pkts[(t->ip6.num)++] =3D pkt; > } > pkt->l2_len =3D 0; > - pkt->l3_len =3D sizeof(struct ip6_hdr); > + pkt->l3_len =3D 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, struc= t rte_mbuf *pkt, > *sa_ret =3D NULL; >=20 > ip =3D rte_pktmbuf_mtod(pkt, struct ip *); > - if (ip->ip_v =3D=3D IPVERSION) > - esp =3D (struct esp_hdr *)(ip + 1); > - else > - esp =3D (struct esp_hdr *)(((struct ip6_hdr *)ip) + 1); > + esp =3D rte_pktmbuf_mtod_offset(pkt, struct esp_hdr *, pkt->l3_len); >=20 > if (esp->spi =3D=3D INVALID_SPI) > return; > diff --git a/examples/ipsec-secgw/test/test-scapy.py b/examples/ipsec-sec= gw/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=3D31 -l 0 --vdev=3Dcrypto_openssl --vde= v=3Dnet_tap0 --vdev=3Dnet_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 (loc= al) > + > +# 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 =3D "52:54:00:00:00:01" > +DST_ETHER =3D "52:54:00:00:00:02" > +SRC_ADDR =3D "1111::1" > +DST_ADDR =3D "2222::1" > +LOCAL_IFACE =3D "dtap1" > +REMOTE_IFACE =3D "dtap0" > + > + > +class Interface(object): > + ETH_P_ALL =3D 3 > + MAX_PACKET_SIZE =3D 1280 > + def __init__(self, ifname): > + self.name =3D ifname > + self.s =3D socket.socket(socket.AF_PACKET, socket.SOCK_RAW, sock= et.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 =3D pkt[ESP] > + > + # decrypt dummy packet with no extensions > + d =3D sa.decrypt(IPv6()/esp) > + > + # fix 'next header' in the preceding header of the original > + # packet and remove ESP > + pkt[ESP].underlayer.nh =3D d[IPv6].nh > + pkt[ESP].underlayer.remove_payload() > + > + # combine L3 header with decrypted payload > + npkt =3D pkt/d[IPv6].payload > + > + # fix length > + npkt[IPv6].plen =3D d[IPv6].plen + len(pkt[IPv6].payload) > + > + return npkt > + > + def setUp(self): > + self.ilocal =3D Interface(LOCAL_IFACE) > + self.iremote =3D Interface(REMOTE_IFACE) > + self.outb_sa =3D SecurityAssociation(ESP, spi=3D5, crypt_algo=3D= 'AES-CBC', crypt_key=3D'\x00'*16, auth_algo=3D'HMAC-SHA1-96', > auth_key=3D'\x00'*20) > + self.inb_sa =3D SecurityAssociation(ESP, spi=3D6, crypt_algo=3D'= AES-CBC', crypt_key=3D'\x00'*16, auth_algo=3D'HMAC-SHA1-96', > auth_key=3D'\x00'*20) > + > + def test_outb_ipv6_noopt(self): > + pkt =3D Ether(src=3DSRC_ETHER, dst=3DDST_ETHER) > + pkt /=3D IPv6(src=3DSRC_ADDR, dst=3DDST_ADDR) > + pkt /=3D UDP(sport=3D123,dport=3D456)/Raw(load=3D"abc") > + self.ilocal.send_packet(pkt) > + > + # check response > + resp =3D 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 =3D 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 =3D [] > + hoptions.append(RouterAlert(value=3D2)) > + hoptions.append(Jumbo(jumboplen=3D5000)) > + hoptions.append(Pad1()) > + > + doptions =3D [] > + doptions.append(HAO(hoa=3D"1234::4321")) > + > + pkt =3D Ether(src=3DSRC_ETHER, dst=3DDST_ETHER) > + pkt /=3D IPv6(src=3DSRC_ADDR, dst=3DDST_ADDR) > + pkt /=3D IPv6ExtHdrHopByHop(options=3Dhoptions) > + pkt /=3D IPv6ExtHdrRouting(addresses=3D["3333::3","4444::4"]) > + pkt /=3D IPv6ExtHdrDestOpt(options=3Ddoptions) > + pkt /=3D UDP(sport=3D123,dport=3D456)/Raw(load=3D"abc") > + self.ilocal.send_packet(pkt) > + > + # check response > + resp =3D self.iremote.recv_packet() > + self.assertEqual(resp[IPv6].nh, socket.IPPROTO_HOPOPTS) > + > + # check extensions > + self.assertEqual(resp[IPv6ExtHdrHopByHop].nh, socket.IPPROTO_ROU= TING) > + self.assertEqual(resp[IPv6ExtHdrRouting].nh, socket.IPPROTO_DSTO= PTS) > + self.assertEqual(resp[IPv6ExtHdrDestOpt].nh, socket.IPPROTO_ESP) > + > + # check ESP > + self.assertEqual(resp[ESP].spi, 5) > + > + # decrypt response, check packet after decryption > + d =3D TestTransportMode.decrypt(resp[IPv6], self.outb_sa) > + self.assertEqual(d[IPv6].nh, socket.IPPROTO_HOPOPTS) > + self.assertEqual(d[IPv6ExtHdrHopByHop].nh, socket.IPPROTO_ROUTIN= G) > + 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 =3D IPv6(src=3DDST_ADDR, dst=3DSRC_ADDR) > + pkt /=3D UDP(sport=3D123,dport=3D456)/Raw(load=3D"abc") > + e =3D self.inb_sa.encrypt(pkt) > + e =3D Ether(src=3DDST_ETHER, dst=3DSRC_ETHER)/e > + self.iremote.send_packet(e) > + > + # check response > + resp =3D 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 =3D [] > + hoptions.append(RouterAlert(value=3D2)) > + hoptions.append(Jumbo(jumboplen=3D5000)) > + hoptions.append(Pad1()) > + > + doptions =3D [] > + doptions.append(HAO(hoa=3D"1234::4321")) > + > + # prepare packet with options > + pkt =3D IPv6(src=3DDST_ADDR, dst=3DSRC_ADDR) > + pkt /=3D IPv6ExtHdrHopByHop(options=3Dhoptions) > + pkt /=3D IPv6ExtHdrRouting(addresses=3D["3333::3","4444::4"]) > + pkt /=3D IPv6ExtHdrDestOpt(options=3Ddoptions) > + pkt /=3D UDP(sport=3D123,dport=3D456)/Raw(load=3D"abc") > + > + # encrypt and send packet > + e =3D self.inb_sa.encrypt(pkt) > + e =3D Ether(src=3DDST_ETHER, dst=3DSRC_ETHER)/e > + self.iremote.send_packet(e) > + > + # check response > + resp =3D self.ilocal.recv_packet() > + self.assertEqual(resp[IPv6].nh, socket.IPPROTO_HOPOPTS) > + self.assertEqual(resp[IPv6ExtHdrHopByHop].nh, socket.IPPROTO_ROU= TING) > + self.assertEqual(resp[IPv6ExtHdrRouting].nh, socket.IPPROTO_DSTO= PTS) > + 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 =3D UDP(sport=3D123,dport=3D456)/Raw(load=3D"abc") > + e =3D self.inb_sa.encrypt(IPv6()/pkt) > + > + # craft and send inbound packet > + e =3D Ether(src=3DDST_ETHER, dst=3DSRC_ETHER)/IPv6(src=3DDST_ADD= R, dst=3DSRC_ADDR)/IPv6ExtHdrFragment()/e[IPv6].payload > + self.iremote.send_packet(e) > + > + # check response > + resp =3D 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__ =3D=3D "__main__": > + unittest.main() > -- > 2.21.0.windows.1