doc: whitespace changes in licenses
[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 #ifndef _IPV4_RSMBL_H_
35 #define _IPV4_RSMBL_H_
36
37 /**
38  * @file
39  * IPv4 reassemble
40  *
41  * Implementation of IPv4 reassemble.
42  *
43  */
44
45 enum {
46         LAST_FRAG_IDX,
47         FIRST_FRAG_IDX,
48         MIN_FRAG_NUM,
49         MAX_FRAG_NUM = 4,
50 };
51
52 struct ipv4_frag {
53         uint16_t ofs;
54         uint16_t len;
55         struct rte_mbuf *mb;
56 };
57
58 /*
59  * Use <src addr, dst_addr, id> to uniquely indetify fragmented datagram.
60  */
61 struct ipv4_frag_key {
62         uint64_t  src_dst;
63         uint32_t  id;
64 };
65
66 #define IPV4_FRAG_KEY_INVALIDATE(k)     ((k)->src_dst = 0)
67 #define IPV4_FRAG_KEY_EMPTY(k)          ((k)->src_dst == 0)
68
69 #define IPV4_FRAG_KEY_CMP(k1, k2)       \
70         (((k1)->src_dst ^ (k2)->src_dst) | ((k1)->id ^ (k2)->id))
71
72
73 /*
74  * Fragmented packet to reassemble.
75  * First two entries in the frags[] array are for the last and first fragments.
76  */
77 struct ipv4_frag_pkt {
78         TAILQ_ENTRY(ipv4_frag_pkt) lru;   /* LRU list */
79         struct ipv4_frag_key key;
80         uint64_t             start;       /* creation timestamp */
81         uint32_t             total_size;  /* expected reassembled size */
82         uint32_t             frag_size;   /* size of fragments received */
83         uint32_t             last_idx;    /* index of next entry to fill */
84         struct ipv4_frag     frags[MAX_FRAG_NUM];
85 } __rte_cache_aligned;
86
87
88 struct ipv4_frag_death_row {
89         uint32_t cnt;
90         struct rte_mbuf *row[MAX_PKT_BURST * (MAX_FRAG_NUM + 1)];
91 };
92
93 #define IPV4_FRAG_MBUF2DR(dr, mb)       ((dr)->row[(dr)->cnt++] = (mb))
94
95 /* logging macros. */
96
97 #ifdef IPV4_FRAG_DEBUG
98 #define IPV4_FRAG_LOG(lvl, fmt, args...)        RTE_LOG(lvl, USER1, fmt, ##args)
99 #else
100 #define IPV4_FRAG_LOG(lvl, fmt, args...)        do {} while(0)
101 #endif /* IPV4_FRAG_DEBUG */
102
103
104 static inline void
105 ipv4_frag_reset(struct ipv4_frag_pkt *fp, uint64_t tms)
106 {
107         static const struct ipv4_frag zero_frag = {
108                 .ofs = 0,
109                 .len = 0,
110                 .mb = NULL,
111         };
112
113         fp->start = tms;
114         fp->total_size = UINT32_MAX;
115         fp->frag_size = 0;
116         fp->last_idx = MIN_FRAG_NUM;
117         fp->frags[LAST_FRAG_IDX] = zero_frag;
118         fp->frags[FIRST_FRAG_IDX] = zero_frag;
119 }
120
121 static inline void
122 ipv4_frag_free(struct ipv4_frag_pkt *fp, struct ipv4_frag_death_row *dr)
123 {
124         uint32_t i, k;
125
126         k = dr->cnt;
127         for (i = 0; i != fp->last_idx; i++) {
128                 if (fp->frags[i].mb != NULL) {
129                         dr->row[k++] = fp->frags[i].mb;
130                         fp->frags[i].mb = NULL;
131                 }
132         }
133
134         fp->last_idx = 0;
135         dr->cnt = k;
136 }
137
138 static inline void
139 ipv4_frag_free_death_row(struct ipv4_frag_death_row *dr, uint32_t prefetch)
140 {
141         uint32_t i, k, n;
142
143         k = RTE_MIN(prefetch, dr->cnt);
144         n = dr->cnt;
145
146         for (i = 0; i != k; i++) 
147                 rte_prefetch0(dr->row[i]);
148
149         for (i = 0; i != n - k; i++) {
150                 rte_prefetch0(dr->row[i + k]);
151                 rte_pktmbuf_free(dr->row[i]);
152         }
153
154         for (; i != n; i++)
155                 rte_pktmbuf_free(dr->row[i]);
156
157         dr->cnt = 0;
158 }
159
160 /*
161  * Helper function.
162  * Takes 2 mbufs that represents two framents of the same packet and
163  * chains them into one mbuf.
164  */
165 static inline void
166 ipv4_frag_chain(struct rte_mbuf *mn, struct rte_mbuf *mp)
167 {
168         struct rte_mbuf *ms;
169
170         /* adjust start of the last fragment data. */
171         rte_pktmbuf_adj(mp, (uint16_t)(mp->pkt.vlan_macip.f.l2_len +
172                 mp->pkt.vlan_macip.f.l3_len));
173                                 
174         /* chain two fragments. */
175         ms = rte_pktmbuf_lastseg(mn);
176         ms->pkt.next = mp;
177
178         /* accumulate number of segments and total length. */
179         mn->pkt.nb_segs = (uint8_t)(mn->pkt.nb_segs + mp->pkt.nb_segs);
180         mn->pkt.pkt_len += mp->pkt.pkt_len;
181                                         
182         /* reset pkt_len and nb_segs for chained fragment. */
183         mp->pkt.pkt_len = mp->pkt.data_len;
184         mp->pkt.nb_segs = 1;
185 }
186
187 /*
188  * Reassemble fragments into one packet.
189  */
190 static inline struct rte_mbuf *
191 ipv4_frag_reassemble(const struct ipv4_frag_pkt *fp)
192 {
193         struct ipv4_hdr *ip_hdr;
194         struct rte_mbuf *m, *prev;
195         uint32_t i, n, ofs, first_len;
196
197         first_len = fp->frags[FIRST_FRAG_IDX].len;
198         n = fp->last_idx - 1;
199
200         /*start from the last fragment. */
201         m = fp->frags[LAST_FRAG_IDX].mb;
202         ofs = fp->frags[LAST_FRAG_IDX].ofs;
203
204         while (ofs != first_len) {
205
206                 prev = m;
207
208                 for (i = n; i != FIRST_FRAG_IDX && ofs != first_len; i--) {
209
210                         /* previous fragment found. */
211                         if(fp->frags[i].ofs + fp->frags[i].len == ofs) {
212
213                                 ipv4_frag_chain(fp->frags[i].mb, m);
214
215                                 /* update our last fragment and offset. */
216                                 m = fp->frags[i].mb;
217                                 ofs = fp->frags[i].ofs;
218                         }
219                 }
220
221                 /* error - hole in the packet. */
222                 if (m == prev) {
223                         return (NULL);
224                 }
225         }
226
227         /* chain with the first fragment. */
228         ipv4_frag_chain(fp->frags[FIRST_FRAG_IDX].mb, m);
229         m = fp->frags[FIRST_FRAG_IDX].mb;
230
231         /* update mbuf fields for reassembled packet. */
232         m->ol_flags |= PKT_TX_IP_CKSUM;
233
234         /* update ipv4 header for the reassmebled packet */
235         ip_hdr = (struct ipv4_hdr*)(rte_pktmbuf_mtod(m, uint8_t *) +
236                 m->pkt.vlan_macip.f.l2_len);
237
238         ip_hdr->total_length = rte_cpu_to_be_16((uint16_t)(fp->total_size +
239                 m->pkt.vlan_macip.f.l3_len));
240         ip_hdr->fragment_offset = (uint16_t)(ip_hdr->fragment_offset &
241                 rte_cpu_to_be_16(IPV4_HDR_DF_FLAG));
242         ip_hdr->hdr_checksum = 0;
243
244         return (m);
245 }
246
247 static inline struct rte_mbuf *
248 ipv4_frag_process(struct ipv4_frag_pkt *fp, struct ipv4_frag_death_row *dr,
249         struct rte_mbuf *mb, uint16_t ofs, uint16_t len, uint16_t more_frags)
250 {
251         uint32_t idx;
252
253         fp->frag_size += len;
254
255         /* this is the first fragment. */
256         if (ofs == 0) {
257                 idx = (fp->frags[FIRST_FRAG_IDX].mb == NULL) ?
258                         FIRST_FRAG_IDX : UINT32_MAX;
259
260         /* this is the last fragment. */
261         } else if (more_frags == 0) {
262                 fp->total_size = ofs + len;
263                 idx = (fp->frags[LAST_FRAG_IDX].mb == NULL) ?
264                         LAST_FRAG_IDX : UINT32_MAX;
265
266         /* this is the intermediate fragment. */
267         } else if ((idx = fp->last_idx) <
268                 sizeof (fp->frags) / sizeof (fp->frags[0])) {
269                 fp->last_idx++;
270         }
271
272         /*
273          * errorneous packet: either exceeed max allowed number of fragments,
274          * or duplicate first/last fragment encountered.
275          */
276         if (idx >= sizeof (fp->frags) / sizeof (fp->frags[0])) {
277
278                 /* report an error. */
279                 IPV4_FRAG_LOG(DEBUG, "%s:%d invalid fragmented packet:\n"
280                         "ipv4_frag_pkt: %p, key: <%" PRIx64 ", %#x>, "
281                         "total_size: %u, frag_size: %u, last_idx: %u\n"
282                         "first fragment: ofs: %u, len: %u\n"
283                         "last fragment: ofs: %u, len: %u\n\n",
284                         __func__, __LINE__,
285                         fp, fp->key.src_dst, fp->key.id,
286                         fp->total_size, fp->frag_size, fp->last_idx,
287                         fp->frags[FIRST_FRAG_IDX].ofs,
288                         fp->frags[FIRST_FRAG_IDX].len,
289                         fp->frags[LAST_FRAG_IDX].ofs,
290                         fp->frags[LAST_FRAG_IDX].len);
291
292                 /* free all fragments, invalidate the entry. */
293                 ipv4_frag_free(fp, dr);
294                 IPV4_FRAG_KEY_INVALIDATE(&fp->key);
295                 IPV4_FRAG_MBUF2DR(dr, mb);
296
297                 return (NULL);
298         }
299
300         fp->frags[idx].ofs = ofs;
301         fp->frags[idx].len = len;
302         fp->frags[idx].mb = mb;
303
304         mb = NULL;
305
306         /* not all fragments are collected yet. */
307         if (likely (fp->frag_size < fp->total_size)) {
308                 return (mb);
309
310         /* if we collected all fragments, then try to reassemble. */
311         } else if (fp->frag_size == fp->total_size &&
312                         fp->frags[FIRST_FRAG_IDX].mb != NULL) {
313                 mb = ipv4_frag_reassemble(fp);
314         }
315
316         /* errorenous set of fragments. */
317         if (mb == NULL) {
318
319                 /* report an error. */
320                 IPV4_FRAG_LOG(DEBUG, "%s:%d invalid fragmented packet:\n"
321                         "ipv4_frag_pkt: %p, key: <%" PRIx64 ", %#x>, "
322                         "total_size: %u, frag_size: %u, last_idx: %u\n"
323                         "first fragment: ofs: %u, len: %u\n"
324                         "last fragment: ofs: %u, len: %u\n\n",
325                         __func__, __LINE__,
326                         fp, fp->key.src_dst, fp->key.id,
327                         fp->total_size, fp->frag_size, fp->last_idx,
328                         fp->frags[FIRST_FRAG_IDX].ofs,
329                         fp->frags[FIRST_FRAG_IDX].len,
330                         fp->frags[LAST_FRAG_IDX].ofs,
331                         fp->frags[LAST_FRAG_IDX].len);
332
333                 /* free associated resources. */
334                 ipv4_frag_free(fp, dr);
335         }
336
337         /* we are done with that entry, invalidate it. */
338         IPV4_FRAG_KEY_INVALIDATE(&fp->key);
339         return (mb);
340 }
341
342 #include "ipv4_frag_tbl.h"
343
344 /*
345  * Process new mbuf with fragment of IPV4 packet.
346  * Incoming mbuf should have it's l2_len/l3_len fields setuped correclty.
347  * @param tbl
348  *   Table where to lookup/add the fragmented packet.
349  * @param mb
350  *   Incoming mbuf with IPV4 fragment.
351  * @param tms
352  *   Fragment arrival timestamp.
353  * @param ip_hdr
354  *   Pointer to the IPV4 header inside the fragment.
355  * @param ip_ofs
356  *   Fragment's offset (as extracted from the header).
357  * @param ip_flag
358  *   Fragment's MF flag.
359  * @return  
360  *   Pointer to mbuf for reassebled packet, or NULL if:
361  *   - an error occured.
362  *   - not all fragments of the packet are collected yet.
363  */
364 static inline struct rte_mbuf *
365 ipv4_frag_mbuf(struct ipv4_frag_tbl *tbl, struct ipv4_frag_death_row *dr,
366         struct rte_mbuf *mb, uint64_t tms, struct ipv4_hdr *ip_hdr,
367         uint16_t ip_ofs, uint16_t ip_flag)
368 {
369         struct ipv4_frag_pkt *fp;
370         struct ipv4_frag_key key;
371         const uint64_t *psd;
372         uint16_t ip_len;
373
374         psd = (uint64_t *)&ip_hdr->src_addr;
375         key.src_dst = psd[0];
376         key.id = ip_hdr->packet_id;
377
378         ip_ofs *= IPV4_HDR_OFFSET_UNITS;
379         ip_len = (uint16_t)(rte_be_to_cpu_16(ip_hdr->total_length) -
380                 mb->pkt.vlan_macip.f.l3_len);
381
382         IPV4_FRAG_LOG(DEBUG, "%s:%d:\n"
383                 "mbuf: %p, tms: %" PRIu64
384                 ", key: <%" PRIx64 ", %#x>, ofs: %u, len: %u, flags: %#x\n"
385                 "tbl: %p, max_cycles: %" PRIu64 ", entry_mask: %#x, "
386                 "max_entries: %u, use_entries: %u\n\n",
387                 __func__, __LINE__,
388                 mb, tms, key.src_dst, key.id, ip_ofs, ip_len, ip_flag,
389                 tbl, tbl->max_cycles, tbl->entry_mask, tbl->max_entries,
390                 tbl->use_entries);
391
392         /* try to find/add entry into the fragment's table. */
393         if ((fp = ipv4_frag_find(tbl, dr, &key, tms)) == NULL) {
394                 IPV4_FRAG_MBUF2DR(dr, mb);
395                 return (NULL);
396         }
397
398         IPV4_FRAG_LOG(DEBUG, "%s:%d:\n"
399                 "tbl: %p, max_entries: %u, use_entries: %u\n"
400                 "ipv4_frag_pkt: %p, key: <%" PRIx64 ", %#x>, start: %" PRIu64
401                 ", total_size: %u, frag_size: %u, last_idx: %u\n\n",
402                 __func__, __LINE__,
403                 tbl, tbl->max_entries, tbl->use_entries,
404                 fp, fp->key.src_dst, fp->key.id, fp->start,
405                 fp->total_size, fp->frag_size, fp->last_idx);
406                 
407
408         /* process the fragmented packet. */
409         mb = ipv4_frag_process(fp, dr, mb, ip_ofs, ip_len, ip_flag);
410         ipv4_frag_inuse(tbl, fp);
411
412         IPV4_FRAG_LOG(DEBUG, "%s:%d:\n"
413                 "mbuf: %p\n"
414                 "tbl: %p, max_entries: %u, use_entries: %u\n"
415                 "ipv4_frag_pkt: %p, key: <%" PRIx64 ", %#x>, start: %" PRIu64
416                 ", total_size: %u, frag_size: %u, last_idx: %u\n\n",
417                 __func__, __LINE__, mb,
418                 tbl, tbl->max_entries, tbl->use_entries,
419                 fp, fp->key.src_dst, fp->key.id, fp->start,
420                 fp->total_size, fp->frag_size, fp->last_idx);
421
422         return (mb);
423 }
424
425 #endif /* _IPV4_RSMBL_H_ */