rte_net_get_ptype stops at the MPLS layer and never identifies the L3 protocol of the payload. Also, the label parsing uses a fixed maximum of 5 headers instead of checking the bottom of stack bit.
Use the bottom of stack bit to consume all labels and inspect the first nibble of the payload to determine if it is IPv4 or IPv6. Add test cases to verify this works. Ensure that an unknown protocol after MPLS (e.g. ARP) does not produce a bogus L3 type. Signed-off-by: Robin Jarry <[email protected]> --- app/test/test_net_ptype.c | 57 +++++++++++++++++++++++++++++++++++++++ lib/net/rte_net.c | 34 ++++++++++++++++------- 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/app/test/test_net_ptype.c b/app/test/test_net_ptype.c index bfe85da13543..cc7026077191 100644 --- a/app/test/test_net_ptype.c +++ b/app/test/test_net_ptype.c @@ -118,6 +118,51 @@ static const uint8_t pkt_vlan_vlan_ipv4_udp[] = { 0x89, 0x6f, 0x78, }; +/* Ether(type=MPLS)/MPLS(label=42,s=1)/IP()/UDP()/Raw('x') */ +static const uint8_t pkt_mpls_ipv4_udp[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x88, 0x47, 0x00, 0x02, + 0xa1, 0x40, 0x45, 0x00, 0x00, 0x1d, 0x00, 0x01, + 0x00, 0x00, 0x40, 0x11, 0x7c, 0xcd, 0x7f, 0x00, + 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x35, + 0x00, 0x35, 0x00, 0x09, 0x89, 0x6f, 0x78, +}; + +/* Ether(type=MPLS)/MPLS(label=42,s=0)/MPLS(label=43,s=1)/IPv6()/TCP() */ +static const uint8_t pkt_mpls2_ipv6_tcp[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x88, 0x47, 0x00, 0x02, + 0xa0, 0x40, 0x00, 0x02, 0xb1, 0x40, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x06, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x14, + 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x50, 0x02, 0x20, 0x00, 0x8f, 0x7d, + 0x00, 0x00, +}; + +/* Ether(type=MPLSM)/MPLS(label=42,s=1)/IP()/UDP()/Raw('x') */ +static const uint8_t pkt_mplsm_ipv4_udp[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x88, 0x48, 0x00, 0x02, + 0xa1, 0x40, 0x45, 0x00, 0x00, 0x1d, 0x00, 0x01, + 0x00, 0x00, 0x40, 0x11, 0x7c, 0xcd, 0x7f, 0x00, + 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x35, + 0x00, 0x35, 0x00, 0x09, 0x89, 0x6f, 0x78, +}; + +/* Ether(type=MPLS)/MPLS(label=42,s=1)/ARP() -- unknown L3 after MPLS */ +static const uint8_t pkt_mpls_arp[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x88, 0x47, 0x00, 0x02, + 0xa1, 0x40, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x02, +}; + /* Ether()/Dot1AD(vlan=42)/Dot1Q(vlan=43)/IPv6()/TCP() */ static const uint8_t pkt_qinq_ipv6_tcp[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, @@ -222,6 +267,18 @@ test_net_ptype(void) ret |= test_case(pool, pkt_qinq_ipv6_tcp, RTE_PTYPE_L2_ETHER_QINQ | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP, 22, 40, 20); + ret |= test_case(pool, pkt_mpls_ipv4_udp, + RTE_PTYPE_L2_ETHER_MPLS | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP, + 18, 20, 8); + ret |= test_case(pool, pkt_mpls2_ipv6_tcp, + RTE_PTYPE_L2_ETHER_MPLS | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP, + 22, 40, 20); + ret |= test_case(pool, pkt_mplsm_ipv4_udp, + RTE_PTYPE_L2_ETHER_MPLS | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP, + 18, 20, 8); + ret |= test_case(pool, pkt_mpls_arp, + RTE_PTYPE_L2_ETHER_MPLS, + 18, 0, 0); rte_mempool_free(pool); diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c index d3cded961fb5..3bb5fbc16d43 100644 --- a/lib/net/rte_net.c +++ b/lib/net/rte_net.c @@ -371,22 +371,36 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m, } else if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) || (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLSM))) { - unsigned int i; const struct rte_mpls_hdr *mh; struct rte_mpls_hdr mh_copy; + const uint8_t *nimble; + uint8_t nimble_copy; -#define MAX_MPLS_HDR 5 - for (i = 0; i < MAX_MPLS_HDR; i++) { - mh = rte_pktmbuf_read(m, off + (i * sizeof(*mh)), - sizeof(*mh), &mh_copy); + pkt_type = RTE_PTYPE_L2_ETHER_MPLS; + + /* consume all labels until bottom of stack is reached */ + do { + mh = rte_pktmbuf_read(m, off, sizeof(*mh), &mh_copy); if (unlikely(mh == NULL)) return pkt_type; - } - if (i == MAX_MPLS_HDR) + off += sizeof(*mh); + hdr_lens->l2_len += sizeof(*mh); + } while (!mh->bs); + + /* try to guess what is the payload based on the first 4 bits */ + nimble = rte_pktmbuf_read(m, off, sizeof(*nimble), &nimble_copy); + if (nimble == NULL) return pkt_type; - pkt_type = RTE_PTYPE_L2_ETHER_MPLS; - hdr_lens->l2_len += (sizeof(*mh) * i); - return pkt_type; + switch (*nimble & 0xf0) { + case 0x40: + proto = RTE_BE16(RTE_ETHER_TYPE_IPV4); + break; + case 0x60: + proto = RTE_BE16(RTE_ETHER_TYPE_IPV6); + break; + default: + return pkt_type; + } } l3: -- 2.54.0

