4 * Copyright(c) 2010-2013 Intel Corporation. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * * Neither the name of Intel Corporation nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #ifndef _IPV4_RSMBL_H_
36 #define _IPV4_RSMBL_H_
42 * Implementation of IPv4 reassemble.
60 * Use <src addr, dst_addr, id> to uniquely indetify fragmented datagram.
62 struct ipv4_frag_key {
67 #define IPV4_FRAG_KEY_INVALIDATE(k) ((k)->src_dst = 0)
68 #define IPV4_FRAG_KEY_EMPTY(k) ((k)->src_dst == 0)
70 #define IPV4_FRAG_KEY_CMP(k1, k2) \
71 (((k1)->src_dst ^ (k2)->src_dst) | ((k1)->id ^ (k2)->id))
75 * Fragmented packet to reassemble.
76 * First two entries in the frags[] array are for the last and first fragments.
78 struct ipv4_frag_pkt {
79 TAILQ_ENTRY(ipv4_frag_pkt) lru; /* LRU list */
80 struct ipv4_frag_key key;
81 uint64_t start; /* creation timestamp */
82 uint32_t total_size; /* expected reassembled size */
83 uint32_t frag_size; /* size of fragments received */
84 uint32_t last_idx; /* index of next entry to fill */
85 struct ipv4_frag frags[MAX_FRAG_NUM];
86 } __rte_cache_aligned;
89 struct ipv4_frag_death_row {
91 struct rte_mbuf *row[MAX_PKT_BURST * (MAX_FRAG_NUM + 1)];
94 #define IPV4_FRAG_MBUF2DR(dr, mb) ((dr)->row[(dr)->cnt++] = (mb))
98 #ifdef IPV4_FRAG_DEBUG
99 #define IPV4_FRAG_LOG(lvl, fmt, args...) RTE_LOG(lvl, USER1, fmt, ##args)
101 #define IPV4_FRAG_LOG(lvl, fmt, args...) do {} while(0)
102 #endif /* IPV4_FRAG_DEBUG */
106 ipv4_frag_reset(struct ipv4_frag_pkt *fp, uint64_t tms)
108 static const struct ipv4_frag zero_frag = {
115 fp->total_size = UINT32_MAX;
117 fp->last_idx = MIN_FRAG_NUM;
118 fp->frags[LAST_FRAG_IDX] = zero_frag;
119 fp->frags[FIRST_FRAG_IDX] = zero_frag;
123 ipv4_frag_free(struct ipv4_frag_pkt *fp, struct ipv4_frag_death_row *dr)
128 for (i = 0; i != fp->last_idx; i++) {
129 if (fp->frags[i].mb != NULL) {
130 dr->row[k++] = fp->frags[i].mb;
131 fp->frags[i].mb = NULL;
140 ipv4_frag_free_death_row(struct ipv4_frag_death_row *dr, uint32_t prefetch)
144 k = RTE_MIN(prefetch, dr->cnt);
147 for (i = 0; i != k; i++)
148 rte_prefetch0(dr->row[i]);
150 for (i = 0; i != n - k; i++) {
151 rte_prefetch0(dr->row[i + k]);
152 rte_pktmbuf_free(dr->row[i]);
156 rte_pktmbuf_free(dr->row[i]);
163 * Takes 2 mbufs that represents two framents of the same packet and
164 * chains them into one mbuf.
167 ipv4_frag_chain(struct rte_mbuf *mn, struct rte_mbuf *mp)
171 /* adjust start of the last fragment data. */
172 rte_pktmbuf_adj(mp, (uint16_t)(mp->pkt.vlan_macip.f.l2_len +
173 mp->pkt.vlan_macip.f.l3_len));
175 /* chain two fragments. */
176 ms = rte_pktmbuf_lastseg(mn);
179 /* accumulate number of segments and total length. */
180 mn->pkt.nb_segs = (uint8_t)(mn->pkt.nb_segs + mp->pkt.nb_segs);
181 mn->pkt.pkt_len += mp->pkt.pkt_len;
183 /* reset pkt_len and nb_segs for chained fragment. */
184 mp->pkt.pkt_len = mp->pkt.data_len;
189 * Reassemble fragments into one packet.
191 static inline struct rte_mbuf *
192 ipv4_frag_reassemble(const struct ipv4_frag_pkt *fp)
194 struct ipv4_hdr *ip_hdr;
195 struct rte_mbuf *m, *prev;
196 uint32_t i, n, ofs, first_len;
198 first_len = fp->frags[FIRST_FRAG_IDX].len;
199 n = fp->last_idx - 1;
201 /*start from the last fragment. */
202 m = fp->frags[LAST_FRAG_IDX].mb;
203 ofs = fp->frags[LAST_FRAG_IDX].ofs;
205 while (ofs != first_len) {
209 for (i = n; i != FIRST_FRAG_IDX && ofs != first_len; i--) {
211 /* previous fragment found. */
212 if(fp->frags[i].ofs + fp->frags[i].len == ofs) {
214 ipv4_frag_chain(fp->frags[i].mb, m);
216 /* update our last fragment and offset. */
218 ofs = fp->frags[i].ofs;
222 /* error - hole in the packet. */
228 /* chain with the first fragment. */
229 ipv4_frag_chain(fp->frags[FIRST_FRAG_IDX].mb, m);
230 m = fp->frags[FIRST_FRAG_IDX].mb;
232 /* update mbuf fields for reassembled packet. */
233 m->ol_flags |= PKT_TX_IP_CKSUM;
235 /* update ipv4 header for the reassmebled packet */
236 ip_hdr = (struct ipv4_hdr*)(rte_pktmbuf_mtod(m, uint8_t *) +
237 m->pkt.vlan_macip.f.l2_len);
239 ip_hdr->total_length = rte_cpu_to_be_16((uint16_t)(fp->total_size +
240 m->pkt.vlan_macip.f.l3_len));
241 ip_hdr->fragment_offset = (uint16_t)(ip_hdr->fragment_offset &
242 rte_cpu_to_be_16(IPV4_HDR_DF_FLAG));
243 ip_hdr->hdr_checksum = 0;
248 static inline struct rte_mbuf *
249 ipv4_frag_process(struct ipv4_frag_pkt *fp, struct ipv4_frag_death_row *dr,
250 struct rte_mbuf *mb, uint16_t ofs, uint16_t len, uint16_t more_frags)
254 fp->frag_size += len;
256 /* this is the first fragment. */
258 idx = (fp->frags[FIRST_FRAG_IDX].mb == NULL) ?
259 FIRST_FRAG_IDX : UINT32_MAX;
261 /* this is the last fragment. */
262 } else if (more_frags == 0) {
263 fp->total_size = ofs + len;
264 idx = (fp->frags[LAST_FRAG_IDX].mb == NULL) ?
265 LAST_FRAG_IDX : UINT32_MAX;
267 /* this is the intermediate fragment. */
268 } else if ((idx = fp->last_idx) <
269 sizeof (fp->frags) / sizeof (fp->frags[0])) {
274 * errorneous packet: either exceeed max allowed number of fragments,
275 * or duplicate first/last fragment encountered.
277 if (idx >= sizeof (fp->frags) / sizeof (fp->frags[0])) {
279 /* report an error. */
280 IPV4_FRAG_LOG(DEBUG, "%s:%d invalid fragmented packet:\n"
281 "ipv4_frag_pkt: %p, key: <%" PRIx64 ", %#x>, "
282 "total_size: %u, frag_size: %u, last_idx: %u\n"
283 "first fragment: ofs: %u, len: %u\n"
284 "last fragment: ofs: %u, len: %u\n\n",
286 fp, fp->key.src_dst, fp->key.id,
287 fp->total_size, fp->frag_size, fp->last_idx,
288 fp->frags[FIRST_FRAG_IDX].ofs,
289 fp->frags[FIRST_FRAG_IDX].len,
290 fp->frags[LAST_FRAG_IDX].ofs,
291 fp->frags[LAST_FRAG_IDX].len);
293 /* free all fragments, invalidate the entry. */
294 ipv4_frag_free(fp, dr);
295 IPV4_FRAG_KEY_INVALIDATE(&fp->key);
296 IPV4_FRAG_MBUF2DR(dr, mb);
301 fp->frags[idx].ofs = ofs;
302 fp->frags[idx].len = len;
303 fp->frags[idx].mb = mb;
307 /* not all fragments are collected yet. */
308 if (likely (fp->frag_size < fp->total_size)) {
311 /* if we collected all fragments, then try to reassemble. */
312 } else if (fp->frag_size == fp->total_size &&
313 fp->frags[FIRST_FRAG_IDX].mb != NULL) {
314 mb = ipv4_frag_reassemble(fp);
317 /* errorenous set of fragments. */
320 /* report an error. */
321 IPV4_FRAG_LOG(DEBUG, "%s:%d invalid fragmented packet:\n"
322 "ipv4_frag_pkt: %p, key: <%" PRIx64 ", %#x>, "
323 "total_size: %u, frag_size: %u, last_idx: %u\n"
324 "first fragment: ofs: %u, len: %u\n"
325 "last fragment: ofs: %u, len: %u\n\n",
327 fp, fp->key.src_dst, fp->key.id,
328 fp->total_size, fp->frag_size, fp->last_idx,
329 fp->frags[FIRST_FRAG_IDX].ofs,
330 fp->frags[FIRST_FRAG_IDX].len,
331 fp->frags[LAST_FRAG_IDX].ofs,
332 fp->frags[LAST_FRAG_IDX].len);
334 /* free associated resources. */
335 ipv4_frag_free(fp, dr);
338 /* we are done with that entry, invalidate it. */
339 IPV4_FRAG_KEY_INVALIDATE(&fp->key);
343 #include "ipv4_frag_tbl.h"
346 * Process new mbuf with fragment of IPV4 packet.
347 * Incoming mbuf should have it's l2_len/l3_len fields setuped correclty.
349 * Table where to lookup/add the fragmented packet.
351 * Incoming mbuf with IPV4 fragment.
353 * Fragment arrival timestamp.
355 * Pointer to the IPV4 header inside the fragment.
357 * Fragment's offset (as extracted from the header).
359 * Fragment's MF flag.
361 * Pointer to mbuf for reassebled packet, or NULL if:
362 * - an error occured.
363 * - not all fragments of the packet are collected yet.
365 static inline struct rte_mbuf *
366 ipv4_frag_mbuf(struct ipv4_frag_tbl *tbl, struct ipv4_frag_death_row *dr,
367 struct rte_mbuf *mb, uint64_t tms, struct ipv4_hdr *ip_hdr,
368 uint16_t ip_ofs, uint16_t ip_flag)
370 struct ipv4_frag_pkt *fp;
371 struct ipv4_frag_key key;
375 psd = (uint64_t *)&ip_hdr->src_addr;
376 key.src_dst = psd[0];
377 key.id = ip_hdr->packet_id;
379 ip_ofs *= IPV4_HDR_OFFSET_UNITS;
380 ip_len = (uint16_t)(rte_be_to_cpu_16(ip_hdr->total_length) -
381 mb->pkt.vlan_macip.f.l3_len);
383 IPV4_FRAG_LOG(DEBUG, "%s:%d:\n"
384 "mbuf: %p, tms: %" PRIu64
385 ", key: <%" PRIx64 ", %#x>, ofs: %u, len: %u, flags: %#x\n"
386 "tbl: %p, max_cycles: %" PRIu64 ", entry_mask: %#x, "
387 "max_entries: %u, use_entries: %u\n\n",
389 mb, tms, key.src_dst, key.id, ip_ofs, ip_len, ip_flag,
390 tbl, tbl->max_cycles, tbl->entry_mask, tbl->max_entries,
393 /* try to find/add entry into the fragment's table. */
394 if ((fp = ipv4_frag_find(tbl, dr, &key, tms)) == NULL) {
395 IPV4_FRAG_MBUF2DR(dr, mb);
399 IPV4_FRAG_LOG(DEBUG, "%s:%d:\n"
400 "tbl: %p, max_entries: %u, use_entries: %u\n"
401 "ipv4_frag_pkt: %p, key: <%" PRIx64 ", %#x>, start: %" PRIu64
402 ", total_size: %u, frag_size: %u, last_idx: %u\n\n",
404 tbl, tbl->max_entries, tbl->use_entries,
405 fp, fp->key.src_dst, fp->key.id, fp->start,
406 fp->total_size, fp->frag_size, fp->last_idx);
409 /* process the fragmented packet. */
410 mb = ipv4_frag_process(fp, dr, mb, ip_ofs, ip_len, ip_flag);
411 ipv4_frag_inuse(tbl, fp);
413 IPV4_FRAG_LOG(DEBUG, "%s:%d:\n"
415 "tbl: %p, max_entries: %u, use_entries: %u\n"
416 "ipv4_frag_pkt: %p, key: <%" PRIx64 ", %#x>, start: %" PRIu64
417 ", total_size: %u, frag_size: %u, last_idx: %u\n\n",
418 __func__, __LINE__, mb,
419 tbl, tbl->max_entries, tbl->use_entries,
420 fp, fp->key.src_dst, fp->key.id, fp->start,
421 fp->total_size, fp->frag_size, fp->last_idx);
426 #endif /* _IPV4_RSMBL_H_ */