1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2020 Inspur Corporation 3 */ 4 5 #ifndef _GRO_UDP4_H_ 6 #define _GRO_UDP4_H_ 7 8 #include <rte_ip.h> 9 10 #define INVALID_ARRAY_INDEX 0xffffffffUL 11 #define GRO_UDP4_TBL_MAX_ITEM_NUM (1024UL * 1024UL) 12 13 /* 14 * The max length of a IPv4 packet, which includes the length of the L3 15 * header, the L4 header and the data payload. 16 */ 17 #define MAX_IPV4_PKT_LENGTH UINT16_MAX 18 19 /* Header fields representing a UDP/IPv4 flow */ 20 struct udp4_flow_key { 21 struct rte_ether_addr eth_saddr; 22 struct rte_ether_addr eth_daddr; 23 uint32_t ip_src_addr; 24 uint32_t ip_dst_addr; 25 26 /* IP fragment for UDP does not contain UDP header 27 * except the first one. But IP ID must be same. 28 */ 29 uint16_t ip_id; 30 }; 31 32 struct gro_udp4_flow { 33 struct udp4_flow_key key; 34 /* 35 * The index of the first packet in the flow. 36 * INVALID_ARRAY_INDEX indicates an empty flow. 37 */ 38 uint32_t start_index; 39 }; 40 41 struct gro_udp4_item { 42 /* 43 * The first MBUF segment of the packet. If the value 44 * is NULL, it means the item is empty. 45 */ 46 struct rte_mbuf *firstseg; 47 /* The last MBUF segment of the packet */ 48 struct rte_mbuf *lastseg; 49 /* 50 * The time when the first packet is inserted into the table. 51 * This value won't be updated, even if the packet is merged 52 * with other packets. 53 */ 54 uint64_t start_time; 55 /* 56 * next_pkt_idx is used to chain the packets that 57 * are in the same flow but can't be merged together 58 * (e.g. caused by packet reordering). 59 */ 60 uint32_t next_pkt_idx; 61 /* offset of IP fragment packet */ 62 uint16_t frag_offset; 63 /* is last IP fragment? */ 64 uint8_t is_last_frag; 65 /* the number of merged packets */ 66 uint16_t nb_merged; 67 }; 68 69 /* 70 * UDP/IPv4 reassembly table structure. 71 */ 72 struct gro_udp4_tbl { 73 /* item array */ 74 struct gro_udp4_item *items; 75 /* flow array */ 76 struct gro_udp4_flow *flows; 77 /* current item number */ 78 uint32_t item_num; 79 /* current flow num */ 80 uint32_t flow_num; 81 /* item array size */ 82 uint32_t max_item_num; 83 /* flow array size */ 84 uint32_t max_flow_num; 85 }; 86 87 /** 88 * This function creates a UDP/IPv4 reassembly table. 89 * 90 * @param socket_id 91 * Socket index for allocating the UDP/IPv4 reassemble table 92 * @param max_flow_num 93 * The maximum number of flows in the UDP/IPv4 GRO table 94 * @param max_item_per_flow 95 * The maximum number of packets per flow 96 * 97 * @return 98 * - Return the table pointer on success. 99 * - Return NULL on failure. 100 */ 101 void *gro_udp4_tbl_create(uint16_t socket_id, 102 uint16_t max_flow_num, 103 uint16_t max_item_per_flow); 104 105 /** 106 * This function destroys a UDP/IPv4 reassembly table. 107 * 108 * @param tbl 109 * Pointer pointing to the UDP/IPv4 reassembly table. 110 */ 111 void gro_udp4_tbl_destroy(void *tbl); 112 113 /** 114 * This function merges a UDP/IPv4 packet. 115 * 116 * This function does not check if the packet has correct checksums and 117 * does not re-calculate checksums for the merged packet. It returns the 118 * packet if it isn't UDP fragment or there is no available space in 119 * the table. 120 * 121 * @param pkt 122 * Packet to reassemble 123 * @param tbl 124 * Pointer pointing to the UDP/IPv4 reassembly table 125 * @start_time 126 * The time when the packet is inserted into the table 127 * 128 * @return 129 * - Return a positive value if the packet is merged. 130 * - Return zero if the packet isn't merged but stored in the table. 131 * - Return a negative value for invalid parameters or no available 132 * space in the table. 133 */ 134 int32_t gro_udp4_reassemble(struct rte_mbuf *pkt, 135 struct gro_udp4_tbl *tbl, 136 uint64_t start_time); 137 138 /** 139 * This function flushes timeout packets in a UDP/IPv4 reassembly table, 140 * and without updating checksums. 141 * 142 * @param tbl 143 * UDP/IPv4 reassembly table pointer 144 * @param flush_timestamp 145 * Flush packets which are inserted into the table before or at the 146 * flush_timestamp. 147 * @param out 148 * Pointer array used to keep flushed packets 149 * @param nb_out 150 * The element number in 'out'. It also determines the maximum number of 151 * packets that can be flushed finally. 152 * 153 * @return 154 * The number of flushed packets 155 */ 156 uint16_t gro_udp4_tbl_timeout_flush(struct gro_udp4_tbl *tbl, 157 uint64_t flush_timestamp, 158 struct rte_mbuf **out, 159 uint16_t nb_out); 160 161 /** 162 * This function returns the number of the packets in a UDP/IPv4 163 * reassembly table. 164 * 165 * @param tbl 166 * UDP/IPv4 reassembly table pointer 167 * 168 * @return 169 * The number of packets in the table 170 */ 171 uint32_t gro_udp4_tbl_pkt_count(void *tbl); 172 173 /* 174 * Check if two UDP/IPv4 packets belong to the same flow. 175 */ 176 static inline int 177 is_same_udp4_flow(struct udp4_flow_key k1, struct udp4_flow_key k2) 178 { 179 return (rte_is_same_ether_addr(&k1.eth_saddr, &k2.eth_saddr) && 180 rte_is_same_ether_addr(&k1.eth_daddr, &k2.eth_daddr) && 181 (k1.ip_src_addr == k2.ip_src_addr) && 182 (k1.ip_dst_addr == k2.ip_dst_addr) && 183 (k1.ip_id == k2.ip_id)); 184 } 185 186 /* 187 * Merge two UDP/IPv4 packets without updating checksums. 188 * If cmp is larger than 0, append the new packet to the 189 * original packet. Otherwise, pre-pend the new packet to 190 * the original packet. 191 */ 192 static inline int 193 merge_two_udp4_packets(struct gro_udp4_item *item, 194 struct rte_mbuf *pkt, 195 int cmp, 196 uint16_t frag_offset, 197 uint8_t is_last_frag, 198 uint16_t l2_offset) 199 { 200 struct rte_mbuf *pkt_head, *pkt_tail, *lastseg; 201 uint16_t hdr_len, l2_len; 202 uint32_t ip_len; 203 204 if (cmp > 0) { 205 pkt_head = item->firstseg; 206 pkt_tail = pkt; 207 } else { 208 pkt_head = pkt; 209 pkt_tail = item->firstseg; 210 } 211 212 /* check if the IPv4 packet length is greater than the max value */ 213 hdr_len = l2_offset + pkt_head->l2_len + pkt_head->l3_len; 214 l2_len = l2_offset > 0 ? pkt_head->outer_l2_len : pkt_head->l2_len; 215 ip_len = pkt_head->pkt_len - l2_len 216 + pkt_tail->pkt_len - hdr_len; 217 if (unlikely(ip_len > MAX_IPV4_PKT_LENGTH)) 218 return 0; 219 220 /* remove the packet header for the tail packet */ 221 rte_pktmbuf_adj(pkt_tail, hdr_len); 222 223 /* chain two packets together */ 224 if (cmp > 0) { 225 item->lastseg->next = pkt; 226 item->lastseg = rte_pktmbuf_lastseg(pkt); 227 } else { 228 lastseg = rte_pktmbuf_lastseg(pkt); 229 lastseg->next = item->firstseg; 230 item->firstseg = pkt; 231 item->frag_offset = frag_offset; 232 } 233 item->nb_merged++; 234 if (is_last_frag) 235 item->is_last_frag = is_last_frag; 236 237 /* update MBUF metadata for the merged packet */ 238 pkt_head->nb_segs += pkt_tail->nb_segs; 239 pkt_head->pkt_len += pkt_tail->pkt_len; 240 241 return 1; 242 } 243 244 /* 245 * Check if two UDP/IPv4 packets are neighbors. 246 */ 247 static inline int 248 udp4_check_neighbor(struct gro_udp4_item *item, 249 uint16_t frag_offset, 250 uint16_t ip_dl, 251 uint16_t l2_offset) 252 { 253 struct rte_mbuf *pkt_orig = item->firstseg; 254 uint16_t len; 255 256 /* check if the two packets are neighbors */ 257 len = pkt_orig->pkt_len - l2_offset - pkt_orig->l2_len - 258 pkt_orig->l3_len; 259 if (frag_offset == item->frag_offset + len) 260 /* append the new packet */ 261 return 1; 262 else if (frag_offset + ip_dl == item->frag_offset) 263 /* pre-pend the new packet */ 264 return -1; 265 266 return 0; 267 } 268 269 static inline int 270 is_ipv4_fragment(const struct rte_ipv4_hdr *hdr) 271 { 272 uint16_t flag_offset, ip_flag, ip_ofs; 273 274 flag_offset = rte_be_to_cpu_16(hdr->fragment_offset); 275 ip_ofs = (uint16_t)(flag_offset & RTE_IPV4_HDR_OFFSET_MASK); 276 ip_flag = (uint16_t)(flag_offset & RTE_IPV4_HDR_MF_FLAG); 277 278 return ip_flag != 0 || ip_ofs != 0; 279 } 280 #endif 281