199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson * Copyright(c) 2020 Inspur Corporation
399a2dd95SBruce Richardson */
499a2dd95SBruce Richardson
599a2dd95SBruce Richardson #include <rte_malloc.h>
699a2dd95SBruce Richardson #include <rte_mbuf.h>
799a2dd95SBruce Richardson #include <rte_ethdev.h>
899a2dd95SBruce Richardson #include <rte_udp.h>
999a2dd95SBruce Richardson
1099a2dd95SBruce Richardson #include "gro_vxlan_udp4.h"
1199a2dd95SBruce Richardson
1299a2dd95SBruce Richardson void *
gro_vxlan_udp4_tbl_create(uint16_t socket_id,uint16_t max_flow_num,uint16_t max_item_per_flow)1399a2dd95SBruce Richardson gro_vxlan_udp4_tbl_create(uint16_t socket_id,
1499a2dd95SBruce Richardson uint16_t max_flow_num,
1599a2dd95SBruce Richardson uint16_t max_item_per_flow)
1699a2dd95SBruce Richardson {
1799a2dd95SBruce Richardson struct gro_vxlan_udp4_tbl *tbl;
1899a2dd95SBruce Richardson size_t size;
1999a2dd95SBruce Richardson uint32_t entries_num, i;
2099a2dd95SBruce Richardson
2199a2dd95SBruce Richardson entries_num = max_flow_num * max_item_per_flow;
2299a2dd95SBruce Richardson entries_num = RTE_MIN(entries_num, GRO_VXLAN_UDP4_TBL_MAX_ITEM_NUM);
2399a2dd95SBruce Richardson
2499a2dd95SBruce Richardson if (entries_num == 0)
2599a2dd95SBruce Richardson return NULL;
2699a2dd95SBruce Richardson
2799a2dd95SBruce Richardson tbl = rte_zmalloc_socket(__func__,
2899a2dd95SBruce Richardson sizeof(struct gro_vxlan_udp4_tbl),
2999a2dd95SBruce Richardson RTE_CACHE_LINE_SIZE,
3099a2dd95SBruce Richardson socket_id);
3199a2dd95SBruce Richardson if (tbl == NULL)
3299a2dd95SBruce Richardson return NULL;
3399a2dd95SBruce Richardson
3499a2dd95SBruce Richardson size = sizeof(struct gro_vxlan_udp4_item) * entries_num;
3599a2dd95SBruce Richardson tbl->items = rte_zmalloc_socket(__func__,
3699a2dd95SBruce Richardson size,
3799a2dd95SBruce Richardson RTE_CACHE_LINE_SIZE,
3899a2dd95SBruce Richardson socket_id);
3999a2dd95SBruce Richardson if (tbl->items == NULL) {
4099a2dd95SBruce Richardson rte_free(tbl);
4199a2dd95SBruce Richardson return NULL;
4299a2dd95SBruce Richardson }
4399a2dd95SBruce Richardson tbl->max_item_num = entries_num;
4499a2dd95SBruce Richardson
4599a2dd95SBruce Richardson size = sizeof(struct gro_vxlan_udp4_flow) * entries_num;
4699a2dd95SBruce Richardson tbl->flows = rte_zmalloc_socket(__func__,
4799a2dd95SBruce Richardson size,
4899a2dd95SBruce Richardson RTE_CACHE_LINE_SIZE,
4999a2dd95SBruce Richardson socket_id);
5099a2dd95SBruce Richardson if (tbl->flows == NULL) {
5199a2dd95SBruce Richardson rte_free(tbl->items);
5299a2dd95SBruce Richardson rte_free(tbl);
5399a2dd95SBruce Richardson return NULL;
5499a2dd95SBruce Richardson }
5599a2dd95SBruce Richardson
5699a2dd95SBruce Richardson for (i = 0; i < entries_num; i++)
5799a2dd95SBruce Richardson tbl->flows[i].start_index = INVALID_ARRAY_INDEX;
5899a2dd95SBruce Richardson tbl->max_flow_num = entries_num;
5999a2dd95SBruce Richardson
6099a2dd95SBruce Richardson return tbl;
6199a2dd95SBruce Richardson }
6299a2dd95SBruce Richardson
6399a2dd95SBruce Richardson void
gro_vxlan_udp4_tbl_destroy(void * tbl)6499a2dd95SBruce Richardson gro_vxlan_udp4_tbl_destroy(void *tbl)
6599a2dd95SBruce Richardson {
6699a2dd95SBruce Richardson struct gro_vxlan_udp4_tbl *vxlan_tbl = tbl;
6799a2dd95SBruce Richardson
6899a2dd95SBruce Richardson if (vxlan_tbl) {
6999a2dd95SBruce Richardson rte_free(vxlan_tbl->items);
7099a2dd95SBruce Richardson rte_free(vxlan_tbl->flows);
7199a2dd95SBruce Richardson }
7299a2dd95SBruce Richardson rte_free(vxlan_tbl);
7399a2dd95SBruce Richardson }
7499a2dd95SBruce Richardson
7599a2dd95SBruce Richardson static inline uint32_t
find_an_empty_item(struct gro_vxlan_udp4_tbl * tbl)7699a2dd95SBruce Richardson find_an_empty_item(struct gro_vxlan_udp4_tbl *tbl)
7799a2dd95SBruce Richardson {
7899a2dd95SBruce Richardson uint32_t max_item_num = tbl->max_item_num, i;
7999a2dd95SBruce Richardson
8099a2dd95SBruce Richardson for (i = 0; i < max_item_num; i++)
8199a2dd95SBruce Richardson if (tbl->items[i].inner_item.firstseg == NULL)
8299a2dd95SBruce Richardson return i;
8399a2dd95SBruce Richardson return INVALID_ARRAY_INDEX;
8499a2dd95SBruce Richardson }
8599a2dd95SBruce Richardson
8699a2dd95SBruce Richardson static inline uint32_t
find_an_empty_flow(struct gro_vxlan_udp4_tbl * tbl)8799a2dd95SBruce Richardson find_an_empty_flow(struct gro_vxlan_udp4_tbl *tbl)
8899a2dd95SBruce Richardson {
8999a2dd95SBruce Richardson uint32_t max_flow_num = tbl->max_flow_num, i;
9099a2dd95SBruce Richardson
9199a2dd95SBruce Richardson for (i = 0; i < max_flow_num; i++)
9299a2dd95SBruce Richardson if (tbl->flows[i].start_index == INVALID_ARRAY_INDEX)
9399a2dd95SBruce Richardson return i;
9499a2dd95SBruce Richardson return INVALID_ARRAY_INDEX;
9599a2dd95SBruce Richardson }
9699a2dd95SBruce Richardson
9799a2dd95SBruce Richardson static inline uint32_t
insert_new_item(struct gro_vxlan_udp4_tbl * tbl,struct rte_mbuf * pkt,uint64_t start_time,uint32_t prev_idx,uint16_t frag_offset,uint8_t is_last_frag)9899a2dd95SBruce Richardson insert_new_item(struct gro_vxlan_udp4_tbl *tbl,
9999a2dd95SBruce Richardson struct rte_mbuf *pkt,
10099a2dd95SBruce Richardson uint64_t start_time,
10199a2dd95SBruce Richardson uint32_t prev_idx,
10299a2dd95SBruce Richardson uint16_t frag_offset,
10399a2dd95SBruce Richardson uint8_t is_last_frag)
10499a2dd95SBruce Richardson {
10599a2dd95SBruce Richardson uint32_t item_idx;
10699a2dd95SBruce Richardson
10799a2dd95SBruce Richardson item_idx = find_an_empty_item(tbl);
10899a2dd95SBruce Richardson if (unlikely(item_idx == INVALID_ARRAY_INDEX))
10999a2dd95SBruce Richardson return INVALID_ARRAY_INDEX;
11099a2dd95SBruce Richardson
11199a2dd95SBruce Richardson tbl->items[item_idx].inner_item.firstseg = pkt;
11299a2dd95SBruce Richardson tbl->items[item_idx].inner_item.lastseg = rte_pktmbuf_lastseg(pkt);
11399a2dd95SBruce Richardson tbl->items[item_idx].inner_item.start_time = start_time;
11499a2dd95SBruce Richardson tbl->items[item_idx].inner_item.next_pkt_idx = INVALID_ARRAY_INDEX;
11599a2dd95SBruce Richardson tbl->items[item_idx].inner_item.frag_offset = frag_offset;
11699a2dd95SBruce Richardson tbl->items[item_idx].inner_item.is_last_frag = is_last_frag;
11799a2dd95SBruce Richardson tbl->items[item_idx].inner_item.nb_merged = 1;
11899a2dd95SBruce Richardson tbl->item_num++;
11999a2dd95SBruce Richardson
12099a2dd95SBruce Richardson /* If the previous packet exists, chain the new one with it. */
12199a2dd95SBruce Richardson if (prev_idx != INVALID_ARRAY_INDEX) {
12299a2dd95SBruce Richardson tbl->items[item_idx].inner_item.next_pkt_idx =
12399a2dd95SBruce Richardson tbl->items[prev_idx].inner_item.next_pkt_idx;
12499a2dd95SBruce Richardson tbl->items[prev_idx].inner_item.next_pkt_idx = item_idx;
12599a2dd95SBruce Richardson }
12699a2dd95SBruce Richardson
12799a2dd95SBruce Richardson return item_idx;
12899a2dd95SBruce Richardson }
12999a2dd95SBruce Richardson
13099a2dd95SBruce Richardson static inline uint32_t
delete_item(struct gro_vxlan_udp4_tbl * tbl,uint32_t item_idx,uint32_t prev_item_idx)13199a2dd95SBruce Richardson delete_item(struct gro_vxlan_udp4_tbl *tbl,
13299a2dd95SBruce Richardson uint32_t item_idx,
13399a2dd95SBruce Richardson uint32_t prev_item_idx)
13499a2dd95SBruce Richardson {
13599a2dd95SBruce Richardson uint32_t next_idx = tbl->items[item_idx].inner_item.next_pkt_idx;
13699a2dd95SBruce Richardson
13799a2dd95SBruce Richardson /* NULL indicates an empty item. */
13899a2dd95SBruce Richardson tbl->items[item_idx].inner_item.firstseg = NULL;
13999a2dd95SBruce Richardson tbl->item_num--;
14099a2dd95SBruce Richardson if (prev_item_idx != INVALID_ARRAY_INDEX)
14199a2dd95SBruce Richardson tbl->items[prev_item_idx].inner_item.next_pkt_idx = next_idx;
14299a2dd95SBruce Richardson
14399a2dd95SBruce Richardson return next_idx;
14499a2dd95SBruce Richardson }
14599a2dd95SBruce Richardson
14699a2dd95SBruce Richardson static inline uint32_t
insert_new_flow(struct gro_vxlan_udp4_tbl * tbl,struct vxlan_udp4_flow_key * src,uint32_t item_idx)14799a2dd95SBruce Richardson insert_new_flow(struct gro_vxlan_udp4_tbl *tbl,
14899a2dd95SBruce Richardson struct vxlan_udp4_flow_key *src,
14999a2dd95SBruce Richardson uint32_t item_idx)
15099a2dd95SBruce Richardson {
15199a2dd95SBruce Richardson struct vxlan_udp4_flow_key *dst;
15299a2dd95SBruce Richardson uint32_t flow_idx;
15399a2dd95SBruce Richardson
15499a2dd95SBruce Richardson flow_idx = find_an_empty_flow(tbl);
15599a2dd95SBruce Richardson if (unlikely(flow_idx == INVALID_ARRAY_INDEX))
15699a2dd95SBruce Richardson return INVALID_ARRAY_INDEX;
15799a2dd95SBruce Richardson
15899a2dd95SBruce Richardson dst = &(tbl->flows[flow_idx].key);
15999a2dd95SBruce Richardson
16099a2dd95SBruce Richardson rte_ether_addr_copy(&(src->inner_key.eth_saddr),
16199a2dd95SBruce Richardson &(dst->inner_key.eth_saddr));
16299a2dd95SBruce Richardson rte_ether_addr_copy(&(src->inner_key.eth_daddr),
16399a2dd95SBruce Richardson &(dst->inner_key.eth_daddr));
16499a2dd95SBruce Richardson dst->inner_key.ip_src_addr = src->inner_key.ip_src_addr;
16599a2dd95SBruce Richardson dst->inner_key.ip_dst_addr = src->inner_key.ip_dst_addr;
16699a2dd95SBruce Richardson dst->inner_key.ip_id = src->inner_key.ip_id;
16799a2dd95SBruce Richardson
16899a2dd95SBruce Richardson dst->vxlan_hdr.vx_flags = src->vxlan_hdr.vx_flags;
16999a2dd95SBruce Richardson dst->vxlan_hdr.vx_vni = src->vxlan_hdr.vx_vni;
17099a2dd95SBruce Richardson rte_ether_addr_copy(&(src->outer_eth_saddr), &(dst->outer_eth_saddr));
17199a2dd95SBruce Richardson rte_ether_addr_copy(&(src->outer_eth_daddr), &(dst->outer_eth_daddr));
17299a2dd95SBruce Richardson dst->outer_ip_src_addr = src->outer_ip_src_addr;
17399a2dd95SBruce Richardson dst->outer_ip_dst_addr = src->outer_ip_dst_addr;
17499a2dd95SBruce Richardson dst->outer_dst_port = src->outer_dst_port;
17599a2dd95SBruce Richardson
17699a2dd95SBruce Richardson tbl->flows[flow_idx].start_index = item_idx;
17799a2dd95SBruce Richardson tbl->flow_num++;
17899a2dd95SBruce Richardson
17999a2dd95SBruce Richardson return flow_idx;
18099a2dd95SBruce Richardson }
18199a2dd95SBruce Richardson
18299a2dd95SBruce Richardson static inline int
is_same_vxlan_udp4_flow(struct vxlan_udp4_flow_key k1,struct vxlan_udp4_flow_key k2)18399a2dd95SBruce Richardson is_same_vxlan_udp4_flow(struct vxlan_udp4_flow_key k1,
18499a2dd95SBruce Richardson struct vxlan_udp4_flow_key k2)
18599a2dd95SBruce Richardson {
18699a2dd95SBruce Richardson /* For VxLAN packet, outer udp src port is calculated from
18799a2dd95SBruce Richardson * inner packet RSS hash, udp src port of the first UDP
18899a2dd95SBruce Richardson * fragment is different from one of other UDP fragments
18999a2dd95SBruce Richardson * even if they are same flow, so we have to skip outer udp
19099a2dd95SBruce Richardson * src port comparison here.
19199a2dd95SBruce Richardson */
19299a2dd95SBruce Richardson return (rte_is_same_ether_addr(&k1.outer_eth_saddr,
19399a2dd95SBruce Richardson &k2.outer_eth_saddr) &&
19499a2dd95SBruce Richardson rte_is_same_ether_addr(&k1.outer_eth_daddr,
19599a2dd95SBruce Richardson &k2.outer_eth_daddr) &&
19699a2dd95SBruce Richardson (k1.outer_ip_src_addr == k2.outer_ip_src_addr) &&
19799a2dd95SBruce Richardson (k1.outer_ip_dst_addr == k2.outer_ip_dst_addr) &&
19899a2dd95SBruce Richardson (k1.outer_dst_port == k2.outer_dst_port) &&
19999a2dd95SBruce Richardson (k1.vxlan_hdr.vx_flags == k2.vxlan_hdr.vx_flags) &&
20099a2dd95SBruce Richardson (k1.vxlan_hdr.vx_vni == k2.vxlan_hdr.vx_vni) &&
20199a2dd95SBruce Richardson is_same_udp4_flow(k1.inner_key, k2.inner_key));
20299a2dd95SBruce Richardson }
20399a2dd95SBruce Richardson
20499a2dd95SBruce Richardson static inline int
udp4_check_vxlan_neighbor(struct gro_vxlan_udp4_item * item,uint16_t frag_offset,uint16_t ip_dl)20599a2dd95SBruce Richardson udp4_check_vxlan_neighbor(struct gro_vxlan_udp4_item *item,
20699a2dd95SBruce Richardson uint16_t frag_offset,
20799a2dd95SBruce Richardson uint16_t ip_dl)
20899a2dd95SBruce Richardson {
20999a2dd95SBruce Richardson struct rte_mbuf *pkt = item->inner_item.firstseg;
21099a2dd95SBruce Richardson int cmp;
21199a2dd95SBruce Richardson uint16_t l2_offset;
21299a2dd95SBruce Richardson int ret = 0;
21399a2dd95SBruce Richardson
21499a2dd95SBruce Richardson /* Note: if outer DF bit is set, i.e outer_is_atomic is 0,
21599a2dd95SBruce Richardson * we needn't compare outer_ip_id because they are same,
21699a2dd95SBruce Richardson * for the case outer_is_atomic is 1, we also have no way
21799a2dd95SBruce Richardson * to compare outer_ip_id because the difference between
21899a2dd95SBruce Richardson * outer_ip_ids of two received packets isn't always +/-1.
21999a2dd95SBruce Richardson * So skip outer_ip_id comparison here.
22099a2dd95SBruce Richardson */
22199a2dd95SBruce Richardson
22299a2dd95SBruce Richardson l2_offset = pkt->outer_l2_len + pkt->outer_l3_len;
22399a2dd95SBruce Richardson cmp = udp4_check_neighbor(&item->inner_item, frag_offset, ip_dl,
22499a2dd95SBruce Richardson l2_offset);
22599a2dd95SBruce Richardson if (cmp > 0)
22699a2dd95SBruce Richardson /* Append the new packet. */
22799a2dd95SBruce Richardson ret = 1;
22899a2dd95SBruce Richardson else if (cmp < 0)
22999a2dd95SBruce Richardson /* Prepend the new packet. */
23099a2dd95SBruce Richardson ret = -1;
23199a2dd95SBruce Richardson
23299a2dd95SBruce Richardson return ret;
23399a2dd95SBruce Richardson }
23499a2dd95SBruce Richardson
23599a2dd95SBruce Richardson static inline int
merge_two_vxlan_udp4_packets(struct gro_vxlan_udp4_item * item,struct rte_mbuf * pkt,int cmp,uint16_t frag_offset,uint8_t is_last_frag)23699a2dd95SBruce Richardson merge_two_vxlan_udp4_packets(struct gro_vxlan_udp4_item *item,
23799a2dd95SBruce Richardson struct rte_mbuf *pkt,
23899a2dd95SBruce Richardson int cmp,
23999a2dd95SBruce Richardson uint16_t frag_offset,
24099a2dd95SBruce Richardson uint8_t is_last_frag)
24199a2dd95SBruce Richardson {
24299a2dd95SBruce Richardson if (merge_two_udp4_packets(&item->inner_item, pkt, cmp, frag_offset,
24399a2dd95SBruce Richardson is_last_frag,
24499a2dd95SBruce Richardson pkt->outer_l2_len + pkt->outer_l3_len)) {
24599a2dd95SBruce Richardson return 1;
24699a2dd95SBruce Richardson }
24799a2dd95SBruce Richardson
24899a2dd95SBruce Richardson return 0;
24999a2dd95SBruce Richardson }
25099a2dd95SBruce Richardson
25199a2dd95SBruce Richardson static inline void
update_vxlan_header(struct gro_vxlan_udp4_item * item)25299a2dd95SBruce Richardson update_vxlan_header(struct gro_vxlan_udp4_item *item)
25399a2dd95SBruce Richardson {
25499a2dd95SBruce Richardson struct rte_ipv4_hdr *ipv4_hdr;
25599a2dd95SBruce Richardson struct rte_udp_hdr *udp_hdr;
25699a2dd95SBruce Richardson struct rte_mbuf *pkt = item->inner_item.firstseg;
25799a2dd95SBruce Richardson uint16_t len;
25899a2dd95SBruce Richardson uint16_t frag_offset;
25999a2dd95SBruce Richardson
26099a2dd95SBruce Richardson /* Update the outer IPv4 header. */
26199a2dd95SBruce Richardson len = pkt->pkt_len - pkt->outer_l2_len;
262*63a98ffeSStephen Hemminger ipv4_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_ipv4_hdr *,
26399a2dd95SBruce Richardson pkt->outer_l2_len);
26499a2dd95SBruce Richardson ipv4_hdr->total_length = rte_cpu_to_be_16(len);
26599a2dd95SBruce Richardson
26699a2dd95SBruce Richardson /* Update the outer UDP header. */
26799a2dd95SBruce Richardson len -= pkt->outer_l3_len;
26899a2dd95SBruce Richardson udp_hdr = (struct rte_udp_hdr *)((char *)ipv4_hdr + pkt->outer_l3_len);
26999a2dd95SBruce Richardson udp_hdr->dgram_len = rte_cpu_to_be_16(len);
27099a2dd95SBruce Richardson
27199a2dd95SBruce Richardson /* Update the inner IPv4 header. */
27299a2dd95SBruce Richardson len -= pkt->l2_len;
27399a2dd95SBruce Richardson ipv4_hdr = (struct rte_ipv4_hdr *)((char *)udp_hdr + pkt->l2_len);
27499a2dd95SBruce Richardson ipv4_hdr->total_length = rte_cpu_to_be_16(len);
27599a2dd95SBruce Richardson
27699a2dd95SBruce Richardson /* Clear MF bit if it is last fragment */
27799a2dd95SBruce Richardson if (item->inner_item.is_last_frag) {
27899a2dd95SBruce Richardson frag_offset = rte_be_to_cpu_16(ipv4_hdr->fragment_offset);
27999a2dd95SBruce Richardson ipv4_hdr->fragment_offset =
28099a2dd95SBruce Richardson rte_cpu_to_be_16(frag_offset & ~RTE_IPV4_HDR_MF_FLAG);
28199a2dd95SBruce Richardson }
28299a2dd95SBruce Richardson }
28399a2dd95SBruce Richardson
28499a2dd95SBruce Richardson int32_t
gro_vxlan_udp4_reassemble(struct rte_mbuf * pkt,struct gro_vxlan_udp4_tbl * tbl,uint64_t start_time)28599a2dd95SBruce Richardson gro_vxlan_udp4_reassemble(struct rte_mbuf *pkt,
28699a2dd95SBruce Richardson struct gro_vxlan_udp4_tbl *tbl,
28799a2dd95SBruce Richardson uint64_t start_time)
28899a2dd95SBruce Richardson {
28999a2dd95SBruce Richardson struct rte_ether_hdr *outer_eth_hdr, *eth_hdr;
29099a2dd95SBruce Richardson struct rte_ipv4_hdr *outer_ipv4_hdr, *ipv4_hdr;
29199a2dd95SBruce Richardson struct rte_udp_hdr *udp_hdr;
29299a2dd95SBruce Richardson struct rte_vxlan_hdr *vxlan_hdr;
29399a2dd95SBruce Richardson uint16_t frag_offset;
29499a2dd95SBruce Richardson uint8_t is_last_frag;
29599a2dd95SBruce Richardson int16_t ip_dl;
29699a2dd95SBruce Richardson uint16_t ip_id;
29799a2dd95SBruce Richardson
29899a2dd95SBruce Richardson struct vxlan_udp4_flow_key key;
29999a2dd95SBruce Richardson uint32_t cur_idx, prev_idx, item_idx;
30099a2dd95SBruce Richardson uint32_t i, max_flow_num, remaining_flow_num;
30199a2dd95SBruce Richardson int cmp;
30299a2dd95SBruce Richardson uint16_t hdr_len;
30399a2dd95SBruce Richardson uint8_t find;
30499a2dd95SBruce Richardson
30599a2dd95SBruce Richardson outer_eth_hdr = rte_pktmbuf_mtod(pkt, struct rte_ether_hdr *);
30699a2dd95SBruce Richardson outer_ipv4_hdr = (struct rte_ipv4_hdr *)((char *)outer_eth_hdr +
30799a2dd95SBruce Richardson pkt->outer_l2_len);
30899a2dd95SBruce Richardson
30999a2dd95SBruce Richardson udp_hdr = (struct rte_udp_hdr *)((char *)outer_ipv4_hdr +
31099a2dd95SBruce Richardson pkt->outer_l3_len);
31199a2dd95SBruce Richardson vxlan_hdr = (struct rte_vxlan_hdr *)((char *)udp_hdr +
31299a2dd95SBruce Richardson sizeof(struct rte_udp_hdr));
31399a2dd95SBruce Richardson eth_hdr = (struct rte_ether_hdr *)((char *)vxlan_hdr +
31499a2dd95SBruce Richardson sizeof(struct rte_vxlan_hdr));
31599a2dd95SBruce Richardson /* l2_len = outer udp hdr len + vxlan hdr len + inner l2 len */
31699a2dd95SBruce Richardson ipv4_hdr = (struct rte_ipv4_hdr *)((char *)udp_hdr + pkt->l2_len);
31799a2dd95SBruce Richardson
31899a2dd95SBruce Richardson /*
31999a2dd95SBruce Richardson * Don't process the packet which has non-fragment inner IP.
32099a2dd95SBruce Richardson */
32199a2dd95SBruce Richardson if (!is_ipv4_fragment(ipv4_hdr))
32299a2dd95SBruce Richardson return -1;
32399a2dd95SBruce Richardson
32499a2dd95SBruce Richardson hdr_len = pkt->outer_l2_len + pkt->outer_l3_len + pkt->l2_len +
32599a2dd95SBruce Richardson pkt->l3_len;
32699a2dd95SBruce Richardson /*
32799a2dd95SBruce Richardson * Don't process the packet whose payload length is less than or
32899a2dd95SBruce Richardson * equal to 0.
32999a2dd95SBruce Richardson */
33099a2dd95SBruce Richardson if (pkt->pkt_len <= hdr_len)
33199a2dd95SBruce Richardson return -1;
33299a2dd95SBruce Richardson
33399a2dd95SBruce Richardson ip_dl = pkt->pkt_len - hdr_len;
33499a2dd95SBruce Richardson
33599a2dd95SBruce Richardson ip_id = rte_be_to_cpu_16(ipv4_hdr->packet_id);
33699a2dd95SBruce Richardson frag_offset = rte_be_to_cpu_16(ipv4_hdr->fragment_offset);
33799a2dd95SBruce Richardson is_last_frag = ((frag_offset & RTE_IPV4_HDR_MF_FLAG) == 0) ? 1 : 0;
33899a2dd95SBruce Richardson frag_offset = (uint16_t)(frag_offset & RTE_IPV4_HDR_OFFSET_MASK) << 3;
33999a2dd95SBruce Richardson
34004d43857SDmitry Kozlyuk rte_ether_addr_copy(&(eth_hdr->src_addr), &(key.inner_key.eth_saddr));
34104d43857SDmitry Kozlyuk rte_ether_addr_copy(&(eth_hdr->dst_addr), &(key.inner_key.eth_daddr));
34299a2dd95SBruce Richardson key.inner_key.ip_src_addr = ipv4_hdr->src_addr;
34399a2dd95SBruce Richardson key.inner_key.ip_dst_addr = ipv4_hdr->dst_addr;
34499a2dd95SBruce Richardson key.inner_key.ip_id = ip_id;
34599a2dd95SBruce Richardson
34699a2dd95SBruce Richardson key.vxlan_hdr.vx_flags = vxlan_hdr->vx_flags;
34799a2dd95SBruce Richardson key.vxlan_hdr.vx_vni = vxlan_hdr->vx_vni;
34804d43857SDmitry Kozlyuk rte_ether_addr_copy(&(outer_eth_hdr->src_addr), &(key.outer_eth_saddr));
34904d43857SDmitry Kozlyuk rte_ether_addr_copy(&(outer_eth_hdr->dst_addr), &(key.outer_eth_daddr));
35099a2dd95SBruce Richardson key.outer_ip_src_addr = outer_ipv4_hdr->src_addr;
35199a2dd95SBruce Richardson key.outer_ip_dst_addr = outer_ipv4_hdr->dst_addr;
35299a2dd95SBruce Richardson /* Note: It is unnecessary to save outer_src_port here because it can
35399a2dd95SBruce Richardson * be different for VxLAN UDP fragments from the same flow.
35499a2dd95SBruce Richardson */
35599a2dd95SBruce Richardson key.outer_dst_port = udp_hdr->dst_port;
35699a2dd95SBruce Richardson
35799a2dd95SBruce Richardson /* Search for a matched flow. */
35899a2dd95SBruce Richardson max_flow_num = tbl->max_flow_num;
35999a2dd95SBruce Richardson remaining_flow_num = tbl->flow_num;
36099a2dd95SBruce Richardson find = 0;
36199a2dd95SBruce Richardson for (i = 0; i < max_flow_num && remaining_flow_num; i++) {
36299a2dd95SBruce Richardson if (tbl->flows[i].start_index != INVALID_ARRAY_INDEX) {
36399a2dd95SBruce Richardson if (is_same_vxlan_udp4_flow(tbl->flows[i].key, key)) {
36499a2dd95SBruce Richardson find = 1;
36599a2dd95SBruce Richardson break;
36699a2dd95SBruce Richardson }
36799a2dd95SBruce Richardson remaining_flow_num--;
36899a2dd95SBruce Richardson }
36999a2dd95SBruce Richardson }
37099a2dd95SBruce Richardson
37199a2dd95SBruce Richardson /*
37299a2dd95SBruce Richardson * Can't find a matched flow. Insert a new flow and store the
37399a2dd95SBruce Richardson * packet into the flow.
37499a2dd95SBruce Richardson */
37599a2dd95SBruce Richardson if (find == 0) {
37699a2dd95SBruce Richardson item_idx = insert_new_item(tbl, pkt, start_time,
37799a2dd95SBruce Richardson INVALID_ARRAY_INDEX, frag_offset,
37899a2dd95SBruce Richardson is_last_frag);
37999a2dd95SBruce Richardson if (unlikely(item_idx == INVALID_ARRAY_INDEX))
38099a2dd95SBruce Richardson return -1;
38199a2dd95SBruce Richardson if (insert_new_flow(tbl, &key, item_idx) ==
38299a2dd95SBruce Richardson INVALID_ARRAY_INDEX) {
38399a2dd95SBruce Richardson /*
38499a2dd95SBruce Richardson * Fail to insert a new flow, so
38599a2dd95SBruce Richardson * delete the inserted packet.
38699a2dd95SBruce Richardson */
38799a2dd95SBruce Richardson delete_item(tbl, item_idx, INVALID_ARRAY_INDEX);
38899a2dd95SBruce Richardson return -1;
38999a2dd95SBruce Richardson }
39099a2dd95SBruce Richardson return 0;
39199a2dd95SBruce Richardson }
39299a2dd95SBruce Richardson
39399a2dd95SBruce Richardson /* Check all packets in the flow and try to find a neighbor. */
39499a2dd95SBruce Richardson cur_idx = tbl->flows[i].start_index;
39599a2dd95SBruce Richardson prev_idx = cur_idx;
39699a2dd95SBruce Richardson do {
39799a2dd95SBruce Richardson cmp = udp4_check_vxlan_neighbor(&(tbl->items[cur_idx]),
39899a2dd95SBruce Richardson frag_offset, ip_dl);
39999a2dd95SBruce Richardson if (cmp) {
40099a2dd95SBruce Richardson if (merge_two_vxlan_udp4_packets(
40199a2dd95SBruce Richardson &(tbl->items[cur_idx]),
40299a2dd95SBruce Richardson pkt, cmp, frag_offset,
40399a2dd95SBruce Richardson is_last_frag)) {
40499a2dd95SBruce Richardson return 1;
40599a2dd95SBruce Richardson }
40699a2dd95SBruce Richardson /*
40799a2dd95SBruce Richardson * Can't merge two packets, as the packet
40899a2dd95SBruce Richardson * length will be greater than the max value.
40999a2dd95SBruce Richardson * Insert the packet into the flow.
41099a2dd95SBruce Richardson */
41199a2dd95SBruce Richardson if (insert_new_item(tbl, pkt, start_time, prev_idx,
41299a2dd95SBruce Richardson frag_offset, is_last_frag) ==
41399a2dd95SBruce Richardson INVALID_ARRAY_INDEX)
41499a2dd95SBruce Richardson return -1;
41599a2dd95SBruce Richardson return 0;
41699a2dd95SBruce Richardson }
41799a2dd95SBruce Richardson
41899a2dd95SBruce Richardson /* Ensure inserted items are ordered by frag_offset */
41999a2dd95SBruce Richardson if (frag_offset
42099a2dd95SBruce Richardson < tbl->items[cur_idx].inner_item.frag_offset) {
42199a2dd95SBruce Richardson break;
42299a2dd95SBruce Richardson }
42399a2dd95SBruce Richardson
42499a2dd95SBruce Richardson prev_idx = cur_idx;
42599a2dd95SBruce Richardson cur_idx = tbl->items[cur_idx].inner_item.next_pkt_idx;
42699a2dd95SBruce Richardson } while (cur_idx != INVALID_ARRAY_INDEX);
42799a2dd95SBruce Richardson
42899a2dd95SBruce Richardson /* Can't find neighbor. Insert the packet into the flow. */
42999a2dd95SBruce Richardson if (cur_idx == tbl->flows[i].start_index) {
43099a2dd95SBruce Richardson /* Insert it before the first packet of the flow */
43199a2dd95SBruce Richardson item_idx = insert_new_item(tbl, pkt, start_time,
43299a2dd95SBruce Richardson INVALID_ARRAY_INDEX, frag_offset,
43399a2dd95SBruce Richardson is_last_frag);
43499a2dd95SBruce Richardson if (unlikely(item_idx == INVALID_ARRAY_INDEX))
43599a2dd95SBruce Richardson return -1;
43699a2dd95SBruce Richardson tbl->items[item_idx].inner_item.next_pkt_idx = cur_idx;
43799a2dd95SBruce Richardson tbl->flows[i].start_index = item_idx;
43899a2dd95SBruce Richardson } else {
43999a2dd95SBruce Richardson if (insert_new_item(tbl, pkt, start_time, prev_idx,
44099a2dd95SBruce Richardson frag_offset, is_last_frag
44199a2dd95SBruce Richardson ) == INVALID_ARRAY_INDEX)
44299a2dd95SBruce Richardson return -1;
44399a2dd95SBruce Richardson }
44499a2dd95SBruce Richardson
44599a2dd95SBruce Richardson return 0;
44699a2dd95SBruce Richardson }
44799a2dd95SBruce Richardson
44899a2dd95SBruce Richardson static int
gro_vxlan_udp4_merge_items(struct gro_vxlan_udp4_tbl * tbl,uint32_t start_idx)44999a2dd95SBruce Richardson gro_vxlan_udp4_merge_items(struct gro_vxlan_udp4_tbl *tbl,
45099a2dd95SBruce Richardson uint32_t start_idx)
45199a2dd95SBruce Richardson {
45299a2dd95SBruce Richardson uint16_t frag_offset;
45399a2dd95SBruce Richardson uint8_t is_last_frag;
45499a2dd95SBruce Richardson int16_t ip_dl;
45599a2dd95SBruce Richardson struct rte_mbuf *pkt;
45699a2dd95SBruce Richardson int cmp;
45799a2dd95SBruce Richardson uint32_t item_idx;
45899a2dd95SBruce Richardson uint16_t hdr_len;
45999a2dd95SBruce Richardson
46099a2dd95SBruce Richardson item_idx = tbl->items[start_idx].inner_item.next_pkt_idx;
46199a2dd95SBruce Richardson while (item_idx != INVALID_ARRAY_INDEX) {
46299a2dd95SBruce Richardson pkt = tbl->items[item_idx].inner_item.firstseg;
46399a2dd95SBruce Richardson hdr_len = pkt->outer_l2_len + pkt->outer_l3_len + pkt->l2_len +
46499a2dd95SBruce Richardson pkt->l3_len;
46599a2dd95SBruce Richardson ip_dl = pkt->pkt_len - hdr_len;
46699a2dd95SBruce Richardson frag_offset = tbl->items[item_idx].inner_item.frag_offset;
46799a2dd95SBruce Richardson is_last_frag = tbl->items[item_idx].inner_item.is_last_frag;
46899a2dd95SBruce Richardson cmp = udp4_check_vxlan_neighbor(&(tbl->items[start_idx]),
46999a2dd95SBruce Richardson frag_offset, ip_dl);
47099a2dd95SBruce Richardson if (cmp) {
47199a2dd95SBruce Richardson if (merge_two_vxlan_udp4_packets(
47299a2dd95SBruce Richardson &(tbl->items[start_idx]),
47399a2dd95SBruce Richardson pkt, cmp, frag_offset,
47499a2dd95SBruce Richardson is_last_frag)) {
47599a2dd95SBruce Richardson item_idx = delete_item(tbl, item_idx,
47699a2dd95SBruce Richardson INVALID_ARRAY_INDEX);
47799a2dd95SBruce Richardson tbl->items[start_idx].inner_item.next_pkt_idx
47899a2dd95SBruce Richardson = item_idx;
47999a2dd95SBruce Richardson } else
48099a2dd95SBruce Richardson return 0;
48199a2dd95SBruce Richardson } else
48299a2dd95SBruce Richardson return 0;
48399a2dd95SBruce Richardson }
48499a2dd95SBruce Richardson
48599a2dd95SBruce Richardson return 0;
48699a2dd95SBruce Richardson }
48799a2dd95SBruce Richardson
48899a2dd95SBruce Richardson uint16_t
gro_vxlan_udp4_tbl_timeout_flush(struct gro_vxlan_udp4_tbl * tbl,uint64_t flush_timestamp,struct rte_mbuf ** out,uint16_t nb_out)48999a2dd95SBruce Richardson gro_vxlan_udp4_tbl_timeout_flush(struct gro_vxlan_udp4_tbl *tbl,
49099a2dd95SBruce Richardson uint64_t flush_timestamp,
49199a2dd95SBruce Richardson struct rte_mbuf **out,
49299a2dd95SBruce Richardson uint16_t nb_out)
49399a2dd95SBruce Richardson {
49499a2dd95SBruce Richardson uint16_t k = 0;
49599a2dd95SBruce Richardson uint32_t i, j;
49699a2dd95SBruce Richardson uint32_t max_flow_num = tbl->max_flow_num;
49799a2dd95SBruce Richardson
49899a2dd95SBruce Richardson for (i = 0; i < max_flow_num; i++) {
49999a2dd95SBruce Richardson if (unlikely(tbl->flow_num == 0))
50099a2dd95SBruce Richardson return k;
50199a2dd95SBruce Richardson
50299a2dd95SBruce Richardson j = tbl->flows[i].start_index;
50399a2dd95SBruce Richardson while (j != INVALID_ARRAY_INDEX) {
50499a2dd95SBruce Richardson if (tbl->items[j].inner_item.start_time <=
50599a2dd95SBruce Richardson flush_timestamp) {
50699a2dd95SBruce Richardson gro_vxlan_udp4_merge_items(tbl, j);
50799a2dd95SBruce Richardson out[k++] = tbl->items[j].inner_item.firstseg;
50899a2dd95SBruce Richardson if (tbl->items[j].inner_item.nb_merged > 1)
50999a2dd95SBruce Richardson update_vxlan_header(&(tbl->items[j]));
51099a2dd95SBruce Richardson /*
51199a2dd95SBruce Richardson * Delete the item and get the next packet
51299a2dd95SBruce Richardson * index.
51399a2dd95SBruce Richardson */
51499a2dd95SBruce Richardson j = delete_item(tbl, j, INVALID_ARRAY_INDEX);
51599a2dd95SBruce Richardson tbl->flows[i].start_index = j;
51699a2dd95SBruce Richardson if (j == INVALID_ARRAY_INDEX)
51799a2dd95SBruce Richardson tbl->flow_num--;
51899a2dd95SBruce Richardson
51999a2dd95SBruce Richardson if (unlikely(k == nb_out))
52099a2dd95SBruce Richardson return k;
52199a2dd95SBruce Richardson } else
52299a2dd95SBruce Richardson /*
52399a2dd95SBruce Richardson * Flushing packets does not strictly follow
52499a2dd95SBruce Richardson * timestamp. It does not flush left packets of
52599a2dd95SBruce Richardson * the flow this time once it finds one item
52699a2dd95SBruce Richardson * whose start_time is greater than
52799a2dd95SBruce Richardson * flush_timestamp. So go to check other flows.
52899a2dd95SBruce Richardson */
52999a2dd95SBruce Richardson break;
53099a2dd95SBruce Richardson }
53199a2dd95SBruce Richardson }
53299a2dd95SBruce Richardson return k;
53399a2dd95SBruce Richardson }
53499a2dd95SBruce Richardson
53599a2dd95SBruce Richardson uint32_t
gro_vxlan_udp4_tbl_pkt_count(void * tbl)53699a2dd95SBruce Richardson gro_vxlan_udp4_tbl_pkt_count(void *tbl)
53799a2dd95SBruce Richardson {
53899a2dd95SBruce Richardson struct gro_vxlan_udp4_tbl *gro_tbl = tbl;
53999a2dd95SBruce Richardson
54099a2dd95SBruce Richardson if (gro_tbl)
54199a2dd95SBruce Richardson return gro_tbl->item_num;
54299a2dd95SBruce Richardson
54399a2dd95SBruce Richardson return 0;
54499a2dd95SBruce Richardson }
545