]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*- |
2 | * BSD LICENSE | |
3 | * | |
4 | * Copyright(c) 2010-2014 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 | #include <stddef.h> | |
35 | #include <errno.h> | |
36 | ||
37 | #include <rte_memcpy.h> | |
38 | #include <rte_mempool.h> | |
39 | #include <rte_debug.h> | |
40 | ||
41 | #include "ip_frag_common.h" | |
42 | ||
43 | /* Fragment Offset */ | |
44 | #define IPV4_HDR_DF_SHIFT 14 | |
45 | #define IPV4_HDR_MF_SHIFT 13 | |
46 | #define IPV4_HDR_FO_SHIFT 3 | |
47 | ||
48 | #define IPV4_HDR_DF_MASK (1 << IPV4_HDR_DF_SHIFT) | |
49 | #define IPV4_HDR_MF_MASK (1 << IPV4_HDR_MF_SHIFT) | |
50 | ||
51 | #define IPV4_HDR_FO_MASK ((1 << IPV4_HDR_FO_SHIFT) - 1) | |
52 | ||
53 | static inline void __fill_ipv4hdr_frag(struct ipv4_hdr *dst, | |
54 | const struct ipv4_hdr *src, uint16_t len, uint16_t fofs, | |
55 | uint16_t dofs, uint32_t mf) | |
56 | { | |
57 | rte_memcpy(dst, src, sizeof(*dst)); | |
58 | fofs = (uint16_t)(fofs + (dofs >> IPV4_HDR_FO_SHIFT)); | |
59 | fofs = (uint16_t)(fofs | mf << IPV4_HDR_MF_SHIFT); | |
60 | dst->fragment_offset = rte_cpu_to_be_16(fofs); | |
61 | dst->total_length = rte_cpu_to_be_16(len); | |
62 | dst->hdr_checksum = 0; | |
63 | } | |
64 | ||
65 | static inline void __free_fragments(struct rte_mbuf *mb[], uint32_t num) | |
66 | { | |
67 | uint32_t i; | |
68 | for (i = 0; i != num; i++) | |
69 | rte_pktmbuf_free(mb[i]); | |
70 | } | |
71 | ||
72 | /** | |
73 | * IPv4 fragmentation. | |
74 | * | |
75 | * This function implements the fragmentation of IPv4 packets. | |
76 | * | |
77 | * @param pkt_in | |
78 | * The input packet. | |
79 | * @param pkts_out | |
80 | * Array storing the output fragments. | |
81 | * @param mtu_size | |
82 | * Size in bytes of the Maximum Transfer Unit (MTU) for the outgoing IPv4 | |
83 | * datagrams. This value includes the size of the IPv4 header. | |
84 | * @param pool_direct | |
85 | * MBUF pool used for allocating direct buffers for the output fragments. | |
86 | * @param pool_indirect | |
87 | * MBUF pool used for allocating indirect buffers for the output fragments. | |
88 | * @return | |
89 | * Upon successful completion - number of output fragments placed | |
90 | * in the pkts_out array. | |
91 | * Otherwise - (-1) * <errno>. | |
92 | */ | |
93 | int32_t | |
94 | rte_ipv4_fragment_packet(struct rte_mbuf *pkt_in, | |
95 | struct rte_mbuf **pkts_out, | |
96 | uint16_t nb_pkts_out, | |
97 | uint16_t mtu_size, | |
98 | struct rte_mempool *pool_direct, | |
99 | struct rte_mempool *pool_indirect) | |
100 | { | |
101 | struct rte_mbuf *in_seg = NULL; | |
102 | struct ipv4_hdr *in_hdr; | |
103 | uint32_t out_pkt_pos, in_seg_data_pos; | |
104 | uint32_t more_in_segs; | |
105 | uint16_t fragment_offset, flag_offset, frag_size; | |
106 | ||
107 | frag_size = (uint16_t)(mtu_size - sizeof(struct ipv4_hdr)); | |
108 | ||
109 | /* Fragment size should be a multiply of 8. */ | |
110 | RTE_ASSERT((frag_size & IPV4_HDR_FO_MASK) == 0); | |
111 | ||
112 | in_hdr = rte_pktmbuf_mtod(pkt_in, struct ipv4_hdr *); | |
113 | flag_offset = rte_cpu_to_be_16(in_hdr->fragment_offset); | |
114 | ||
115 | /* If Don't Fragment flag is set */ | |
116 | if (unlikely ((flag_offset & IPV4_HDR_DF_MASK) != 0)) | |
117 | return -ENOTSUP; | |
118 | ||
119 | /* Check that pkts_out is big enough to hold all fragments */ | |
120 | if (unlikely(frag_size * nb_pkts_out < | |
121 | (uint16_t)(pkt_in->pkt_len - sizeof (struct ipv4_hdr)))) | |
122 | return -EINVAL; | |
123 | ||
124 | in_seg = pkt_in; | |
125 | in_seg_data_pos = sizeof(struct ipv4_hdr); | |
126 | out_pkt_pos = 0; | |
127 | fragment_offset = 0; | |
128 | ||
129 | more_in_segs = 1; | |
130 | while (likely(more_in_segs)) { | |
131 | struct rte_mbuf *out_pkt = NULL, *out_seg_prev = NULL; | |
132 | uint32_t more_out_segs; | |
133 | struct ipv4_hdr *out_hdr; | |
134 | ||
135 | /* Allocate direct buffer */ | |
136 | out_pkt = rte_pktmbuf_alloc(pool_direct); | |
137 | if (unlikely(out_pkt == NULL)) { | |
138 | __free_fragments(pkts_out, out_pkt_pos); | |
139 | return -ENOMEM; | |
140 | } | |
141 | ||
142 | /* Reserve space for the IP header that will be built later */ | |
143 | out_pkt->data_len = sizeof(struct ipv4_hdr); | |
144 | out_pkt->pkt_len = sizeof(struct ipv4_hdr); | |
145 | ||
146 | out_seg_prev = out_pkt; | |
147 | more_out_segs = 1; | |
148 | while (likely(more_out_segs && more_in_segs)) { | |
149 | struct rte_mbuf *out_seg = NULL; | |
150 | uint32_t len; | |
151 | ||
152 | /* Allocate indirect buffer */ | |
153 | out_seg = rte_pktmbuf_alloc(pool_indirect); | |
154 | if (unlikely(out_seg == NULL)) { | |
155 | rte_pktmbuf_free(out_pkt); | |
156 | __free_fragments(pkts_out, out_pkt_pos); | |
157 | return -ENOMEM; | |
158 | } | |
159 | out_seg_prev->next = out_seg; | |
160 | out_seg_prev = out_seg; | |
161 | ||
162 | /* Prepare indirect buffer */ | |
163 | rte_pktmbuf_attach(out_seg, in_seg); | |
164 | len = mtu_size - out_pkt->pkt_len; | |
165 | if (len > (in_seg->data_len - in_seg_data_pos)) { | |
166 | len = in_seg->data_len - in_seg_data_pos; | |
167 | } | |
168 | out_seg->data_off = in_seg->data_off + in_seg_data_pos; | |
169 | out_seg->data_len = (uint16_t)len; | |
170 | out_pkt->pkt_len = (uint16_t)(len + | |
171 | out_pkt->pkt_len); | |
172 | out_pkt->nb_segs += 1; | |
173 | in_seg_data_pos += len; | |
174 | ||
175 | /* Current output packet (i.e. fragment) done ? */ | |
176 | if (unlikely(out_pkt->pkt_len >= mtu_size)) | |
177 | more_out_segs = 0; | |
178 | ||
179 | /* Current input segment done ? */ | |
180 | if (unlikely(in_seg_data_pos == in_seg->data_len)) { | |
181 | in_seg = in_seg->next; | |
182 | in_seg_data_pos = 0; | |
183 | ||
184 | if (unlikely(in_seg == NULL)) | |
185 | more_in_segs = 0; | |
186 | } | |
187 | } | |
188 | ||
189 | /* Build the IP header */ | |
190 | ||
191 | out_hdr = rte_pktmbuf_mtod(out_pkt, struct ipv4_hdr *); | |
192 | ||
193 | __fill_ipv4hdr_frag(out_hdr, in_hdr, | |
194 | (uint16_t)out_pkt->pkt_len, | |
195 | flag_offset, fragment_offset, more_in_segs); | |
196 | ||
197 | fragment_offset = (uint16_t)(fragment_offset + | |
198 | out_pkt->pkt_len - sizeof(struct ipv4_hdr)); | |
199 | ||
200 | out_pkt->ol_flags |= PKT_TX_IP_CKSUM; | |
201 | out_pkt->l3_len = sizeof(struct ipv4_hdr); | |
202 | ||
203 | /* Write the fragment to the output list */ | |
204 | pkts_out[out_pkt_pos] = out_pkt; | |
205 | out_pkt_pos ++; | |
206 | } | |
207 | ||
208 | return out_pkt_pos; | |
209 | } |