ipsec_mbuf.c revision 1.19 1 /* $NetBSD: ipsec_mbuf.c,v 1.19 2018/02/14 14:19:53 maxv Exp $ */
2 /*-
3 * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: /repoman/r/ncvs/src/sys/netipsec/ipsec_mbuf.c,v 1.5.2.2 2003/03/28 20:32:53 sam Exp $
28 */
29
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: ipsec_mbuf.c,v 1.19 2018/02/14 14:19:53 maxv Exp $");
32
33 /*
34 * IPsec-specific mbuf routines.
35 */
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/mbuf.h>
40 #include <sys/socket.h>
41
42 #include <net/route.h>
43 #include <netinet/in.h>
44
45 #include <netipsec/ipsec.h>
46 #include <netipsec/ipsec_var.h>
47 #include <netipsec/ipsec_private.h>
48
49 /*
50 * Create a writable copy of the mbuf chain. While doing this
51 * we compact the chain with a goal of producing a chain with
52 * at most two mbufs. The second mbuf in this chain is likely
53 * to be a cluster. The primary purpose of this work is to create
54 * a writable packet for encryption, compression, etc. The
55 * secondary goal is to linearize the data so the data can be
56 * passed to crypto hardware in the most efficient manner possible.
57 */
58 struct mbuf *
59 m_clone(struct mbuf *m0)
60 {
61 struct mbuf *m, *mprev;
62 struct mbuf *n, *mfirst, *mlast;
63 int len, off;
64
65 KASSERT(m0 != NULL);
66
67 mprev = NULL;
68 for (m = m0; m != NULL; m = mprev->m_next) {
69 /*
70 * Regular mbufs are ignored unless there's a cluster
71 * in front of it that we can use to coalesce. We do
72 * the latter mainly so later clusters can be coalesced
73 * also w/o having to handle them specially (i.e. convert
74 * mbuf+cluster -> cluster). This optimization is heavily
75 * influenced by the assumption that we're running over
76 * Ethernet where MCLBYTES is large enough that the max
77 * packet size will permit lots of coalescing into a
78 * single cluster. This in turn permits efficient
79 * crypto operations, especially when using hardware.
80 */
81 if ((m->m_flags & M_EXT) == 0) {
82 if (mprev && (mprev->m_flags & M_EXT) &&
83 m->m_len <= M_TRAILINGSPACE(mprev)) {
84 /* XXX: this ignores mbuf types */
85 memcpy(mtod(mprev, char *) + mprev->m_len,
86 mtod(m, char *), m->m_len);
87 mprev->m_len += m->m_len;
88 mprev->m_next = m->m_next; /* unlink from chain */
89 m_free(m); /* reclaim mbuf */
90 IPSEC_STATINC(IPSEC_STAT_MBCOALESCED);
91 } else {
92 mprev = m;
93 }
94 continue;
95 }
96 /*
97 * Writable mbufs are left alone (for now). Note
98 * that for 4.x systems it's not possible to identify
99 * whether or not mbufs with external buffers are
100 * writable unless they use clusters.
101 */
102 if (M_EXT_WRITABLE(m)) {
103 mprev = m;
104 continue;
105 }
106
107 /*
108 * Not writable, replace with a copy or coalesce with
109 * the previous mbuf if possible (since we have to copy
110 * it anyway, we try to reduce the number of mbufs and
111 * clusters so that future work is easier).
112 */
113 KASSERTMSG(m->m_flags & M_EXT, "m_flags 0x%x", m->m_flags);
114 /* NB: we only coalesce into a cluster or larger */
115 if (mprev != NULL && (mprev->m_flags & M_EXT) &&
116 m->m_len <= M_TRAILINGSPACE(mprev)) {
117 /* XXX: this ignores mbuf types */
118 memcpy(mtod(mprev, char *) + mprev->m_len,
119 mtod(m, char *), m->m_len);
120 mprev->m_len += m->m_len;
121 mprev->m_next = m->m_next; /* unlink from chain */
122 m_free(m); /* reclaim mbuf */
123 IPSEC_STATINC(IPSEC_STAT_CLCOALESCED);
124 continue;
125 }
126
127 /*
128 * Allocate new space to hold the copy...
129 */
130 /* XXX why can M_PKTHDR be set past the first mbuf? */
131 if (mprev == NULL && (m->m_flags & M_PKTHDR)) {
132 /*
133 * NB: if a packet header is present we must
134 * allocate the mbuf separately from any cluster
135 * because M_MOVE_PKTHDR will smash the data
136 * pointer and drop the M_EXT marker.
137 */
138 MGETHDR(n, M_DONTWAIT, m->m_type);
139 if (n == NULL) {
140 m_freem(m0);
141 return (NULL);
142 }
143 M_MOVE_PKTHDR(n, m);
144 MCLGET(n, M_DONTWAIT);
145 if ((n->m_flags & M_EXT) == 0) {
146 m_free(n);
147 m_freem(m0);
148 return (NULL);
149 }
150 } else {
151 n = m_getcl(M_DONTWAIT, m->m_type, m->m_flags);
152 if (n == NULL) {
153 m_freem(m0);
154 return (NULL);
155 }
156 }
157 /*
158 * ... and copy the data. We deal with jumbo mbufs
159 * (i.e. m_len > MCLBYTES) by splitting them into
160 * clusters. We could just malloc a buffer and make
161 * it external but too many device drivers don't know
162 * how to break up the non-contiguous memory when
163 * doing DMA.
164 */
165 len = m->m_len;
166 off = 0;
167 mfirst = n;
168 mlast = NULL;
169 for (;;) {
170 int cc = min(len, MCLBYTES);
171 memcpy(mtod(n, char *), mtod(m, char *) + off, cc);
172 n->m_len = cc;
173 if (mlast != NULL)
174 mlast->m_next = n;
175 mlast = n;
176 IPSEC_STATINC(IPSEC_STAT_CLCOPIED);
177
178 len -= cc;
179 if (len <= 0)
180 break;
181 off += cc;
182
183 n = m_getcl(M_DONTWAIT, m->m_type, m->m_flags);
184 if (n == NULL) {
185 m_freem(mfirst);
186 m_freem(m0);
187 return (NULL);
188 }
189 }
190 n->m_next = m->m_next;
191 if (mprev == NULL)
192 m0 = mfirst; /* new head of chain */
193 else
194 mprev->m_next = mfirst; /* replace old mbuf */
195 m_free(m); /* release old mbuf */
196 mprev = mfirst;
197 }
198 return (m0);
199 }
200
201 /*
202 * Make space for a new header of length hlen at skip bytes
203 * into the packet. When doing this we allocate new mbufs only
204 * when absolutely necessary. The mbuf where the new header
205 * is to go is returned together with an offset into the mbuf.
206 * If NULL is returned then the mbuf chain may have been modified;
207 * the caller is assumed to always free the chain.
208 */
209 struct mbuf *
210 m_makespace(struct mbuf *m0, int skip, int hlen, int *off)
211 {
212 struct mbuf *m;
213 unsigned remain;
214
215 KASSERT(m0 != NULL);
216 KASSERTMSG(hlen < MHLEN, "hlen too big: %u", hlen);
217
218 for (m = m0; m && skip > m->m_len; m = m->m_next)
219 skip -= m->m_len;
220 if (m == NULL)
221 return (NULL);
222 /*
223 * At this point skip is the offset into the mbuf m
224 * where the new header should be placed. Figure out
225 * if there's space to insert the new header. If so,
226 * and copying the remainder makese sense then do so.
227 * Otherwise insert a new mbuf in the chain, splitting
228 * the contents of m as needed.
229 */
230 remain = m->m_len - skip; /* data to move */
231 if (hlen > M_TRAILINGSPACE(m)) {
232 struct mbuf *n0, *n, **np;
233 int todo, len, done, alloc;
234
235 n0 = NULL;
236 np = &n0;
237 alloc = 0;
238 done = 0;
239 todo = remain;
240 while (todo > 0) {
241 if (todo > MHLEN) {
242 n = m_getcl(M_DONTWAIT, m->m_type, 0);
243 len = MCLBYTES;
244 }
245 else {
246 n = m_get(M_DONTWAIT, m->m_type);
247 len = MHLEN;
248 }
249 if (n == NULL) {
250 m_freem(n0);
251 return NULL;
252 }
253 *np = n;
254 np = &n->m_next;
255 alloc++;
256 len = min(todo, len);
257 memcpy(n->m_data, mtod(m, char *) + skip + done, len);
258 n->m_len = len;
259 done += len;
260 todo -= len;
261 }
262
263 if (hlen <= M_TRAILINGSPACE(m) + remain) {
264 m->m_len = skip + hlen;
265 *off = skip;
266 if (n0 != NULL) {
267 *np = m->m_next;
268 m->m_next = n0;
269 }
270 }
271 else {
272 n = m_get(M_DONTWAIT, m->m_type);
273 if (n == NULL) {
274 m_freem(n0);
275 return NULL;
276 }
277 alloc++;
278
279 if ((n->m_next = n0) == NULL)
280 np = &n->m_next;
281 n0 = n;
282
283 *np = m->m_next;
284 m->m_next = n0;
285
286 n->m_len = hlen;
287 m->m_len = skip;
288
289 m = n; /* header is at front ... */
290 *off = 0; /* ... of new mbuf */
291 }
292
293 IPSEC_STATADD(IPSEC_STAT_MBINSERTED, alloc);
294 } else {
295 /*
296 * Copy the remainder to the back of the mbuf
297 * so there's space to write the new header.
298 */
299 /* XXX can this be memcpy? does it handle overlap? */
300 memmove(mtod(m, char *) + skip + hlen,
301 mtod(m, char *) + skip, remain);
302 m->m_len += hlen;
303 *off = skip;
304 }
305 m0->m_pkthdr.len += hlen; /* adjust packet length */
306 return m;
307 }
308
309 /*
310 * m_pad(m, n) pads <m> with <n> bytes at the end. The packet header
311 * length is updated, and a pointer to the first byte of the padding
312 * (which is guaranteed to be all in one mbuf) is returned.
313 */
314 void *
315 m_pad(struct mbuf *m, int n)
316 {
317 register struct mbuf *m0, *m1;
318 register int len, pad;
319 void *retval;
320
321 if (n <= 0) { /* No stupid arguments. */
322 IPSECLOG(LOG_DEBUG, "pad length invalid (%d)\n", n);
323 m_freem(m);
324 return NULL;
325 }
326
327 len = m->m_pkthdr.len;
328 pad = n;
329 m0 = m;
330
331 while (m0->m_len < len) {
332 KASSERTMSG(m0->m_next != NULL,
333 "m0 null, len %u m_len %u", len, m0->m_len);/*XXX*/
334 len -= m0->m_len;
335 m0 = m0->m_next;
336 }
337
338 if (m0->m_len != len) {
339 IPSECLOG(LOG_DEBUG,
340 "length mismatch (should be %d instead of %d)\n",
341 m->m_pkthdr.len, m->m_pkthdr.len + m0->m_len - len);
342
343 m_freem(m);
344 return NULL;
345 }
346
347 /* Check for zero-length trailing mbufs, and find the last one. */
348 for (m1 = m0; m1->m_next; m1 = m1->m_next) {
349 if (m1->m_next->m_len != 0) {
350 IPSECLOG(LOG_DEBUG,
351 "length mismatch (should be %d instead of %d)\n",
352 m->m_pkthdr.len,
353 m->m_pkthdr.len + m1->m_next->m_len);
354
355 m_freem(m);
356 return NULL;
357 }
358
359 m0 = m1->m_next;
360 }
361
362 if (pad > M_TRAILINGSPACE(m0)) {
363 /* Add an mbuf to the chain. */
364 MGET(m1, M_DONTWAIT, MT_DATA);
365 if (m1 == 0) {
366 m_freem(m0);
367 IPSECLOG(LOG_DEBUG, "unable to get extra mbuf\n");
368 return NULL;
369 }
370
371 m0->m_next = m1;
372 m0 = m1;
373 m0->m_len = 0;
374 }
375
376 retval = m0->m_data + m0->m_len;
377 m0->m_len += pad;
378 m->m_pkthdr.len += pad;
379
380 return retval;
381 }
382
383 /*
384 * Remove hlen data at offset skip in the packet. This is used by
385 * the protocols strip protocol headers and associated data (e.g. IV,
386 * authenticator) on input.
387 */
388 int
389 m_striphdr(struct mbuf *m, int skip, int hlen)
390 {
391 struct mbuf *m1;
392 int roff;
393
394 /* Find beginning of header */
395 m1 = m_getptr(m, skip, &roff);
396 if (m1 == NULL)
397 return (EINVAL);
398
399 /* Remove the header and associated data from the mbuf. */
400 if (roff == 0) {
401 /* The header was at the beginning of the mbuf */
402 IPSEC_STATINC(IPSEC_STAT_INPUT_FRONT);
403 m_adj(m1, hlen);
404 if ((m1->m_flags & M_PKTHDR) == 0)
405 m->m_pkthdr.len -= hlen;
406 } else if (roff + hlen >= m1->m_len) {
407 struct mbuf *mo;
408
409 /*
410 * Part or all of the header is at the end of this mbuf,
411 * so first let's remove the remainder of the header from
412 * the beginning of the remainder of the mbuf chain, if any.
413 */
414 IPSEC_STATINC(IPSEC_STAT_INPUT_END);
415 if (roff + hlen > m1->m_len) {
416 /* Adjust the next mbuf by the remainder */
417 m_adj(m1->m_next, roff + hlen - m1->m_len);
418
419 /* The second mbuf is guaranteed not to have a pkthdr... */
420 m->m_pkthdr.len -= (roff + hlen - m1->m_len);
421 }
422
423 /* Now, let's unlink the mbuf chain for a second...*/
424 mo = m1->m_next;
425 m1->m_next = NULL;
426
427 /* ...and trim the end of the first part of the chain...sick */
428 m_adj(m1, -(m1->m_len - roff));
429 if ((m1->m_flags & M_PKTHDR) == 0)
430 m->m_pkthdr.len -= (m1->m_len - roff);
431
432 /* Finally, let's relink */
433 m1->m_next = mo;
434 } else {
435 /*
436 * The header lies in the "middle" of the mbuf; copy
437 * the remainder of the mbuf down over the header.
438 */
439 IPSEC_STATINC(IPSEC_STAT_INPUT_MIDDLE);
440 memmove(mtod(m1, u_char *) + roff,
441 mtod(m1, u_char *) + roff + hlen,
442 m1->m_len - (roff + hlen));
443 m1->m_len -= hlen;
444 m->m_pkthdr.len -= hlen;
445 }
446 return (0);
447 }
448