01cb95b07df6e6a85568170227e4ecb7a1c7fd09
[dpdk.git] / examples / ip_reassembly / ipv4_rsmbl.h
1 /*-
2  *   BSD LICENSE
3  * 
4  *   Copyright(c) 2010-2013 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  * 
7  *   Redistribution and use in source and binary forms, with or without 
8  *   modification, are permitted provided that the following conditions 
9  *   are met:
10  * 
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 
16  *       distribution.
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.
20  * 
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.
32  * 
33  */
34
35 #ifndef _IPV4_RSMBL_H_
36 #define _IPV4_RSMBL_H_
37
38 /**
39  * @file
40  * IPv4 reassemble
41  *
42  * Implementation of IPv4 reassemble.
43  *
44  */
45
46 enum {
47         LAST_FRAG_IDX,
48         FIRST_FRAG_IDX,
49         MIN_FRAG_NUM,
50         MAX_FRAG_NUM = 4,
51 };
52
53 struct ipv4_frag {
54         uint16_t ofs;
55         uint16_t len;
56         struct rte_mbuf *mb;
57 };
58
59 /*
60  * Use <src addr, dst_addr, id> to uniquely indetify fragmented datagram.
61  */
62 struct ipv4_frag_key {
63         uint64_t  src_dst;
64         uint32_t  id;
65 };
66
67 #define IPV4_FRAG_KEY_INVALIDATE(k)     ((k)->src_dst = 0)
68 #define IPV4_FRAG_KEY_EMPTY(k)          ((k)->src_dst == 0)
69
70 #define IPV4_FRAG_KEY_CMP(k1, k2)       \
71         (((k1)->src_dst ^ (k2)->src_dst) | ((k1)->id ^ (k2)->id))
72
73
74 /*
75  * Fragmented packet to reassemble.
76  * First two entries in the frags[] array are for the last and first fragments.
77  */
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;
87
88
89 struct ipv4_frag_death_row {
90         uint32_t cnt;
91         struct rte_mbuf *row[MAX_PKT_BURST * (MAX_FRAG_NUM + 1)];
92 };
93
94 #define IPV4_FRAG_MBUF2DR(dr, mb)       ((dr)->row[(dr)->cnt++] = (mb))
95
96 /* logging macros. */
97
98 #ifdef IPV4_FRAG_DEBUG
99 #define IPV4_FRAG_LOG(lvl, fmt, args...)        RTE_LOG(lvl, USER1, fmt, ##args)
100 #else
101 #define IPV4_FRAG_LOG(lvl, fmt, args...)        do {} while(0)
102 #endif /* IPV4_FRAG_DEBUG */
103
104
105 static inline void
106 ipv4_frag_reset(struct ipv4_frag_pkt *fp, uint64_t tms)
107 {
108         static const struct ipv4_frag zero_frag = {
109                 .ofs = 0,
110                 .len = 0,
111                 .mb = NULL,
112         };
113
114         fp->start = tms;
115         fp->total_size = UINT32_MAX;
116         fp->frag_size = 0;
117         fp->last_idx = MIN_FRAG_NUM;
118         fp->frags[LAST_FRAG_IDX] = zero_frag;
119         fp->frags[FIRST_FRAG_IDX] = zero_frag;
120 }
121
122 static inline void
123 ipv4_frag_free(struct ipv4_frag_pkt *fp, struct ipv4_frag_death_row *dr)
124 {
125         uint32_t i, k;
126
127         k = dr->cnt;
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;
132                 }
133         }
134
135         fp->last_idx = 0;
136         dr->cnt = k;
137 }
138
139 static inline void
140 ipv4_frag_free_death_row(struct ipv4_frag_death_row *dr, uint32_t prefetch)
141 {
142         uint32_t i, k, n;
143
144         k = RTE_MIN(prefetch, dr->cnt);
145         n = dr->cnt;
146
147         for (i = 0; i != k; i++) 
148                 rte_prefetch0(dr->row[i]);
149
150         for (i = 0; i != n - k; i++) {
151                 rte_prefetch0(dr->row[i + k]);
152                 rte_pktmbuf_free(dr->row[i]);
153         }
154
155         for (; i != n; i++)
156                 rte_pktmbuf_free(dr->row[i]);
157
158         dr->cnt = 0;
159 }
160
161 /*
162  * Helper function.
163  * Takes 2 mbufs that represents two framents of the same packet and
164  * chains them into one mbuf.
165  */
166 static inline void
167 ipv4_frag_chain(struct rte_mbuf *mn, struct rte_mbuf *mp)
168 {
169         struct rte_mbuf *ms;
170
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));
174                                 
175         /* chain two fragments. */
176         ms = rte_pktmbuf_lastseg(mn);
177         ms->pkt.next = mp;
178
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;
182                                         
183         /* reset pkt_len and nb_segs for chained fragment. */
184         mp->pkt.pkt_len = mp->pkt.data_len;
185         mp->pkt.nb_segs = 1;
186 }
187
188 /*
189  * Reassemble fragments into one packet.
190  */
191 static inline struct rte_mbuf *
192 ipv4_frag_reassemble(const struct ipv4_frag_pkt *fp)
193 {
194         struct ipv4_hdr *ip_hdr;
195         struct rte_mbuf *m, *prev;
196         uint32_t i, n, ofs, first_len;
197
198         first_len = fp->frags[FIRST_FRAG_IDX].len;
199         n = fp->last_idx - 1;
200
201         /*start from the last fragment. */
202         m = fp->frags[LAST_FRAG_IDX].mb;
203         ofs = fp->frags[LAST_FRAG_IDX].ofs;
204
205         while (ofs != first_len) {
206
207                 prev = m;
208
209                 for (i = n; i != FIRST_FRAG_IDX && ofs != first_len; i--) {
210
211                         /* previous fragment found. */
212                         if(fp->frags[i].ofs + fp->frags[i].len == ofs) {
213
214                                 ipv4_frag_chain(fp->frags[i].mb, m);
215
216                                 /* update our last fragment and offset. */
217                                 m = fp->frags[i].mb;
218                                 ofs = fp->frags[i].ofs;
219                         }
220                 }
221
222                 /* error - hole in the packet. */
223                 if (m == prev) {
224                         return (NULL);
225                 }
226         }
227
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;
231
232         /* update mbuf fields for reassembled packet. */
233         m->ol_flags |= PKT_TX_IP_CKSUM;
234
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);
238
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;
244
245         return (m);
246 }
247
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)
251 {
252         uint32_t idx;
253
254         fp->frag_size += len;
255
256         /* this is the first fragment. */
257         if (ofs == 0) {
258                 idx = (fp->frags[FIRST_FRAG_IDX].mb == NULL) ?
259                         FIRST_FRAG_IDX : UINT32_MAX;
260
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;
266
267         /* this is the intermediate fragment. */
268         } else if ((idx = fp->last_idx) <
269                 sizeof (fp->frags) / sizeof (fp->frags[0])) {
270                 fp->last_idx++;
271         }
272
273         /*
274          * errorneous packet: either exceeed max allowed number of fragments,
275          * or duplicate first/last fragment encountered.
276          */
277         if (idx >= sizeof (fp->frags) / sizeof (fp->frags[0])) {
278
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",
285                         __func__, __LINE__,
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);
292
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);
297
298                 return (NULL);
299         }
300
301         fp->frags[idx].ofs = ofs;
302         fp->frags[idx].len = len;
303         fp->frags[idx].mb = mb;
304
305         mb = NULL;
306
307         /* not all fragments are collected yet. */
308         if (likely (fp->frag_size < fp->total_size)) {
309                 return (mb);
310
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);
315         }
316
317         /* errorenous set of fragments. */
318         if (mb == NULL) {
319
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",
326                         __func__, __LINE__,
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);
333
334                 /* free associated resources. */
335                 ipv4_frag_free(fp, dr);
336         }
337
338         /* we are done with that entry, invalidate it. */
339         IPV4_FRAG_KEY_INVALIDATE(&fp->key);
340         return (mb);
341 }
342
343 #include "ipv4_frag_tbl.h"
344
345 /*
346  * Process new mbuf with fragment of IPV4 packet.
347  * Incoming mbuf should have it's l2_len/l3_len fields setuped correclty.
348  * @param tbl
349  *   Table where to lookup/add the fragmented packet.
350  * @param mb
351  *   Incoming mbuf with IPV4 fragment.
352  * @param tms
353  *   Fragment arrival timestamp.
354  * @param ip_hdr
355  *   Pointer to the IPV4 header inside the fragment.
356  * @param ip_ofs
357  *   Fragment's offset (as extracted from the header).
358  * @param ip_flag
359  *   Fragment's MF flag.
360  * @return  
361  *   Pointer to mbuf for reassebled packet, or NULL if:
362  *   - an error occured.
363  *   - not all fragments of the packet are collected yet.
364  */
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)
369 {
370         struct ipv4_frag_pkt *fp;
371         struct ipv4_frag_key key;
372         const uint64_t *psd;
373         uint16_t ip_len;
374
375         psd = (uint64_t *)&ip_hdr->src_addr;
376         key.src_dst = psd[0];
377         key.id = ip_hdr->packet_id;
378
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);
382
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",
388                 __func__, __LINE__,
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,
391                 tbl->use_entries);
392
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);
396                 return (NULL);
397         }
398
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",
403                 __func__, __LINE__,
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);
407                 
408
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);
412
413         IPV4_FRAG_LOG(DEBUG, "%s:%d:\n"
414                 "mbuf: %p\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);
422
423         return (mb);
424 }
425
426 #endif /* _IPV4_RSMBL_H_ */