/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016 6WIND S.A. */ #include #include #include #include #include #include #include #include #include #include #include /* get l3 packet type from ip6 next protocol */ static uint32_t ptype_l3_ip6(uint8_t ip6_proto) { static const uint32_t ip6_ext_proto_map[256] = { [IPPROTO_HOPOPTS] = RTE_PTYPE_L3_IPV6_EXT - RTE_PTYPE_L3_IPV6, [IPPROTO_ROUTING] = RTE_PTYPE_L3_IPV6_EXT - RTE_PTYPE_L3_IPV6, [IPPROTO_FRAGMENT] = RTE_PTYPE_L3_IPV6_EXT - RTE_PTYPE_L3_IPV6, [IPPROTO_ESP] = RTE_PTYPE_L3_IPV6_EXT - RTE_PTYPE_L3_IPV6, [IPPROTO_AH] = RTE_PTYPE_L3_IPV6_EXT - RTE_PTYPE_L3_IPV6, [IPPROTO_DSTOPTS] = RTE_PTYPE_L3_IPV6_EXT - RTE_PTYPE_L3_IPV6, }; return RTE_PTYPE_L3_IPV6 + ip6_ext_proto_map[ip6_proto]; } /* get l3 packet type from ip version and header length */ static uint32_t ptype_l3_ip(uint8_t ipv_ihl) { static const uint32_t ptype_l3_ip_proto_map[256] = { [0x45] = RTE_PTYPE_L3_IPV4, [0x46] = RTE_PTYPE_L3_IPV4_EXT, [0x47] = RTE_PTYPE_L3_IPV4_EXT, [0x48] = RTE_PTYPE_L3_IPV4_EXT, [0x49] = RTE_PTYPE_L3_IPV4_EXT, [0x4A] = RTE_PTYPE_L3_IPV4_EXT, [0x4B] = RTE_PTYPE_L3_IPV4_EXT, [0x4C] = RTE_PTYPE_L3_IPV4_EXT, [0x4D] = RTE_PTYPE_L3_IPV4_EXT, [0x4E] = RTE_PTYPE_L3_IPV4_EXT, [0x4F] = RTE_PTYPE_L3_IPV4_EXT, }; return ptype_l3_ip_proto_map[ipv_ihl]; } /* get l4 packet type from proto */ static uint32_t ptype_l4(uint8_t proto) { static const uint32_t ptype_l4_proto[256] = { [IPPROTO_UDP] = RTE_PTYPE_L4_UDP, [IPPROTO_TCP] = RTE_PTYPE_L4_TCP, [IPPROTO_SCTP] = RTE_PTYPE_L4_SCTP, }; return ptype_l4_proto[proto]; } /* get inner l3 packet type from ip6 next protocol */ static uint32_t ptype_inner_l3_ip6(uint8_t ip6_proto) { static const uint32_t ptype_inner_ip6_ext_proto_map[256] = { [IPPROTO_HOPOPTS] = RTE_PTYPE_INNER_L3_IPV6_EXT - RTE_PTYPE_INNER_L3_IPV6, [IPPROTO_ROUTING] = RTE_PTYPE_INNER_L3_IPV6_EXT - RTE_PTYPE_INNER_L3_IPV6, [IPPROTO_FRAGMENT] = RTE_PTYPE_INNER_L3_IPV6_EXT - RTE_PTYPE_INNER_L3_IPV6, [IPPROTO_ESP] = RTE_PTYPE_INNER_L3_IPV6_EXT - RTE_PTYPE_INNER_L3_IPV6, [IPPROTO_AH] = RTE_PTYPE_INNER_L3_IPV6_EXT - RTE_PTYPE_INNER_L3_IPV6, [IPPROTO_DSTOPTS] = RTE_PTYPE_INNER_L3_IPV6_EXT - RTE_PTYPE_INNER_L3_IPV6, }; return RTE_PTYPE_INNER_L3_IPV6 + ptype_inner_ip6_ext_proto_map[ip6_proto]; } /* get inner l3 packet type from ip version and header length */ static uint32_t ptype_inner_l3_ip(uint8_t ipv_ihl) { static const uint32_t ptype_inner_l3_ip_proto_map[256] = { [0x45] = RTE_PTYPE_INNER_L3_IPV4, [0x46] = RTE_PTYPE_INNER_L3_IPV4_EXT, [0x47] = RTE_PTYPE_INNER_L3_IPV4_EXT, [0x48] = RTE_PTYPE_INNER_L3_IPV4_EXT, [0x49] = RTE_PTYPE_INNER_L3_IPV4_EXT, [0x4A] = RTE_PTYPE_INNER_L3_IPV4_EXT, [0x4B] = RTE_PTYPE_INNER_L3_IPV4_EXT, [0x4C] = RTE_PTYPE_INNER_L3_IPV4_EXT, [0x4D] = RTE_PTYPE_INNER_L3_IPV4_EXT, [0x4E] = RTE_PTYPE_INNER_L3_IPV4_EXT, [0x4F] = RTE_PTYPE_INNER_L3_IPV4_EXT, }; return ptype_inner_l3_ip_proto_map[ipv_ihl]; } /* get inner l4 packet type from proto */ static uint32_t ptype_inner_l4(uint8_t proto) { static const uint32_t ptype_inner_l4_proto[256] = { [IPPROTO_UDP] = RTE_PTYPE_INNER_L4_UDP, [IPPROTO_TCP] = RTE_PTYPE_INNER_L4_TCP, [IPPROTO_SCTP] = RTE_PTYPE_INNER_L4_SCTP, }; return ptype_inner_l4_proto[proto]; } /* get the tunnel packet type if any, update proto and off. */ static uint32_t ptype_tunnel(uint16_t *proto, const struct rte_mbuf *m, uint32_t *off) { switch (*proto) { case IPPROTO_GRE: { static const uint8_t opt_len[16] = { [0x0] = 4, [0x1] = 8, [0x2] = 8, [0x8] = 8, [0x3] = 12, [0x9] = 12, [0xa] = 12, [0xb] = 16, }; const struct gre_hdr *gh; struct gre_hdr gh_copy; uint16_t flags; gh = rte_pktmbuf_read(m, *off, sizeof(*gh), &gh_copy); if (unlikely(gh == NULL)) return 0; flags = rte_be_to_cpu_16(*(const uint16_t *)gh); flags >>= 12; if (opt_len[flags] == 0) return 0; *off += opt_len[flags]; *proto = gh->proto; if (*proto == rte_cpu_to_be_16(ETHER_TYPE_TEB)) return RTE_PTYPE_TUNNEL_NVGRE; else return RTE_PTYPE_TUNNEL_GRE; } case IPPROTO_IPIP: *proto = rte_cpu_to_be_16(ETHER_TYPE_IPv4); return RTE_PTYPE_TUNNEL_IP; case IPPROTO_IPV6: *proto = rte_cpu_to_be_16(ETHER_TYPE_IPv6); return RTE_PTYPE_TUNNEL_IP; /* IP is also valid for IPv6 */ default: return 0; } } /* get the ipv4 header length */ static uint8_t ip4_hlen(const struct ipv4_hdr *hdr) { return (hdr->version_ihl & 0xf) * 4; } /* parse ipv6 extended headers, update offset and return next proto */ static uint16_t skip_ip6_ext(uint16_t proto, const struct rte_mbuf *m, uint32_t *off, int *frag) { struct ext_hdr { uint8_t next_hdr; uint8_t len; }; const struct ext_hdr *xh; struct ext_hdr xh_copy; unsigned int i; *frag = 0; #define MAX_EXT_HDRS 5 for (i = 0; i < MAX_EXT_HDRS; i++) { switch (proto) { case IPPROTO_HOPOPTS: case IPPROTO_ROUTING: case IPPROTO_DSTOPTS: xh = rte_pktmbuf_read(m, *off, sizeof(*xh), &xh_copy); if (xh == NULL) return 0; *off += (xh->len + 1) * 8; proto = xh->next_hdr; break; case IPPROTO_FRAGMENT: xh = rte_pktmbuf_read(m, *off, sizeof(*xh), &xh_copy); if (xh == NULL) return 0; *off += 8; proto = xh->next_hdr; *frag = 1; return proto; /* this is always the last ext hdr */ case IPPROTO_NONE: return 0; default: return proto; } } return 0; } /* parse mbuf data to get packet type */ uint32_t rte_net_get_ptype(const struct rte_mbuf *m, struct rte_net_hdr_lens *hdr_lens, uint32_t layers) { struct rte_net_hdr_lens local_hdr_lens; const struct ether_hdr *eh; struct ether_hdr eh_copy; uint32_t pkt_type = RTE_PTYPE_L2_ETHER; uint32_t off = 0; uint16_t proto; if (hdr_lens == NULL) hdr_lens = &local_hdr_lens; eh = rte_pktmbuf_read(m, off, sizeof(*eh), &eh_copy); if (unlikely(eh == NULL)) return 0; proto = eh->ether_type; off = sizeof(*eh); hdr_lens->l2_len = off; if ((layers & RTE_PTYPE_L2_MASK) == 0) return 0; if (proto == rte_cpu_to_be_16(ETHER_TYPE_IPv4)) goto l3; /* fast path if packet is IPv4 */ if (proto == rte_cpu_to_be_16(ETHER_TYPE_VLAN)) { const struct vlan_hdr *vh; struct vlan_hdr vh_copy; pkt_type = RTE_PTYPE_L2_ETHER_VLAN; vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy); if (unlikely(vh == NULL)) return pkt_type; off += sizeof(*vh); hdr_lens->l2_len += sizeof(*vh); proto = vh->eth_proto; } else if (proto == rte_cpu_to_be_16(ETHER_TYPE_QINQ)) { const struct vlan_hdr *vh; struct vlan_hdr vh_copy; pkt_type = RTE_PTYPE_L2_ETHER_QINQ; vh = rte_pktmbuf_read(m, off + sizeof(*vh), sizeof(*vh), &vh_copy); if (unlikely(vh == NULL)) return pkt_type; off += 2 * sizeof(*vh); hdr_lens->l2_len += 2 * sizeof(*vh); proto = vh->eth_proto; } l3: if ((layers & RTE_PTYPE_L3_MASK) == 0) return pkt_type; if (proto == rte_cpu_to_be_16(ETHER_TYPE_IPv4)) { const struct ipv4_hdr *ip4h; struct ipv4_hdr ip4h_copy; ip4h = rte_pktmbuf_read(m, off, sizeof(*ip4h), &ip4h_copy); if (unlikely(ip4h == NULL)) return pkt_type; pkt_type |= ptype_l3_ip(ip4h->version_ihl); hdr_lens->l3_len = ip4_hlen(ip4h); off += hdr_lens->l3_len; if ((layers & RTE_PTYPE_L4_MASK) == 0) return pkt_type; if (ip4h->fragment_offset & rte_cpu_to_be_16( IPV4_HDR_OFFSET_MASK | IPV4_HDR_MF_FLAG)) { pkt_type |= RTE_PTYPE_L4_FRAG; hdr_lens->l4_len = 0; return pkt_type; } proto = ip4h->next_proto_id; pkt_type |= ptype_l4(proto); } else if (proto == rte_cpu_to_be_16(ETHER_TYPE_IPv6)) { const struct ipv6_hdr *ip6h; struct ipv6_hdr ip6h_copy; int frag = 0; ip6h = rte_pktmbuf_read(m, off, sizeof(*ip6h), &ip6h_copy); if (unlikely(ip6h == NULL)) return pkt_type; proto = ip6h->proto; hdr_lens->l3_len = sizeof(*ip6h); off += hdr_lens->l3_len; pkt_type |= ptype_l3_ip6(proto); if ((pkt_type & RTE_PTYPE_L3_MASK) == RTE_PTYPE_L3_IPV6_EXT) { proto = skip_ip6_ext(proto, m, &off, &frag); hdr_lens->l3_len = off - hdr_lens->l2_len; } if (proto == 0) return pkt_type; if ((layers & RTE_PTYPE_L4_MASK) == 0) return pkt_type; if (frag) { pkt_type |= RTE_PTYPE_L4_FRAG; hdr_lens->l4_len = 0; return pkt_type; } pkt_type |= ptype_l4(proto); } if ((pkt_type & RTE_PTYPE_L4_MASK) == RTE_PTYPE_L4_UDP) { hdr_lens->l4_len = sizeof(struct udp_hdr); return pkt_type; } else if ((pkt_type & RTE_PTYPE_L4_MASK) == RTE_PTYPE_L4_TCP) { const struct tcp_hdr *th; struct tcp_hdr th_copy; th = rte_pktmbuf_read(m, off, sizeof(*th), &th_copy); if (unlikely(th == NULL)) return pkt_type & (RTE_PTYPE_L2_MASK | RTE_PTYPE_L3_MASK); hdr_lens->l4_len = (th->data_off & 0xf0) >> 2; return pkt_type; } else if ((pkt_type & RTE_PTYPE_L4_MASK) == RTE_PTYPE_L4_SCTP) { hdr_lens->l4_len = sizeof(struct sctp_hdr); return pkt_type; } else { uint32_t prev_off = off; hdr_lens->l4_len = 0; if ((layers & RTE_PTYPE_TUNNEL_MASK) == 0) return pkt_type; pkt_type |= ptype_tunnel(&proto, m, &off); hdr_lens->tunnel_len = off - prev_off; } /* same job for inner header: we need to duplicate the code * because the packet types do not have the same value. */ if ((layers & RTE_PTYPE_INNER_L2_MASK) == 0) return pkt_type; hdr_lens->inner_l2_len = 0; if (proto == rte_cpu_to_be_16(ETHER_TYPE_TEB)) { eh = rte_pktmbuf_read(m, off, sizeof(*eh), &eh_copy); if (unlikely(eh == NULL)) return pkt_type; pkt_type |= RTE_PTYPE_INNER_L2_ETHER; proto = eh->ether_type; off += sizeof(*eh); hdr_lens->inner_l2_len = sizeof(*eh); } if (proto == rte_cpu_to_be_16(ETHER_TYPE_VLAN)) { const struct vlan_hdr *vh; struct vlan_hdr vh_copy; pkt_type &= ~RTE_PTYPE_INNER_L2_MASK; pkt_type |= RTE_PTYPE_INNER_L2_ETHER_VLAN; vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy); if (unlikely(vh == NULL)) return pkt_type; off += sizeof(*vh); hdr_lens->inner_l2_len += sizeof(*vh); proto = vh->eth_proto; } else if (proto == rte_cpu_to_be_16(ETHER_TYPE_QINQ)) { const struct vlan_hdr *vh; struct vlan_hdr vh_copy; pkt_type &= ~RTE_PTYPE_INNER_L2_MASK; pkt_type |= RTE_PTYPE_INNER_L2_ETHER_QINQ; vh = rte_pktmbuf_read(m, off + sizeof(*vh), sizeof(*vh), &vh_copy); if (unlikely(vh == NULL)) return pkt_type; off += 2 * sizeof(*vh); hdr_lens->inner_l2_len += 2 * sizeof(*vh); proto = vh->eth_proto; } if ((layers & RTE_PTYPE_INNER_L3_MASK) == 0) return pkt_type; if (proto == rte_cpu_to_be_16(ETHER_TYPE_IPv4)) { const struct ipv4_hdr *ip4h; struct ipv4_hdr ip4h_copy; ip4h = rte_pktmbuf_read(m, off, sizeof(*ip4h), &ip4h_copy); if (unlikely(ip4h == NULL)) return pkt_type; pkt_type |= ptype_inner_l3_ip(ip4h->version_ihl); hdr_lens->inner_l3_len = ip4_hlen(ip4h); off += hdr_lens->inner_l3_len; if ((layers & RTE_PTYPE_INNER_L4_MASK) == 0) return pkt_type; if (ip4h->fragment_offset & rte_cpu_to_be_16(IPV4_HDR_OFFSET_MASK | IPV4_HDR_MF_FLAG)) { pkt_type |= RTE_PTYPE_INNER_L4_FRAG; hdr_lens->inner_l4_len = 0; return pkt_type; } proto = ip4h->next_proto_id; pkt_type |= ptype_inner_l4(proto); } else if (proto == rte_cpu_to_be_16(ETHER_TYPE_IPv6)) { const struct ipv6_hdr *ip6h; struct ipv6_hdr ip6h_copy; int frag = 0; ip6h = rte_pktmbuf_read(m, off, sizeof(*ip6h), &ip6h_copy); if (unlikely(ip6h == NULL)) return pkt_type; proto = ip6h->proto; hdr_lens->inner_l3_len = sizeof(*ip6h); off += hdr_lens->inner_l3_len; pkt_type |= ptype_inner_l3_ip6(proto); if ((pkt_type & RTE_PTYPE_INNER_L3_MASK) == RTE_PTYPE_INNER_L3_IPV6_EXT) { uint32_t prev_off; prev_off = off; proto = skip_ip6_ext(proto, m, &off, &frag); hdr_lens->inner_l3_len += off - prev_off; } if (proto == 0) return pkt_type; if ((layers & RTE_PTYPE_INNER_L4_MASK) == 0) return pkt_type; if (frag) { pkt_type |= RTE_PTYPE_INNER_L4_FRAG; hdr_lens->inner_l4_len = 0; return pkt_type; } pkt_type |= ptype_inner_l4(proto); } if ((pkt_type & RTE_PTYPE_INNER_L4_MASK) == RTE_PTYPE_INNER_L4_UDP) { hdr_lens->inner_l4_len = sizeof(struct udp_hdr); } else if ((pkt_type & RTE_PTYPE_INNER_L4_MASK) == RTE_PTYPE_INNER_L4_TCP) { const struct tcp_hdr *th; struct tcp_hdr th_copy; th = rte_pktmbuf_read(m, off, sizeof(*th), &th_copy); if (unlikely(th == NULL)) return pkt_type & (RTE_PTYPE_INNER_L2_MASK | RTE_PTYPE_INNER_L3_MASK); hdr_lens->inner_l4_len = (th->data_off & 0xf0) >> 2; } else if ((pkt_type & RTE_PTYPE_INNER_L4_MASK) == RTE_PTYPE_INNER_L4_SCTP) { hdr_lens->inner_l4_len = sizeof(struct sctp_hdr); } else { hdr_lens->inner_l4_len = 0; } return pkt_type; }