* [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function @ 2019-05-08 10:47 Marcin Smoczynski 2019-05-08 10:47 ` Marcin Smoczynski ` (5 more replies) 0 siblings, 6 replies; 29+ messages in thread From: Marcin Smoczynski @ 2019-05-08 10:47 UTC (permalink / raw) To: marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, akhil.goyal, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev, Marcin Smoczynski 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 <marcinx.smoczynski@intel.com> --- 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 -- 2.21.0.windows.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function 2019-05-08 10:47 [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function Marcin Smoczynski @ 2019-05-08 10:47 ` Marcin Smoczynski 2019-05-08 10:47 ` [dpdk-dev] [PATCH 2/3] ipsec: fix transport mode for ipv6 with extensions Marcin Smoczynski ` (4 subsequent siblings) 5 siblings, 0 replies; 29+ messages in thread From: Marcin Smoczynski @ 2019-05-08 10:47 UTC (permalink / raw) To: marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, akhil.goyal, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev, Marcin Smoczynski 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 <marcinx.smoczynski@intel.com> --- 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 -- 2.21.0.windows.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* [dpdk-dev] [PATCH 2/3] ipsec: fix transport mode for ipv6 with extensions 2019-05-08 10:47 [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function Marcin Smoczynski 2019-05-08 10:47 ` Marcin Smoczynski @ 2019-05-08 10:47 ` Marcin Smoczynski 2019-05-08 10:47 ` Marcin Smoczynski ` (2 more replies) 2019-05-08 10:47 ` [dpdk-dev] [PATCH 3/3] examples/ipsec-secgw: add support for ipv6 options Marcin Smoczynski ` (3 subsequent siblings) 5 siblings, 3 replies; 29+ messages in thread From: Marcin Smoczynski @ 2019-05-08 10:47 UTC (permalink / raw) To: marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, akhil.goyal, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev, Marcin Smoczynski 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 <marcinx.smoczynski@intel.com> --- 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 <rte_ip.h> + /** * @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; } -- 2.21.0.windows.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* [dpdk-dev] [PATCH 2/3] ipsec: fix transport mode for ipv6 with extensions 2019-05-08 10:47 ` [dpdk-dev] [PATCH 2/3] ipsec: fix transport mode for ipv6 with extensions Marcin Smoczynski @ 2019-05-08 10:47 ` Marcin Smoczynski 2019-05-14 12:42 ` Ananyev, Konstantin 2019-06-20 12:07 ` Akhil Goyal 2 siblings, 0 replies; 29+ messages in thread From: Marcin Smoczynski @ 2019-05-08 10:47 UTC (permalink / raw) To: marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, akhil.goyal, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev, Marcin Smoczynski 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 <marcinx.smoczynski@intel.com> --- 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 <rte_ip.h> + /** * @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; } -- 2.21.0.windows.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH 2/3] ipsec: fix transport mode for ipv6 with extensions 2019-05-08 10:47 ` [dpdk-dev] [PATCH 2/3] ipsec: fix transport mode for ipv6 with extensions Marcin Smoczynski 2019-05-08 10:47 ` Marcin Smoczynski @ 2019-05-14 12:42 ` Ananyev, Konstantin 2019-05-14 12:42 ` Ananyev, Konstantin 2019-06-20 12:07 ` Akhil Goyal 2 siblings, 1 reply; 29+ messages in thread From: Ananyev, Konstantin @ 2019-05-14 12:42 UTC (permalink / raw) To: Smoczynski, MarcinX, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, akhil.goyal, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: 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 <marcinx.smoczynski@intel.com> > --- Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com> ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH 2/3] ipsec: fix transport mode for ipv6 with extensions 2019-05-14 12:42 ` Ananyev, Konstantin @ 2019-05-14 12:42 ` Ananyev, Konstantin 0 siblings, 0 replies; 29+ messages in thread From: Ananyev, Konstantin @ 2019-05-14 12:42 UTC (permalink / raw) To: Smoczynski, MarcinX, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, akhil.goyal, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: 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 <marcinx.smoczynski@intel.com> > --- Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com> ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH 2/3] ipsec: fix transport mode for ipv6 with extensions 2019-05-08 10:47 ` [dpdk-dev] [PATCH 2/3] ipsec: fix transport mode for ipv6 with extensions Marcin Smoczynski 2019-05-08 10:47 ` Marcin Smoczynski 2019-05-14 12:42 ` Ananyev, Konstantin @ 2019-06-20 12:07 ` Akhil Goyal 2 siblings, 0 replies; 29+ messages in thread From: Akhil Goyal @ 2019-06-20 12:07 UTC (permalink / raw) To: Marcin Smoczynski, marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev Hi Marcin, > > 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 <marcinx.smoczynski@intel.com> > --- > 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 A nit. Please update the comment in lib/meson.build file for the dependencies. Currently it is only for crypto and security. > 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 <rte_ip.h> > + > /** > * @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; Is this next_proto_off a pointer to an offset or the value of the next_proto. So IMO the name should be next_proto or it should be p_nh > + > + /* 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; > } > -- > 2.21.0.windows.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* [dpdk-dev] [PATCH 3/3] examples/ipsec-secgw: add support for ipv6 options 2019-05-08 10:47 [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function Marcin Smoczynski 2019-05-08 10:47 ` Marcin Smoczynski 2019-05-08 10:47 ` [dpdk-dev] [PATCH 2/3] ipsec: fix transport mode for ipv6 with extensions Marcin Smoczynski @ 2019-05-08 10:47 ` Marcin Smoczynski 2019-05-08 10:47 ` Marcin Smoczynski 2019-05-14 12:51 ` Ananyev, Konstantin 2019-05-14 12:48 ` [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function Ananyev, Konstantin ` (2 subsequent siblings) 5 siblings, 2 replies; 29+ messages in thread From: Marcin Smoczynski @ 2019-05-08 10:47 UTC (permalink / raw) To: marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, akhil.goyal, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev, Marcin Smoczynski 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 <marcinx.smoczynski@intel.com> --- 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 <rte_jhash.h> #include <rte_cryptodev.h> #include <rte_security.h> +#include <rte_ip.h> #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() -- 2.21.0.windows.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* [dpdk-dev] [PATCH 3/3] examples/ipsec-secgw: add support for ipv6 options 2019-05-08 10:47 ` [dpdk-dev] [PATCH 3/3] examples/ipsec-secgw: add support for ipv6 options Marcin Smoczynski @ 2019-05-08 10:47 ` Marcin Smoczynski 2019-05-14 12:51 ` Ananyev, Konstantin 1 sibling, 0 replies; 29+ messages in thread From: Marcin Smoczynski @ 2019-05-08 10:47 UTC (permalink / raw) To: marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, akhil.goyal, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev, Marcin Smoczynski 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 <marcinx.smoczynski@intel.com> --- 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 <rte_jhash.h> #include <rte_cryptodev.h> #include <rte_security.h> +#include <rte_ip.h> #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() -- 2.21.0.windows.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH 3/3] examples/ipsec-secgw: add support for ipv6 options 2019-05-08 10:47 ` [dpdk-dev] [PATCH 3/3] examples/ipsec-secgw: add support for ipv6 options Marcin Smoczynski 2019-05-08 10:47 ` Marcin Smoczynski @ 2019-05-14 12:51 ` Ananyev, Konstantin 2019-05-14 12:51 ` Ananyev, Konstantin 1 sibling, 1 reply; 29+ messages in thread From: Ananyev, Konstantin @ 2019-05-14 12:51 UTC (permalink / raw) To: Smoczynski, MarcinX, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, akhil.goyal, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: 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. 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 > > Signed-off-by: Marcin Smoczynski <marcinx.smoczynski@intel.com> > --- > 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 <rte_jhash.h> > #include <rte_cryptodev.h> > #include <rte_security.h> > +#include <rte_ip.h> > > #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() > -- > 2.21.0.windows.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH 3/3] examples/ipsec-secgw: add support for ipv6 options 2019-05-14 12:51 ` Ananyev, Konstantin @ 2019-05-14 12:51 ` Ananyev, Konstantin 0 siblings, 0 replies; 29+ messages in thread From: Ananyev, Konstantin @ 2019-05-14 12:51 UTC (permalink / raw) To: Smoczynski, MarcinX, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, akhil.goyal, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: 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. 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 > > Signed-off-by: Marcin Smoczynski <marcinx.smoczynski@intel.com> > --- > 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 <rte_jhash.h> > #include <rte_cryptodev.h> > #include <rte_security.h> > +#include <rte_ip.h> > > #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() > -- > 2.21.0.windows.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function 2019-05-08 10:47 [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function Marcin Smoczynski ` (2 preceding siblings ...) 2019-05-08 10:47 ` [dpdk-dev] [PATCH 3/3] examples/ipsec-secgw: add support for ipv6 options Marcin Smoczynski @ 2019-05-14 12:48 ` Ananyev, Konstantin 2019-05-14 12:48 ` Ananyev, Konstantin 2019-06-20 11:40 ` Akhil Goyal 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 0/4] IPv6 with options support for IPsec transport Marcin Smoczynski 5 siblings, 1 reply; 29+ messages in thread From: Ananyev, Konstantin @ 2019-05-14 12:48 UTC (permalink / raw) To: Smoczynski, MarcinX, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, akhil.goyal, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: 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 <marcinx.smoczynski@intel.com> > --- Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com> > 2.21.0.windows.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function 2019-05-14 12:48 ` [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function Ananyev, Konstantin @ 2019-05-14 12:48 ` Ananyev, Konstantin 0 siblings, 0 replies; 29+ messages in thread From: Ananyev, Konstantin @ 2019-05-14 12:48 UTC (permalink / raw) To: Smoczynski, MarcinX, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, akhil.goyal, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: 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 <marcinx.smoczynski@intel.com> > --- Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com> > 2.21.0.windows.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function 2019-05-08 10:47 [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function Marcin Smoczynski ` (3 preceding siblings ...) 2019-05-14 12:48 ` [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function Ananyev, Konstantin @ 2019-06-20 11:40 ` Akhil Goyal 2019-06-20 17:40 ` Ananyev, Konstantin 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 0/4] IPv6 with options support for IPsec transport Marcin Smoczynski 5 siblings, 1 reply; 29+ messages in thread From: Akhil Goyal @ 2019-06-20 11:40 UTC (permalink / raw) To: Marcin Smoczynski, marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev Hi Marcin, > > 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 <marcinx.smoczynski@intel.com> > --- > 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_experimental may not be required for inline functions. > +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: I see that there is some compilation issues with respect to IPPROTO_xxx fields. These are reported in patchworks if you need details. Could you please fix these and send next rev. Thanks, Akhil ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function 2019-06-20 11:40 ` Akhil Goyal @ 2019-06-20 17:40 ` Ananyev, Konstantin 2019-06-21 8:01 ` Akhil Goyal 0 siblings, 1 reply; 29+ messages in thread From: Ananyev, Konstantin @ 2019-06-20 17:40 UTC (permalink / raw) To: Akhil Goyal, Smoczynski, MarcinX, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: dev Hi Akhil, > > Hi Marcin, > > > > > 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 <marcinx.smoczynski@intel.com> > > --- > > 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_experimental may not be required for inline functions. AFAIK we do need that tag for both inline and non-inline functions, till API will be transferred to 'stable' state: $ find lib -type f | xargs grep __rte_experimental | grep inline | wc -l 57 > > > > +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: > I see that there is some compilation issues with respect to IPPROTO_xxx fields. These are reported in patchworks if you need details. > > Could you please fix these and send next rev. > > Thanks, > Akhil ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function 2019-06-20 17:40 ` Ananyev, Konstantin @ 2019-06-21 8:01 ` Akhil Goyal 2019-06-24 11:45 ` Smoczynski, MarcinX 0 siblings, 1 reply; 29+ messages in thread From: Akhil Goyal @ 2019-06-21 8:01 UTC (permalink / raw) To: Ananyev, Konstantin, Smoczynski, MarcinX, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: dev Hi Konstantin, > > Hi Akhil, > > > > > Hi Marcin, > > > > > > > > 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 <marcinx.smoczynski@intel.com> > > > --- > > > 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_experimental may not be required for inline functions. > > AFAIK we do need that tag for both inline and non-inline functions, > till API will be transferred to 'stable' state: > > $ find lib -type f | xargs grep __rte_experimental | grep inline | wc -l > 57 My first impression was that this API was not exposed. Inline APIs are normally not exposed, So I thought rte_experimental is not required. I believe only exposed APIs Need to be tagged with rte_experimental. Now since this API is exposed to the application as well, it should have an entry in the .map file. Otherwise shared build will fail I think for application. > > > > > > > > +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: > > I see that there is some compilation issues with respect to IPPROTO_xxx fields. > These are reported in patchworks if you need details. > > > > Could you please fix these and send next rev. > > > > Thanks, > > Akhil ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function 2019-06-21 8:01 ` Akhil Goyal @ 2019-06-24 11:45 ` Smoczynski, MarcinX 2019-06-25 12:57 ` Akhil Goyal 0 siblings, 1 reply; 29+ messages in thread From: Smoczynski, MarcinX @ 2019-06-24 11:45 UTC (permalink / raw) To: Akhil Goyal, Ananyev, Konstantin, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: dev Hi Akhil, I've double checked and there is no problem with linking application against shared libraries, because inline functions are being inlined during compilation and before linking is done. To be more specific: there is not such symbol as rte_ipv6_get_next_ext in ipsec-secgw.o (which uses this function in prepare_one_packet function). There are a number of inline experimental functions defined today and they are mostly (43 out of 59) not listed in maps, e.g. rte_ticketlock functions family defined here: lib/librte_eal/common/include/generic/rte_ticketlock.h Is there any particular reason we should put inlines in maps? Maps are used by linker to control versions of shared libraries and inlines are not processed by linker. I am aware this patchset causes build failures on BSDs - this is a project-wide issue which I have addressed here: http://patchwork.dpdk.org/patch/53406/ I'll put a note in the next version of this patchset's cover that it depends on abovementioned patchset. Marcin > > Hi Konstantin, > > > > > Hi Akhil, > > > > > > > > Hi Marcin, > > > > > > > > > > > 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 <marcinx.smoczynski@intel.com> > > > > --- > > > > 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_experimental may not be required for inline functions. > > > > AFAIK we do need that tag for both inline and non-inline functions, > > till API will be transferred to 'stable' state: > > > > $ find lib -type f | xargs grep __rte_experimental | grep inline | wc > > -l > > 57 > > My first impression was that this API was not exposed. Inline APIs are > normally not exposed, So I thought rte_experimental is not required. I > believe only exposed APIs Need to be tagged with rte_experimental. > > Now since this API is exposed to the application as well, it should have an > entry in the .map file. > Otherwise shared build will fail I think for application. > > > > > > > > > > > > > +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: > > > I see that there is some compilation issues with respect to IPPROTO_xxx > fields. > > These are reported in patchworks if you need details. > > > > > > Could you please fix these and send next rev. > > > > > > Thanks, > > > Akhil ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function 2019-06-24 11:45 ` Smoczynski, MarcinX @ 2019-06-25 12:57 ` Akhil Goyal 0 siblings, 0 replies; 29+ messages in thread From: Akhil Goyal @ 2019-06-25 12:57 UTC (permalink / raw) To: Smoczynski, MarcinX, Ananyev, Konstantin, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: dev Hi Marcin, > > Hi Akhil, > I've double checked and there is no problem with linking application against > shared libraries, because inline functions are being inlined during compilation > and before linking is done. To be more specific: there is not such symbol as > rte_ipv6_get_next_ext in ipsec-secgw.o (which uses this function in > prepare_one_packet function). > > There are a number of inline experimental functions defined today and they are > mostly (43 out of 59) not listed in maps, e.g. rte_ticketlock functions family > defined here: > lib/librte_eal/common/include/generic/rte_ticketlock.h > > Is there any particular reason we should put inlines in maps? Maps are used by > linker to control versions of shared libraries and inlines are not processed by > linker. > > I am aware this patchset causes build failures on BSDs - this is a project-wide > issue which I have addressed here: > http://patchwork.dpdk.org/patch/53406/ > I'll put a note in the next version of this patchset's cover that it depends on > abovementioned patchset. > > Marcin Thanks for correcting me. ^ permalink raw reply [flat|nested] 29+ messages in thread
* [dpdk-dev] [PATCH v2 0/4] IPv6 with options support for IPsec transport 2019-05-08 10:47 [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function Marcin Smoczynski ` (4 preceding siblings ...) 2019-06-20 11:40 ` Akhil Goyal @ 2019-06-24 13:39 ` Marcin Smoczynski 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 1/4] net: new ipv6 header extension parsing function Marcin Smoczynski ` (4 more replies) 5 siblings, 5 replies; 29+ messages in thread From: Marcin Smoczynski @ 2019-06-24 13:39 UTC (permalink / raw) To: marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, akhil.goyal, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev, Marcin Smoczynski Add support for IPv6 with header extensions (IPv6 options) and IPsec transport mode: 1. Add IPv6 header extension parsing method in librte_net 2. Fix IPv6 header with extension parsing in both librte_ipsec and ipsec sample application using abovementioned function 3. Add new testing mechanism for ipsec-secgw sample application which allows to automatically check how custom packets are processed. This patchset depends on the following patch: http://patchwork.dpdk.org/patch/53406/ Marcin Smoczynski (4): net: new ipv6 header extension parsing function ipsec: fix transport mode for ipv6 with extensions examples/ipsec-secgw: add support for ipv6 options examples/ipsec-secgw: add scapy based unittests examples/ipsec-secgw/ipsec-secgw.c | 35 +++- examples/ipsec-secgw/sa.c | 5 +- examples/ipsec-secgw/test/common_defs.sh | 58 +----- .../ipsec-secgw/test/common_defs_secgw.sh | 65 +++++++ examples/ipsec-secgw/test/pkttest.py | 127 ++++++++++++ examples/ipsec-secgw/test/pkttest.sh | 65 +++++++ examples/ipsec-secgw/test/run_test.sh | 108 ++++++++--- examples/ipsec-secgw/test/trs_ipv6opts.py | 181 ++++++++++++++++++ lib/Makefile | 3 +- lib/librte_ipsec/iph.h | 55 +++++- lib/librte_net/rte_ip.h | 49 +++++ lib/meson.build | 2 +- 12 files changed, 649 insertions(+), 104 deletions(-) create mode 100644 examples/ipsec-secgw/test/common_defs_secgw.sh create mode 100755 examples/ipsec-secgw/test/pkttest.py create mode 100755 examples/ipsec-secgw/test/pkttest.sh mode change 100644 => 100755 examples/ipsec-secgw/test/run_test.sh create mode 100755 examples/ipsec-secgw/test/trs_ipv6opts.py -- 2.17.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* [dpdk-dev] [PATCH v2 1/4] net: new ipv6 header extension parsing function 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 0/4] IPv6 with options support for IPsec transport Marcin Smoczynski @ 2019-06-24 13:39 ` Marcin Smoczynski 2019-06-24 18:54 ` Ananyev, Konstantin 2019-07-02 9:06 ` Olivier Matz 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 2/4] ipsec: fix transport mode for ipv6 with extensions Marcin Smoczynski ` (3 subsequent siblings) 4 siblings, 2 replies; 29+ messages in thread From: Marcin Smoczynski @ 2019-06-24 13:39 UTC (permalink / raw) To: marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, akhil.goyal, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev, Marcin Smoczynski 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 <marcinx.smoczynski@intel.com> --- 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 ae3b7e730..c2c67b85d 100644 --- a/lib/librte_net/rte_ip.h +++ b/lib/librte_net/rte_ip.h @@ -428,6 +428,55 @@ rte_ipv6_udptcp_cksum(const struct rte_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 -- 2.17.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH v2 1/4] net: new ipv6 header extension parsing function 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 1/4] net: new ipv6 header extension parsing function Marcin Smoczynski @ 2019-06-24 18:54 ` Ananyev, Konstantin 2019-07-02 9:06 ` Olivier Matz 1 sibling, 0 replies; 29+ messages in thread From: Ananyev, Konstantin @ 2019-06-24 18:54 UTC (permalink / raw) To: Smoczynski, MarcinX, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, akhil.goyal, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: dev > -----Original Message----- > From: Smoczynski, MarcinX > Sent: Monday, June 24, 2019 2:40 PM > To: Kovacevic, Marko <marko.kovacevic@intel.com>; orika@mellanox.com; Richardson, Bruce <bruce.richardson@intel.com>; De Lara > Guarch, Pablo <pablo.de.lara.guarch@intel.com>; Nicolau, Radu <radu.nicolau@intel.com>; akhil.goyal@nxp.com; Kantecki, Tomasz > <tomasz.kantecki@intel.com>; Ananyev, Konstantin <konstantin.ananyev@intel.com>; Iremonger, Bernard > <bernard.iremonger@intel.com>; olivier.matz@6wind.com > Cc: dev@dpdk.org; Smoczynski, MarcinX <marcinx.smoczynski@intel.com> > Subject: [PATCH v2 1/4] net: new ipv6 header extension parsing function > > 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 <marcinx.smoczynski@intel.com> > --- > 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 ae3b7e730..c2c67b85d 100644 > --- a/lib/librte_net/rte_ip.h > +++ b/lib/librte_net/rte_ip.h > @@ -428,6 +428,55 @@ rte_ipv6_udptcp_cksum(const struct rte_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 > -- Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com> Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com> > 2.17.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH v2 1/4] net: new ipv6 header extension parsing function 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 1/4] net: new ipv6 header extension parsing function Marcin Smoczynski 2019-06-24 18:54 ` Ananyev, Konstantin @ 2019-07-02 9:06 ` Olivier Matz 1 sibling, 0 replies; 29+ messages in thread From: Olivier Matz @ 2019-07-02 9:06 UTC (permalink / raw) To: Marcin Smoczynski Cc: marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, akhil.goyal, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, dev Hi, On Mon, Jun 24, 2019 at 03:39:57PM +0200, Marcin Smoczynski wrote: > 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 <marcinx.smoczynski@intel.com> > --- > 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 ae3b7e730..c2c67b85d 100644 > --- a/lib/librte_net/rte_ip.h > +++ b/lib/librte_net/rte_ip.h > @@ -428,6 +428,55 @@ rte_ipv6_udptcp_cksum(const struct rte_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 I just want to highlight the potential danger of this kind of function: then length is read from the packet (controlled by the peer), and returned without check. My initial fear was that an attacker forge an IPv6 packet, resulting in *ext_len being 0. For instance, calling the function with (proto = IPPROTO_ROUTING, buffer = [ 0, 255, ... ]). Fortunatly, this does not happen, due to the implicit promotion of *p to a larger integer in the (*p + 1) expression. That said, what about adding some comments in the description, saying that: - the pointer p must point to a buffer whose length is at least 2 - the returned length must be checked by the caller to ensure it does not overflow the packet buffer You can add my ack in the next version. Thanks, Olivier ^ permalink raw reply [flat|nested] 29+ messages in thread
* [dpdk-dev] [PATCH v2 2/4] ipsec: fix transport mode for ipv6 with extensions 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 0/4] IPv6 with options support for IPsec transport Marcin Smoczynski 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 1/4] net: new ipv6 header extension parsing function Marcin Smoczynski @ 2019-06-24 13:39 ` Marcin Smoczynski 2019-06-24 18:55 ` Ananyev, Konstantin 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 3/4] examples/ipsec-secgw: add support for ipv6 options Marcin Smoczynski ` (2 subsequent siblings) 4 siblings, 1 reply; 29+ messages in thread From: Marcin Smoczynski @ 2019-06-24 13:39 UTC (permalink / raw) To: marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, akhil.goyal, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev, Marcin Smoczynski 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 <marcinx.smoczynski@intel.com> --- lib/Makefile | 3 ++- lib/librte_ipsec/iph.h | 55 ++++++++++++++++++++++++++++++++++++------ lib/meson.build | 2 +- 3 files changed, 50 insertions(+), 10 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 a0ca41d51..62d78b7b1 100644 --- a/lib/librte_ipsec/iph.h +++ b/lib/librte_ipsec/iph.h @@ -5,6 +5,8 @@ #ifndef _IPH_H_ #define _IPH_H_ +#include <rte_ip.h> + /** * @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 rte_ipv4_hdr *v4h; - struct rte_ipv6_hdr *v6h; int32_t rc; + /* IPv4 */ if ((sa->type & RTE_IPSEC_SATP_IPV_MASK) == RTE_IPSEC_SATP_IPV4) { + struct rte_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 rte_ipv6_hdr *v6h; + uint8_t *p_nh; + v6h = p; - rc = v6h->proto; - v6h->proto = proto; + + /* basic IPv6 header with no extensions */ + if (l3len == sizeof(struct rte_ipv6_hdr)) + p_nh = &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 rte_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 */ + p_nh = pd; + } + + /* update header type; return original value */ + rc = *p_nh; + *p_nh = 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; } diff --git a/lib/meson.build b/lib/meson.build index 992091a94..e93797ede 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -23,7 +23,7 @@ libraries = [ 'kni', 'latencystats', 'lpm', 'member', 'power', 'pdump', 'rawdev', 'rcu', 'reorder', 'sched', 'security', 'stack', 'vhost', - #ipsec lib depends on crypto and security + # ipsec lib depends on net, crypto and security 'ipsec', # add pkt framework libs which use other libs from above 'port', 'table', 'pipeline', -- 2.17.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH v2 2/4] ipsec: fix transport mode for ipv6 with extensions 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 2/4] ipsec: fix transport mode for ipv6 with extensions Marcin Smoczynski @ 2019-06-24 18:55 ` Ananyev, Konstantin 0 siblings, 0 replies; 29+ messages in thread From: Ananyev, Konstantin @ 2019-06-24 18:55 UTC (permalink / raw) To: Smoczynski, MarcinX, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, akhil.goyal, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: 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 <marcinx.smoczynski@intel.com> > --- Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com> Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com> > 2.17.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* [dpdk-dev] [PATCH v2 3/4] examples/ipsec-secgw: add support for ipv6 options 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 0/4] IPv6 with options support for IPsec transport Marcin Smoczynski 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 1/4] net: new ipv6 header extension parsing function Marcin Smoczynski 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 2/4] ipsec: fix transport mode for ipv6 with extensions Marcin Smoczynski @ 2019-06-24 13:39 ` Marcin Smoczynski 2019-06-24 18:55 ` Ananyev, Konstantin 2019-06-24 13:40 ` [dpdk-dev] [PATCH v2 4/4] examples/ipsec-secgw: add scapy based unittests Marcin Smoczynski 2019-06-25 12:59 ` [dpdk-dev] [PATCH v2 0/4] IPv6 with options support for IPsec transport Akhil Goyal 4 siblings, 1 reply; 29+ messages in thread From: Marcin Smoczynski @ 2019-06-24 13:39 UTC (permalink / raw) To: marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, akhil.goyal, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev, Marcin Smoczynski 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. Signed-off-by: Marcin Smoczynski <marcinx.smoczynski@intel.com> --- examples/ipsec-secgw/ipsec-secgw.c | 35 +++++++++++++++++++++++++----- examples/ipsec-secgw/sa.c | 5 +---- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/examples/ipsec-secgw/ipsec-secgw.c b/examples/ipsec-secgw/ipsec-secgw.c index 6c626fa5f..17012caf9 100644 --- a/examples/ipsec-secgw/ipsec-secgw.c +++ b/examples/ipsec-secgw/ipsec-secgw.c @@ -41,6 +41,7 @@ #include <rte_jhash.h> #include <rte_cryptodev.h> #include <rte_security.h> +#include <rte_ip.h> #include "ipsec.h" #include "parser.h" @@ -248,16 +249,40 @@ 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(RTE_ETHER_TYPE_IPV6)) { - nlp = (uint8_t *)rte_pktmbuf_adj(pkt, RTE_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 rte_ipv6_hdr *v6h; + uint8_t *p; + + /* get protocol type */ + v6h = (struct rte_ipv6_hdr *)rte_pktmbuf_adj(pkt, + RTE_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 rte_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 8d47d1def..7262ccee8 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 rte_esp_hdr *)(ip + 1); - else - esp = (struct rte_esp_hdr *)(((struct ip6_hdr *)ip) + 1); + esp = rte_pktmbuf_mtod_offset(pkt, struct rte_esp_hdr *, pkt->l3_len); if (esp->spi == INVALID_SPI) return; -- 2.17.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH v2 3/4] examples/ipsec-secgw: add support for ipv6 options 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 3/4] examples/ipsec-secgw: add support for ipv6 options Marcin Smoczynski @ 2019-06-24 18:55 ` Ananyev, Konstantin 0 siblings, 0 replies; 29+ messages in thread From: Ananyev, Konstantin @ 2019-06-24 18:55 UTC (permalink / raw) To: Smoczynski, MarcinX, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, akhil.goyal, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: 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. > > Signed-off-by: Marcin Smoczynski <marcinx.smoczynski@intel.com> > --- Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com> Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com> > 2.17.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* [dpdk-dev] [PATCH v2 4/4] examples/ipsec-secgw: add scapy based unittests 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 0/4] IPv6 with options support for IPsec transport Marcin Smoczynski ` (2 preceding siblings ...) 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 3/4] examples/ipsec-secgw: add support for ipv6 options Marcin Smoczynski @ 2019-06-24 13:40 ` Marcin Smoczynski 2019-06-24 18:56 ` Ananyev, Konstantin 2019-06-25 12:59 ` [dpdk-dev] [PATCH v2 0/4] IPv6 with options support for IPsec transport Akhil Goyal 4 siblings, 1 reply; 29+ messages in thread From: Marcin Smoczynski @ 2019-06-24 13:40 UTC (permalink / raw) To: marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, akhil.goyal, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev, Marcin Smoczynski Add new unittest-like mechanism which uses scapy to craft custom packets and a set of assertions to check how ipsec-secgw example application is processing them. Python3 with scapy module is required by pkttest.sh to run test scripts. A new mechanism is used to test IPv6 transport mode traffic with header extensions (trs_ipv6opts.py). Fix incomplete test log problem by disabling buffering of ipsec-secgw standard output with stdbuf application. Signed-off-by: Marcin Smoczynski <marcinx.smoczynski@intel.com> --- examples/ipsec-secgw/test/common_defs.sh | 58 +----- .../ipsec-secgw/test/common_defs_secgw.sh | 65 +++++++ examples/ipsec-secgw/test/pkttest.py | 127 ++++++++++++ examples/ipsec-secgw/test/pkttest.sh | 65 +++++++ examples/ipsec-secgw/test/run_test.sh | 108 ++++++++--- examples/ipsec-secgw/test/trs_ipv6opts.py | 181 ++++++++++++++++++ 6 files changed, 519 insertions(+), 85 deletions(-) create mode 100644 examples/ipsec-secgw/test/common_defs_secgw.sh create mode 100755 examples/ipsec-secgw/test/pkttest.py create mode 100755 examples/ipsec-secgw/test/pkttest.sh mode change 100644 => 100755 examples/ipsec-secgw/test/run_test.sh create mode 100755 examples/ipsec-secgw/test/trs_ipv6opts.py diff --git a/examples/ipsec-secgw/test/common_defs.sh b/examples/ipsec-secgw/test/common_defs.sh index 8dc574b50..63ad5415d 100644 --- a/examples/ipsec-secgw/test/common_defs.sh +++ b/examples/ipsec-secgw/test/common_defs.sh @@ -1,22 +1,10 @@ #! /bin/bash -#check that env vars are properly defined - -#check SGW_PATH -if [[ -z "${SGW_PATH}" || ! -x ${SGW_PATH} ]]; then - echo "SGW_PATH is invalid" - exit 127 -fi - #check ETH_DEV if [[ -z "${ETH_DEV}" ]]; then echo "ETH_DEV is invalid" exit 127 fi - -#setup SGW_LCORE -SGW_LCORE=${SGW_LCORE:-0} - #check that REMOTE_HOST is reachable ssh ${REMOTE_HOST} echo st=$? @@ -47,14 +35,6 @@ LOCAL_IPV6=fd12:3456:789a:0031:0000:0000:0000:0092 DPDK_PATH=${RTE_SDK:-${PWD}} DPDK_BUILD=${RTE_TARGET:-x86_64-native-linux-gcc} -SGW_OUT_FILE=./ipsec-secgw.out1 - -SGW_CMD_EAL_PRM="--lcores=${SGW_LCORE} -n 4 ${ETH_DEV}" -SGW_CMD_CFG="(0,0,${SGW_LCORE}),(1,0,${SGW_LCORE})" -SGW_CMD_PRM="-p 0x3 -u 1 -P --config=\"${SGW_CMD_CFG}\"" - -SGW_CFG_FILE=$(mktemp) - # configure local host/ifaces config_local_iface() { @@ -126,37 +106,7 @@ config6_iface() config6_remote_iface } -#start ipsec-secgw -secgw_start() -{ - SGW_EXEC_FILE=$(mktemp) - cat <<EOF > ${SGW_EXEC_FILE} -${SGW_PATH} ${SGW_CMD_EAL_PRM} ${CRYPTO_DEV} \ ---vdev="net_tap0,mac=fixed" \ --- ${SGW_CMD_PRM} ${SGW_CMD_XPRM} -f ${SGW_CFG_FILE} > \ -${SGW_OUT_FILE} 2>&1 & -p=\$! -echo \$p -EOF - - cat ${SGW_EXEC_FILE} - SGW_PID=`/bin/bash -x ${SGW_EXEC_FILE}` - - # wait till ipsec-secgw start properly - i=0 - st=1 - while [[ $i -ne 10 && st -ne 0 ]]; do - sleep 1 - ifconfig ${LOCAL_IFACE} - st=$? - let i++ - done -} - -#stop ipsec-secgw and cleanup -secgw_stop() -{ - kill ${SGW_PID} - rm -f ${SGW_EXEC_FILE} - rm -f ${SGW_CFG_FILE} -} +# secgw application parameters setup +SGW_PORT_CFG="--vdev=\"net_tap0,mac=fixed\" ${ETH_DEV}" +SGW_WAIT_DEV="${LOCAL_IFACE}" +. ${DIR}/common_defs_secgw.sh diff --git a/examples/ipsec-secgw/test/common_defs_secgw.sh b/examples/ipsec-secgw/test/common_defs_secgw.sh new file mode 100644 index 000000000..a50c03cb3 --- /dev/null +++ b/examples/ipsec-secgw/test/common_defs_secgw.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +# check required parameters +SGW_REQ_VARS="SGW_PATH SGW_PORT_CFG SGW_WAIT_DEV" +for reqvar in ${SGW_REQ_VARS} +do + if [[ -z "${!reqvar}" ]]; then + echo "Required parameter ${reqvar} is empty" + exit 127 + fi +done + +# check if SGW_PATH point to an executable +if [[ ! -x ${SGW_PATH} ]]; then + echo "${SGW_PATH} is not executable" + exit 127 +fi + +# setup SGW_LCORE +SGW_LCORE=${SGW_LCORE:-0} + +# setup config and output filenames +SGW_OUT_FILE=./ipsec-secgw.out1 +SGW_CFG_FILE=$(mktemp) + +# setup secgw parameters +SGW_CMD_EAL_PRM="--lcores=${SGW_LCORE} -n 4" +SGW_CMD_CFG="(0,0,${SGW_LCORE}),(1,0,${SGW_LCORE})" +SGW_CMD_PRM="-p 0x3 -u 1 -P --config=\"${SGW_CMD_CFG}\"" + +# start ipsec-secgw +secgw_start() +{ + SGW_EXEC_FILE=$(mktemp) + cat <<EOF > ${SGW_EXEC_FILE} +stdbuf -o0 ${SGW_PATH} ${SGW_CMD_EAL_PRM} ${CRYPTO_DEV} \ +${SGW_PORT_CFG} ${SGW_EAL_XPRM} \ +-- ${SGW_CMD_PRM} ${SGW_CMD_XPRM} -f ${SGW_CFG_FILE} > \ +${SGW_OUT_FILE} 2>&1 & +p=\$! +echo \$p +EOF + + cat ${SGW_EXEC_FILE} + cat ${SGW_CFG_FILE} + SGW_PID=`/bin/bash -x ${SGW_EXEC_FILE}` + + # wait till ipsec-secgw start properly + i=0 + st=1 + while [[ $i -ne 10 && $st -ne 0 ]]; do + sleep 1 + ifconfig ${SGW_WAIT_DEV} + st=$? + let i++ + done +} + +# stop ipsec-secgw and cleanup +secgw_stop() +{ + kill ${SGW_PID} + rm -f ${SGW_EXEC_FILE} + rm -f ${SGW_CFG_FILE} +} diff --git a/examples/ipsec-secgw/test/pkttest.py b/examples/ipsec-secgw/test/pkttest.py new file mode 100755 index 000000000..bcad2156b --- /dev/null +++ b/examples/ipsec-secgw/test/pkttest.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 + +import fcntl +import pkg_resources +import socket +import struct +import sys +import unittest + + +if sys.version_info < (3, 0): + print("Python3 is required to run this script") + sys.exit(1) + + +try: + from scapy.all import Ether +except ImportError: + print("Scapy module is required") + sys.exit(1) + + +PKTTEST_REQ = [ + "scapy==2.4.3rc1", +] + + +def assert_requirements(req): + """ + assert requirement is met + req can hold a string or a list of strings + """ + try: + pkg_resources.require(req) + except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict) as e: + print("Requirement assertion: " + str(e)) + sys.exit(1) + + +TAP_UNPROTECTED = "dtap1" +TAP_PROTECTED = "dtap0" + + +class Interface(object): + ETH_P_ALL = 3 + MAX_PACKET_SIZE = 1280 + IOCTL_GET_INFO = 0x8927 + SOCKET_TIMEOUT = 0.5 + def __init__(self, ifname): + self.name = ifname + + # create and bind socket to specified interface + self.s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(Interface.ETH_P_ALL)) + self.s.settimeout(Interface.SOCKET_TIMEOUT) + self.s.bind((self.name, 0, socket.PACKET_OTHERHOST)) + + # get interface MAC address + info = fcntl.ioctl(self.s.fileno(), Interface.IOCTL_GET_INFO, struct.pack('256s', bytes(ifname[:15], encoding='ascii'))) + self.mac = ':'.join(['%02x' % i for i in info[18:24]]) + + def __del__(self): + self.s.close() + + def send_l3packet(self, pkt, mac): + e = Ether(src=self.mac, dst=mac) + self.send_packet(e/pkt) + + 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) + + def get_mac(self): + return self.mac + + +class PacketXfer(object): + def __init__(self, protected_iface=TAP_PROTECTED, unprotected_iface=TAP_UNPROTECTED): + self.protected_port = Interface(protected_iface) + self.unprotected_port = Interface(unprotected_iface) + + def send_to_protected_port(self, pkt, remote_mac=None): + if remote_mac is None: + remote_mac = self.unprotected_port.get_mac() + self.protected_port.send_l3packet(pkt, remote_mac) + + def send_to_unprotected_port(self, pkt, remote_mac=None): + if remote_mac is None: + remote_mac = self.protected_port.get_mac() + self.unprotected_port.send_l3packet(pkt, remote_mac) + + def xfer_unprotected(self, pkt): + self.send_to_unprotected_port(pkt) + return self.protected_port.recv_packet() + + def xfer_protected(self, pkt): + self.send_to_protected_port(pkt) + return self.unprotected_port.recv_packet() + + +def pkttest(): + if len(sys.argv) == 1: + sys.exit(unittest.main(verbosity=2)) + elif len(sys.argv) == 2: + if sys.argv[1] == "config": + module = __import__('__main__') + try: + print(module.config()) + except AttributeError: + sys.stderr.write("Cannot find \"config()\" in a test") + sys.exit(1) + else: + sys.exit(1) + + +if __name__ == "__main__": + if len(sys.argv) == 2 and sys.argv[1] == "check_reqs": + assert_requirements(PKTTEST_REQ) + else: + print("Usage: " + sys.argv[0] + " check_reqs") diff --git a/examples/ipsec-secgw/test/pkttest.sh b/examples/ipsec-secgw/test/pkttest.sh new file mode 100755 index 000000000..04cd96b2e --- /dev/null +++ b/examples/ipsec-secgw/test/pkttest.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +DIR=$(dirname $0) + +if [ $(id -u) -ne 0 ]; then + echo "Run as root" + exit 1 +fi + +# check python requirements +python3 ${DIR}/pkttest.py check_reqs +if [ $? -ne 0 ]; then + echo "Requirements for Python not met, exiting" + exit 1 +fi + +# secgw application parameters setup +CRYPTO_DEV="--vdev=crypto_null0" +SGW_PORT_CFG="--vdev=net_tap0,mac=fixed --vdev=net_tap1,mac=fixed" +SGW_EAL_XPRM="--no-pci" +SGW_CMD_XPRM=-l +SGW_WAIT_DEV="dtap0" +. ${DIR}/common_defs_secgw.sh + +echo "Running tests: $*" +for testcase in $* +do + # check test file presence + testfile="${DIR}/${testcase}.py" + if [ ! -f ${testfile} ]; then + echo "Invalid test ${testcase}" + continue + fi + + # prepare test config + python3 ${testfile} config > ${SGW_CFG_FILE} + if [ $? -ne 0 ]; then + rm -f ${SGW_CFG_FILE} + echo "Cannot get secgw configuration for test ${testcase}" + exit 1 + fi + + # start the application + secgw_start + + # setup interfaces + ifconfig dtap0 up + ifconfig dtap1 up + + # run the test + echo "Running test case: ${testcase}" + python3 ${testfile} + st=$? + + # stop the application + secgw_stop + + # report test result and exit on failure + if [ $st -eq 0 ]; then + echo "Test case ${testcase} succeeded" + else + echo "Test case ${testcase} failed!" + exit $st + fi +done diff --git a/examples/ipsec-secgw/test/run_test.sh b/examples/ipsec-secgw/test/run_test.sh old mode 100644 new mode 100755 index 3a1a7d4b4..4969effdb --- a/examples/ipsec-secgw/test/run_test.sh +++ b/examples/ipsec-secgw/test/run_test.sh @@ -17,6 +17,17 @@ # naming convention: # 'old' means that ipsec-secgw will run in legacy (non-librte_ipsec mode) # 'tun/trs' refer to tunnel/transport mode respectively + +usage() +{ + echo "Usage:" + echo -e "\t$0 -[46p]" + echo -e "\t\t-4 Perform Linux IPv4 network tests" + echo -e "\t\t-6 Perform Linux IPv6 network tests" + echo -e "\t\t-p Perform packet validation tests" + echo -e "\t\t-h Display this help" +} + LINUX_TEST="tun_aescbc_sha1 \ tun_aescbc_sha1_esn \ tun_aescbc_sha1_esn_atom \ @@ -50,47 +61,82 @@ trs_3descbc_sha1_old \ trs_3descbc_sha1_esn \ trs_3descbc_sha1_esn_atom" -DIR=`dirname $0` +PKT_TESTS="trs_ipv6opts" + +DIR=$(dirname $0) # get input options -st=0 run4=0 run6=0 -while [[ ${st} -eq 0 ]]; do - getopts ":46" opt - st=$? - if [[ "${opt}" == "4" ]]; then - run4=1 - elif [[ "${opt}" == "6" ]]; then - run6=1 - fi +runpkt=0 +while getopts ":46ph" opt +do + case $opt in + 4) + run4=1 + ;; + 6) + run6=1 + ;; + p) + runpkt=1 + ;; + h) + usage + exit 0 + ;; + ?) + echo "Invalid option" + usage + exit 127 + ;; + esac done -if [[ ${run4} -eq 0 && ${run6} -eq 0 ]]; then +# no test suite has been selected +if [[ ${run4} -eq 0 && ${run6} -eq 0 && ${runpkt} -eq 0 ]]; then + usage exit 127 fi -for i in ${LINUX_TEST}; do - - echo "starting test ${i}" +# perform packet processing validation tests +st=0 +if [ $runpkt -eq 1 ]; then + echo "Performing packet validation tests" + /bin/bash ${DIR}/pkttest.sh ${PKT_TESTS} + st=$? - st4=0 - if [[ ${run4} -ne 0 ]]; then - /bin/bash ${DIR}/linux_test4.sh ${i} - st4=$? - echo "test4 ${i} finished with status ${st4}" + echo "pkttests finished with status ${st}" + if [[ ${st} -ne 0 ]]; then + echo "ERROR pkttests FAILED" + exit ${st} fi +fi - st6=0 - if [[ ${run6} -ne 0 ]]; then - /bin/bash ${DIR}/linux_test6.sh ${i} - st6=$? - echo "test6 ${i} finished with status ${st6}" - fi +# perform network tests +if [[ ${run4} -eq 1 || ${run6} -eq 1 ]]; then + for i in ${LINUX_TEST}; do - let "st = st4 + st6" - if [[ $st -ne 0 ]]; then - echo "ERROR test ${i} FAILED" - exit $st - fi -done + echo "starting test ${i}" + + st4=0 + if [[ ${run4} -ne 0 ]]; then + /bin/bash ${DIR}/linux_test4.sh ${i} + st4=$? + echo "test4 ${i} finished with status ${st4}" + fi + + st6=0 + if [[ ${run6} -ne 0 ]]; then + /bin/bash ${DIR}/linux_test6.sh ${i} + st6=$? + echo "test6 ${i} finished with status ${st6}" + fi + + let "st = st4 + st6" + if [[ $st -ne 0 ]]; then + echo "ERROR test ${i} FAILED" + exit $st + fi + done +fi diff --git a/examples/ipsec-secgw/test/trs_ipv6opts.py b/examples/ipsec-secgw/test/trs_ipv6opts.py new file mode 100755 index 000000000..167c89617 --- /dev/null +++ b/examples/ipsec-secgw/test/trs_ipv6opts.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 + +from scapy.all import * +import unittest +import pkttest + + +SRC_ADDR = "1111:0000:0000:0000:0000:0000:0000:0001" +DST_ADDR = "2222:0000:0000:0000:0000:0000:0000:0001" +SRC_NET = "1111:0000:0000:0000:0000:0000:0000:0000/64" +DST_NET = "2222:0000:0000:0000:0000:0000:0000:0000/64" + + +def config(): + return """ +sp ipv6 out esp protect 5 pri 1 \\ +src {0} \\ +dst {1} \\ +sport 0:65535 dport 0:65535 + +sp ipv6 in esp protect 6 pri 1 \\ +src {1} \\ +dst {0} \\ +sport 0:65535 dport 0:65535 + +sa out 5 cipher_algo null auth_algo null mode transport +sa in 6 cipher_algo null auth_algo null mode transport + +rt ipv6 dst {0} port 1 +rt ipv6 dst {1} port 0 +""".format(SRC_NET, DST_NET) + + +class TestTransportWithIPv6Ext(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.px = pkttest.PacketXfer() + self.outb_sa = SecurityAssociation(ESP, spi=5) + self.inb_sa = SecurityAssociation(ESP, spi=6) + + def test_outb_ipv6_noopt(self): + pkt = IPv6(src=SRC_ADDR, dst=DST_ADDR) + pkt /= UDP(sport=123,dport=456)/Raw(load="abc") + + # send and check response + resp = self.px.xfer_unprotected(pkt) + self.assertEqual(resp[IPv6].nh, socket.IPPROTO_ESP) + self.assertEqual(resp[ESP].spi, 5) + + # decrypt response, check packet after decryption + d = TestTransportWithIPv6Ext.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 = 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") + + # send and check response + resp = self.px.xfer_unprotected(pkt) + 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 = TestTransportWithIPv6Ext.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) + + # send and check response + resp = self.px.xfer_protected(e) + 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") + e = self.inb_sa.encrypt(pkt) + + # self encrypted packet and check response + resp = self.px.xfer_protected(e) + 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 = IPv6()/UDP(sport=123,dport=456)/Raw(load="abc") + e = self.inb_sa.encrypt(pkt) + + # craft and send inbound packet + e = IPv6(src=DST_ADDR, dst=SRC_ADDR)/IPv6ExtHdrFragment()/e[IPv6].payload + resp = self.px.xfer_protected(e) + + # check response + 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') + + +pkttest.pkttest() -- 2.17.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH v2 4/4] examples/ipsec-secgw: add scapy based unittests 2019-06-24 13:40 ` [dpdk-dev] [PATCH v2 4/4] examples/ipsec-secgw: add scapy based unittests Marcin Smoczynski @ 2019-06-24 18:56 ` Ananyev, Konstantin 0 siblings, 0 replies; 29+ messages in thread From: Ananyev, Konstantin @ 2019-06-24 18:56 UTC (permalink / raw) To: Smoczynski, MarcinX, Kovacevic, Marko, orika, Richardson, Bruce, De Lara Guarch, Pablo, Nicolau, Radu, akhil.goyal, Kantecki, Tomasz, Iremonger, Bernard, olivier.matz Cc: dev > Add new unittest-like mechanism which uses scapy to craft custom > packets and a set of assertions to check how ipsec-secgw example > application is processing them. Python3 with scapy module is > required by pkttest.sh to run test scripts. > > A new mechanism is used to test IPv6 transport mode traffic with > header extensions (trs_ipv6opts.py). > > Fix incomplete test log problem by disabling buffering of ipsec-secgw > standard output with stdbuf application. > > Signed-off-by: Marcin Smoczynski <marcinx.smoczynski@intel.com> > --- Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com> Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com> > 2.17.1 ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [dpdk-dev] [PATCH v2 0/4] IPv6 with options support for IPsec transport 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 0/4] IPv6 with options support for IPsec transport Marcin Smoczynski ` (3 preceding siblings ...) 2019-06-24 13:40 ` [dpdk-dev] [PATCH v2 4/4] examples/ipsec-secgw: add scapy based unittests Marcin Smoczynski @ 2019-06-25 12:59 ` Akhil Goyal 4 siblings, 0 replies; 29+ messages in thread From: Akhil Goyal @ 2019-06-25 12:59 UTC (permalink / raw) To: Marcin Smoczynski, marko.kovacevic, orika, bruce.richardson, pablo.de.lara.guarch, radu.nicolau, tomasz.kantecki, konstantin.ananyev, bernard.iremonger, olivier.matz Cc: dev > > Add support for IPv6 with header extensions (IPv6 options) and IPsec > transport mode: > 1. Add IPv6 header extension parsing method in librte_net > 2. Fix IPv6 header with extension parsing in both librte_ipsec and > ipsec sample application using abovementioned function > 3. Add new testing mechanism for ipsec-secgw sample application which > allows to automatically check how custom packets are processed. > > This patchset depends on the following patch: > http://patchwork.dpdk.org/patch/53406/ > > Marcin Smoczynski (4): > net: new ipv6 header extension parsing function > ipsec: fix transport mode for ipv6 with extensions > examples/ipsec-secgw: add support for ipv6 options > examples/ipsec-secgw: add scapy based unittests > > examples/ipsec-secgw/ipsec-secgw.c | 35 +++- > examples/ipsec-secgw/sa.c | 5 +- > examples/ipsec-secgw/test/common_defs.sh | 58 +----- > .../ipsec-secgw/test/common_defs_secgw.sh | 65 +++++++ > examples/ipsec-secgw/test/pkttest.py | 127 ++++++++++++ > examples/ipsec-secgw/test/pkttest.sh | 65 +++++++ > examples/ipsec-secgw/test/run_test.sh | 108 ++++++++--- > examples/ipsec-secgw/test/trs_ipv6opts.py | 181 ++++++++++++++++++ > lib/Makefile | 3 +- > lib/librte_ipsec/iph.h | 55 +++++- > lib/librte_net/rte_ip.h | 49 +++++ > lib/meson.build | 2 +- > 12 files changed, 649 insertions(+), 104 deletions(-) > create mode 100644 examples/ipsec-secgw/test/common_defs_secgw.sh > create mode 100755 examples/ipsec-secgw/test/pkttest.py > create mode 100755 examples/ipsec-secgw/test/pkttest.sh > mode change 100644 => 100755 examples/ipsec-secgw/test/run_test.sh > create mode 100755 examples/ipsec-secgw/test/trs_ipv6opts.py > > -- > 2.17.1 Series Acked-by: Akhil Goyal <akhil.goyal@nxp.com> The dependent patch will go in mainline and is a generic patch to fix the compilation. So not holding this series. Series Applied to dpdk-next-crypto Thanks. ^ permalink raw reply [flat|nested] 29+ messages in thread
end of thread, other threads:[~2019-07-02 9:06 UTC | newest] Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2019-05-08 10:47 [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function Marcin Smoczynski 2019-05-08 10:47 ` Marcin Smoczynski 2019-05-08 10:47 ` [dpdk-dev] [PATCH 2/3] ipsec: fix transport mode for ipv6 with extensions Marcin Smoczynski 2019-05-08 10:47 ` Marcin Smoczynski 2019-05-14 12:42 ` Ananyev, Konstantin 2019-05-14 12:42 ` Ananyev, Konstantin 2019-06-20 12:07 ` Akhil Goyal 2019-05-08 10:47 ` [dpdk-dev] [PATCH 3/3] examples/ipsec-secgw: add support for ipv6 options Marcin Smoczynski 2019-05-08 10:47 ` Marcin Smoczynski 2019-05-14 12:51 ` Ananyev, Konstantin 2019-05-14 12:51 ` Ananyev, Konstantin 2019-05-14 12:48 ` [dpdk-dev] [PATCH 1/3] net: new ipv6 header extension parsing function Ananyev, Konstantin 2019-05-14 12:48 ` Ananyev, Konstantin 2019-06-20 11:40 ` Akhil Goyal 2019-06-20 17:40 ` Ananyev, Konstantin 2019-06-21 8:01 ` Akhil Goyal 2019-06-24 11:45 ` Smoczynski, MarcinX 2019-06-25 12:57 ` Akhil Goyal 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 0/4] IPv6 with options support for IPsec transport Marcin Smoczynski 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 1/4] net: new ipv6 header extension parsing function Marcin Smoczynski 2019-06-24 18:54 ` Ananyev, Konstantin 2019-07-02 9:06 ` Olivier Matz 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 2/4] ipsec: fix transport mode for ipv6 with extensions Marcin Smoczynski 2019-06-24 18:55 ` Ananyev, Konstantin 2019-06-24 13:39 ` [dpdk-dev] [PATCH v2 3/4] examples/ipsec-secgw: add support for ipv6 options Marcin Smoczynski 2019-06-24 18:55 ` Ananyev, Konstantin 2019-06-24 13:40 ` [dpdk-dev] [PATCH v2 4/4] examples/ipsec-secgw: add scapy based unittests Marcin Smoczynski 2019-06-24 18:56 ` Ananyev, Konstantin 2019-06-25 12:59 ` [dpdk-dev] [PATCH v2 0/4] IPv6 with options support for IPsec transport Akhil Goyal
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).