1*b1305a6dSmaxv /* $NetBSD: ipsec_mbuf.c,v 1.30 2018/12/22 13:11:38 maxv Exp $ */
216a6b570Smaxv
316a6b570Smaxv /*
4c82e44fcSthorpej * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
5c82e44fcSthorpej * All rights reserved.
6c82e44fcSthorpej *
7c82e44fcSthorpej * Redistribution and use in source and binary forms, with or without
8c82e44fcSthorpej * modification, are permitted provided that the following conditions
9c82e44fcSthorpej * are met:
10c82e44fcSthorpej * 1. Redistributions of source code must retain the above copyright
11c82e44fcSthorpej * notice, this list of conditions and the following disclaimer.
12c82e44fcSthorpej * 2. Redistributions in binary form must reproduce the above copyright
13c82e44fcSthorpej * notice, this list of conditions and the following disclaimer in the
14c82e44fcSthorpej * documentation and/or other materials provided with the distribution.
15c82e44fcSthorpej *
16c82e44fcSthorpej * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17c82e44fcSthorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18c82e44fcSthorpej * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19c82e44fcSthorpej * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20c82e44fcSthorpej * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21c82e44fcSthorpej * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22c82e44fcSthorpej * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23c82e44fcSthorpej * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24c82e44fcSthorpej * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25c82e44fcSthorpej * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26c82e44fcSthorpej * SUCH DAMAGE.
27c82e44fcSthorpej *
28e2c8a664Smaxv * $FreeBSD: sys/netipsec/ipsec_mbuf.c,v 1.5.2.2 2003/03/28 20:32:53 sam Exp $
29c82e44fcSthorpej */
3074029031Sjonathan
3174029031Sjonathan #include <sys/cdefs.h>
32*b1305a6dSmaxv __KERNEL_RCSID(0, "$NetBSD: ipsec_mbuf.c,v 1.30 2018/12/22 13:11:38 maxv Exp $");
3374029031Sjonathan
3474029031Sjonathan /*
3574029031Sjonathan * IPsec-specific mbuf routines.
3674029031Sjonathan */
3774029031Sjonathan
3874029031Sjonathan #include <sys/param.h>
3974029031Sjonathan #include <sys/systm.h>
4074029031Sjonathan #include <sys/mbuf.h>
4174029031Sjonathan
4274029031Sjonathan #include <netipsec/ipsec.h>
4385b3ba5bSjonathan #include <netipsec/ipsec_var.h>
44caf49ea5Sthorpej #include <netipsec/ipsec_private.h>
4574029031Sjonathan
4674029031Sjonathan /*
4774029031Sjonathan * Create a writable copy of the mbuf chain. While doing this
4874029031Sjonathan * we compact the chain with a goal of producing a chain with
4974029031Sjonathan * at most two mbufs. The second mbuf in this chain is likely
5074029031Sjonathan * to be a cluster. The primary purpose of this work is to create
5174029031Sjonathan * a writable packet for encryption, compression, etc. The
5274029031Sjonathan * secondary goal is to linearize the data so the data can be
5374029031Sjonathan * passed to crypto hardware in the most efficient manner possible.
5474029031Sjonathan */
5574029031Sjonathan struct mbuf *
m_clone(struct mbuf * m0)5674029031Sjonathan m_clone(struct mbuf *m0)
5774029031Sjonathan {
5874029031Sjonathan struct mbuf *m, *mprev;
5974029031Sjonathan struct mbuf *n, *mfirst, *mlast;
6074029031Sjonathan int len, off;
6174029031Sjonathan
622620e166Sozaki-r KASSERT(m0 != NULL);
6374029031Sjonathan
6474029031Sjonathan mprev = NULL;
6574029031Sjonathan for (m = m0; m != NULL; m = mprev->m_next) {
6674029031Sjonathan /*
6774029031Sjonathan * Regular mbufs are ignored unless there's a cluster
6848df35d3Smaxv * in front of it that we can use to coalesce.
6974029031Sjonathan */
7074029031Sjonathan if ((m->m_flags & M_EXT) == 0) {
7174029031Sjonathan if (mprev && (mprev->m_flags & M_EXT) &&
7274029031Sjonathan m->m_len <= M_TRAILINGSPACE(mprev)) {
73c252f603Sdegroote memcpy(mtod(mprev, char *) + mprev->m_len,
74c252f603Sdegroote mtod(m, char *), m->m_len);
7574029031Sjonathan mprev->m_len += m->m_len;
7648df35d3Smaxv mprev->m_next = m_free(m);
77caf49ea5Sthorpej IPSEC_STATINC(IPSEC_STAT_MBCOALESCED);
7874029031Sjonathan } else {
7974029031Sjonathan mprev = m;
8074029031Sjonathan }
8174029031Sjonathan continue;
8274029031Sjonathan }
830ad30c0fSmaxv
8474029031Sjonathan /*
8548df35d3Smaxv * Writable mbufs are left alone.
8674029031Sjonathan */
87f193022cSmaxv if (!M_READONLY(m)) {
8874029031Sjonathan mprev = m;
8974029031Sjonathan continue;
9074029031Sjonathan }
9174029031Sjonathan
9274029031Sjonathan /*
9374029031Sjonathan * Not writable, replace with a copy or coalesce with
9474029031Sjonathan * the previous mbuf if possible (since we have to copy
9574029031Sjonathan * it anyway, we try to reduce the number of mbufs and
9674029031Sjonathan * clusters so that future work is easier).
9774029031Sjonathan */
9848df35d3Smaxv
9948df35d3Smaxv /* We only coalesce into a cluster. */
10074029031Sjonathan if (mprev != NULL && (mprev->m_flags & M_EXT) &&
10174029031Sjonathan m->m_len <= M_TRAILINGSPACE(mprev)) {
102c252f603Sdegroote memcpy(mtod(mprev, char *) + mprev->m_len,
103c252f603Sdegroote mtod(m, char *), m->m_len);
10474029031Sjonathan mprev->m_len += m->m_len;
10548df35d3Smaxv mprev->m_next = m_free(m);
106caf49ea5Sthorpej IPSEC_STATINC(IPSEC_STAT_CLCOALESCED);
10774029031Sjonathan continue;
10874029031Sjonathan }
10974029031Sjonathan
11074029031Sjonathan /*
11174029031Sjonathan * Allocate new space to hold the copy...
11274029031Sjonathan */
11374029031Sjonathan if (mprev == NULL && (m->m_flags & M_PKTHDR)) {
11474029031Sjonathan MGETHDR(n, M_DONTWAIT, m->m_type);
11574029031Sjonathan if (n == NULL) {
11674029031Sjonathan m_freem(m0);
1170ad30c0fSmaxv return NULL;
11874029031Sjonathan }
119*b1305a6dSmaxv m_move_pkthdr(n, m);
12074029031Sjonathan MCLGET(n, M_DONTWAIT);
12174029031Sjonathan if ((n->m_flags & M_EXT) == 0) {
12274029031Sjonathan m_free(n);
12374029031Sjonathan m_freem(m0);
1240ad30c0fSmaxv return NULL;
12574029031Sjonathan }
12674029031Sjonathan } else {
12774029031Sjonathan n = m_getcl(M_DONTWAIT, m->m_type, m->m_flags);
12874029031Sjonathan if (n == NULL) {
12974029031Sjonathan m_freem(m0);
1300ad30c0fSmaxv return NULL;
13174029031Sjonathan }
13274029031Sjonathan }
1330ad30c0fSmaxv
13474029031Sjonathan /*
13574029031Sjonathan * ... and copy the data. We deal with jumbo mbufs
13674029031Sjonathan * (i.e. m_len > MCLBYTES) by splitting them into
13774029031Sjonathan * clusters. We could just malloc a buffer and make
13874029031Sjonathan * it external but too many device drivers don't know
13974029031Sjonathan * how to break up the non-contiguous memory when
14074029031Sjonathan * doing DMA.
14174029031Sjonathan */
14274029031Sjonathan len = m->m_len;
14374029031Sjonathan off = 0;
14474029031Sjonathan mfirst = n;
14574029031Sjonathan mlast = NULL;
14674029031Sjonathan for (;;) {
147d1579b2dSriastradh const int cc = uimin(len, MCLBYTES);
148c252f603Sdegroote memcpy(mtod(n, char *), mtod(m, char *) + off, cc);
14974029031Sjonathan n->m_len = cc;
15074029031Sjonathan if (mlast != NULL)
15174029031Sjonathan mlast->m_next = n;
15274029031Sjonathan mlast = n;
153caf49ea5Sthorpej IPSEC_STATINC(IPSEC_STAT_CLCOPIED);
15474029031Sjonathan
15574029031Sjonathan len -= cc;
15674029031Sjonathan if (len <= 0)
15774029031Sjonathan break;
15874029031Sjonathan off += cc;
15974029031Sjonathan
16074029031Sjonathan n = m_getcl(M_DONTWAIT, m->m_type, m->m_flags);
16174029031Sjonathan if (n == NULL) {
16274029031Sjonathan m_freem(mfirst);
16374029031Sjonathan m_freem(m0);
1640ad30c0fSmaxv return NULL;
16574029031Sjonathan }
16674029031Sjonathan }
16774029031Sjonathan n->m_next = m->m_next;
16874029031Sjonathan if (mprev == NULL)
16974029031Sjonathan m0 = mfirst; /* new head of chain */
17074029031Sjonathan else
17174029031Sjonathan mprev->m_next = mfirst; /* replace old mbuf */
17274029031Sjonathan m_free(m); /* release old mbuf */
17374029031Sjonathan mprev = mfirst;
17474029031Sjonathan }
1750ad30c0fSmaxv
1760ad30c0fSmaxv return m0;
17774029031Sjonathan }
17874029031Sjonathan
17974029031Sjonathan /*
18074029031Sjonathan * Make space for a new header of length hlen at skip bytes
18174029031Sjonathan * into the packet. When doing this we allocate new mbufs only
18274029031Sjonathan * when absolutely necessary. The mbuf where the new header
18374029031Sjonathan * is to go is returned together with an offset into the mbuf.
18474029031Sjonathan * If NULL is returned then the mbuf chain may have been modified;
18574029031Sjonathan * the caller is assumed to always free the chain.
18674029031Sjonathan */
18774029031Sjonathan struct mbuf *
m_makespace(struct mbuf * m0,int skip,int hlen,int * off)18874029031Sjonathan m_makespace(struct mbuf *m0, int skip, int hlen, int *off)
18974029031Sjonathan {
19074029031Sjonathan struct mbuf *m;
19174029031Sjonathan unsigned remain;
19274029031Sjonathan
1932620e166Sozaki-r KASSERT(m0 != NULL);
1947ddcc8beSmaxv KASSERT(m0->m_flags & M_PKTHDR);
1952620e166Sozaki-r KASSERTMSG(hlen < MHLEN, "hlen too big: %u", hlen);
19674029031Sjonathan
19774029031Sjonathan for (m = m0; m && skip > m->m_len; m = m->m_next)
19874029031Sjonathan skip -= m->m_len;
19974029031Sjonathan if (m == NULL)
2000ad30c0fSmaxv return NULL;
2010ad30c0fSmaxv
20274029031Sjonathan /*
20374029031Sjonathan * At this point skip is the offset into the mbuf m
20474029031Sjonathan * where the new header should be placed. Figure out
20574029031Sjonathan * if there's space to insert the new header. If so,
20616a6b570Smaxv * and copying the remainder makes sense then do so.
20774029031Sjonathan * Otherwise insert a new mbuf in the chain, splitting
20874029031Sjonathan * the contents of m as needed.
20974029031Sjonathan */
21074029031Sjonathan remain = m->m_len - skip; /* data to move */
21174029031Sjonathan if (hlen > M_TRAILINGSPACE(m)) {
21282a49e73Sseanb struct mbuf *n0, *n, **np;
21382a49e73Sseanb int todo, len, done, alloc;
21474029031Sjonathan
21582a49e73Sseanb n0 = NULL;
21682a49e73Sseanb np = &n0;
21782a49e73Sseanb alloc = 0;
21882a49e73Sseanb done = 0;
21982a49e73Sseanb todo = remain;
22082a49e73Sseanb while (todo > 0) {
22182a49e73Sseanb if (todo > MHLEN) {
22282a49e73Sseanb n = m_getcl(M_DONTWAIT, m->m_type, 0);
22382a49e73Sseanb len = MCLBYTES;
22416a6b570Smaxv } else {
22582a49e73Sseanb n = m_get(M_DONTWAIT, m->m_type);
22682a49e73Sseanb len = MHLEN;
22782a49e73Sseanb }
22882a49e73Sseanb if (n == NULL) {
22982a49e73Sseanb m_freem(n0);
23082a49e73Sseanb return NULL;
23182a49e73Sseanb }
23282a49e73Sseanb *np = n;
23382a49e73Sseanb np = &n->m_next;
23482a49e73Sseanb alloc++;
235d1579b2dSriastradh len = uimin(todo, len);
23682a49e73Sseanb memcpy(n->m_data, mtod(m, char *) + skip + done, len);
23782a49e73Sseanb n->m_len = len;
23882a49e73Sseanb done += len;
23982a49e73Sseanb todo -= len;
24082a49e73Sseanb }
24182a49e73Sseanb
24274029031Sjonathan if (hlen <= M_TRAILINGSPACE(m) + remain) {
24374029031Sjonathan m->m_len = skip + hlen;
24474029031Sjonathan *off = skip;
24582a49e73Sseanb if (n0 != NULL) {
24682a49e73Sseanb *np = m->m_next;
24782a49e73Sseanb m->m_next = n0;
24874029031Sjonathan }
24916a6b570Smaxv } else {
25082a49e73Sseanb n = m_get(M_DONTWAIT, m->m_type);
25182a49e73Sseanb if (n == NULL) {
25282a49e73Sseanb m_freem(n0);
25382a49e73Sseanb return NULL;
25482a49e73Sseanb }
25582a49e73Sseanb alloc++;
25682a49e73Sseanb
25782a49e73Sseanb if ((n->m_next = n0) == NULL)
25882a49e73Sseanb np = &n->m_next;
25982a49e73Sseanb n0 = n;
26082a49e73Sseanb
26182a49e73Sseanb *np = m->m_next;
26282a49e73Sseanb m->m_next = n0;
26382a49e73Sseanb
26482a49e73Sseanb n->m_len = hlen;
26582a49e73Sseanb m->m_len = skip;
26682a49e73Sseanb
26774029031Sjonathan m = n; /* header is at front ... */
26874029031Sjonathan *off = 0; /* ... of new mbuf */
26974029031Sjonathan }
27082a49e73Sseanb
271caf49ea5Sthorpej IPSEC_STATADD(IPSEC_STAT_MBINSERTED, alloc);
27274029031Sjonathan } else {
27374029031Sjonathan /*
27474029031Sjonathan * Copy the remainder to the back of the mbuf
27574029031Sjonathan * so there's space to write the new header.
27674029031Sjonathan */
27774029031Sjonathan /* XXX can this be memcpy? does it handle overlap? */
27829fe0b74Smaxv memmove(mtod(m, char *) + skip + hlen,
27929fe0b74Smaxv mtod(m, char *) + skip, remain);
28074029031Sjonathan m->m_len += hlen;
28174029031Sjonathan *off = skip;
28274029031Sjonathan }
2830ad30c0fSmaxv
28474029031Sjonathan m0->m_pkthdr.len += hlen; /* adjust packet length */
28574029031Sjonathan return m;
28674029031Sjonathan }
28774029031Sjonathan
28874029031Sjonathan /*
28974029031Sjonathan * m_pad(m, n) pads <m> with <n> bytes at the end. The packet header
29074029031Sjonathan * length is updated, and a pointer to the first byte of the padding
29174029031Sjonathan * (which is guaranteed to be all in one mbuf) is returned.
29274029031Sjonathan */
29353524e44Schristos void *
m_pad(struct mbuf * m,int n)29474029031Sjonathan m_pad(struct mbuf *m, int n)
29574029031Sjonathan {
29674029031Sjonathan register struct mbuf *m0, *m1;
29774029031Sjonathan register int len, pad;
29853524e44Schristos void *retval;
29974029031Sjonathan
300f2110e85Smaxv if (__predict_false(n > MLEN)) {
301f2110e85Smaxv panic("%s: %d > MLEN", __func__, n);
30274029031Sjonathan }
3037ddcc8beSmaxv KASSERT(m->m_flags & M_PKTHDR);
30474029031Sjonathan
30574029031Sjonathan len = m->m_pkthdr.len;
30674029031Sjonathan pad = n;
30774029031Sjonathan m0 = m;
30874029031Sjonathan
30974029031Sjonathan while (m0->m_len < len) {
3102620e166Sozaki-r KASSERTMSG(m0->m_next != NULL,
311f2110e85Smaxv "m0 null, len %u m_len %u", len, m0->m_len);
31274029031Sjonathan len -= m0->m_len;
31374029031Sjonathan m0 = m0->m_next;
31474029031Sjonathan }
31574029031Sjonathan
31674029031Sjonathan if (m0->m_len != len) {
317290dc492Sozaki-r IPSECLOG(LOG_DEBUG,
318290dc492Sozaki-r "length mismatch (should be %d instead of %d)\n",
319290dc492Sozaki-r m->m_pkthdr.len, m->m_pkthdr.len + m0->m_len - len);
32074029031Sjonathan m_freem(m);
32174029031Sjonathan return NULL;
32274029031Sjonathan }
32374029031Sjonathan
32474029031Sjonathan /* Check for zero-length trailing mbufs, and find the last one. */
32574029031Sjonathan for (m1 = m0; m1->m_next; m1 = m1->m_next) {
32674029031Sjonathan if (m1->m_next->m_len != 0) {
327290dc492Sozaki-r IPSECLOG(LOG_DEBUG,
328290dc492Sozaki-r "length mismatch (should be %d instead of %d)\n",
32974029031Sjonathan m->m_pkthdr.len,
330290dc492Sozaki-r m->m_pkthdr.len + m1->m_next->m_len);
33174029031Sjonathan m_freem(m);
33274029031Sjonathan return NULL;
33374029031Sjonathan }
33474029031Sjonathan
33574029031Sjonathan m0 = m1->m_next;
33674029031Sjonathan }
33774029031Sjonathan
33874029031Sjonathan if (pad > M_TRAILINGSPACE(m0)) {
33974029031Sjonathan /* Add an mbuf to the chain. */
34074029031Sjonathan MGET(m1, M_DONTWAIT, MT_DATA);
341f2110e85Smaxv if (m1 == NULL) {
342f2110e85Smaxv m_freem(m);
343290dc492Sozaki-r IPSECLOG(LOG_DEBUG, "unable to get extra mbuf\n");
34474029031Sjonathan return NULL;
34574029031Sjonathan }
34674029031Sjonathan
34774029031Sjonathan m0->m_next = m1;
34874029031Sjonathan m0 = m1;
34974029031Sjonathan m0->m_len = 0;
35074029031Sjonathan }
35174029031Sjonathan
35274029031Sjonathan retval = m0->m_data + m0->m_len;
35374029031Sjonathan m0->m_len += pad;
35474029031Sjonathan m->m_pkthdr.len += pad;
35574029031Sjonathan
35674029031Sjonathan return retval;
35774029031Sjonathan }
35874029031Sjonathan
35974029031Sjonathan /*
36074029031Sjonathan * Remove hlen data at offset skip in the packet. This is used by
36174029031Sjonathan * the protocols strip protocol headers and associated data (e.g. IV,
36274029031Sjonathan * authenticator) on input.
36374029031Sjonathan */
36474029031Sjonathan int
m_striphdr(struct mbuf * m,int skip,int hlen)36574029031Sjonathan m_striphdr(struct mbuf *m, int skip, int hlen)
36674029031Sjonathan {
36774029031Sjonathan struct mbuf *m1;
36874029031Sjonathan int roff;
36974029031Sjonathan
3707ddcc8beSmaxv KASSERT(m->m_flags & M_PKTHDR);
3717ddcc8beSmaxv
37274029031Sjonathan /* Find beginning of header */
37374029031Sjonathan m1 = m_getptr(m, skip, &roff);
37474029031Sjonathan if (m1 == NULL)
3750ad30c0fSmaxv return EINVAL;
37674029031Sjonathan
37774029031Sjonathan /* Remove the header and associated data from the mbuf. */
37874029031Sjonathan if (roff == 0) {
37974029031Sjonathan /* The header was at the beginning of the mbuf */
380caf49ea5Sthorpej IPSEC_STATINC(IPSEC_STAT_INPUT_FRONT);
38174029031Sjonathan m_adj(m1, hlen);
382cc059e55Smaxv if (m1 != m)
38374029031Sjonathan m->m_pkthdr.len -= hlen;
38474029031Sjonathan } else if (roff + hlen >= m1->m_len) {
38574029031Sjonathan struct mbuf *mo;
38603bb22c3Smaxv int adjlen;
38774029031Sjonathan
38874029031Sjonathan /*
38974029031Sjonathan * Part or all of the header is at the end of this mbuf,
39074029031Sjonathan * so first let's remove the remainder of the header from
39174029031Sjonathan * the beginning of the remainder of the mbuf chain, if any.
39274029031Sjonathan */
393caf49ea5Sthorpej IPSEC_STATINC(IPSEC_STAT_INPUT_END);
39474029031Sjonathan if (roff + hlen > m1->m_len) {
39503bb22c3Smaxv adjlen = roff + hlen - m1->m_len;
39603bb22c3Smaxv
39774029031Sjonathan /* Adjust the next mbuf by the remainder */
39803bb22c3Smaxv m_adj(m1->m_next, adjlen);
39974029031Sjonathan
40074029031Sjonathan /* The second mbuf is guaranteed not to have a pkthdr... */
40103bb22c3Smaxv m->m_pkthdr.len -= adjlen;
40274029031Sjonathan }
40374029031Sjonathan
40474029031Sjonathan /* Now, let's unlink the mbuf chain for a second...*/
40574029031Sjonathan mo = m1->m_next;
40674029031Sjonathan m1->m_next = NULL;
40774029031Sjonathan
40874029031Sjonathan /* ...and trim the end of the first part of the chain...sick */
40903bb22c3Smaxv adjlen = m1->m_len - roff;
41003bb22c3Smaxv m_adj(m1, -adjlen);
411cc059e55Smaxv if (m1 != m)
41203bb22c3Smaxv m->m_pkthdr.len -= adjlen;
41374029031Sjonathan
41474029031Sjonathan /* Finally, let's relink */
41574029031Sjonathan m1->m_next = mo;
41674029031Sjonathan } else {
41774029031Sjonathan /*
41874029031Sjonathan * The header lies in the "middle" of the mbuf; copy
41974029031Sjonathan * the remainder of the mbuf down over the header.
42074029031Sjonathan */
421caf49ea5Sthorpej IPSEC_STATINC(IPSEC_STAT_INPUT_MIDDLE);
42229fe0b74Smaxv memmove(mtod(m1, u_char *) + roff,
42329fe0b74Smaxv mtod(m1, u_char *) + roff + hlen,
42474029031Sjonathan m1->m_len - (roff + hlen));
42574029031Sjonathan m1->m_len -= hlen;
42674029031Sjonathan m->m_pkthdr.len -= hlen;
42774029031Sjonathan }
4280ad30c0fSmaxv
4290ad30c0fSmaxv return 0;
43074029031Sjonathan }
431