xref: /freebsd/sys/netipsec/ipsec_mbuf.c (revision 076ad2f8)
1 /*-
2  * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 /*
30  * IPsec-specific mbuf routines.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/malloc.h>
36 #include <sys/mbuf.h>
37 #include <sys/socket.h>
38 
39 #include <net/vnet.h>
40 #include <netinet/in.h>
41 #include <netipsec/ipsec.h>
42 
43 /*
44  * Make space for a new header of length hlen at skip bytes
45  * into the packet.  When doing this we allocate new mbufs only
46  * when absolutely necessary.  The mbuf where the new header
47  * is to go is returned together with an offset into the mbuf.
48  * If NULL is returned then the mbuf chain may have been modified;
49  * the caller is assumed to always free the chain.
50  */
51 struct mbuf *
52 m_makespace(struct mbuf *m0, int skip, int hlen, int *off)
53 {
54 	struct mbuf *m;
55 	unsigned remain;
56 
57 	IPSEC_ASSERT(m0 != NULL, ("null mbuf"));
58 	IPSEC_ASSERT(hlen < MHLEN, ("hlen too big: %u", hlen));
59 
60 	for (m = m0; m && skip > m->m_len; m = m->m_next)
61 		skip -= m->m_len;
62 	if (m == NULL)
63 		return (NULL);
64 	/*
65 	 * At this point skip is the offset into the mbuf m
66 	 * where the new header should be placed.  Figure out
67 	 * if there's space to insert the new header.  If so,
68 	 * and copying the remainder makes sense then do so.
69 	 * Otherwise insert a new mbuf in the chain, splitting
70 	 * the contents of m as needed.
71 	 */
72 	remain = m->m_len - skip;		/* data to move */
73 	if (remain > skip &&
74 	    hlen + max_linkhdr < M_LEADINGSPACE(m)) {
75 		/*
76 		 * mbuf has enough free space at the beginning.
77 		 * XXX: which operation is the most heavy - copying of
78 		 *	possible several hundred of bytes or allocation
79 		 *	of new mbuf? We can remove max_linkhdr check
80 		 *	here, but it is possible that this will lead
81 		 *	to allocation of new mbuf in Layer 2 code.
82 		 */
83 		m->m_data -= hlen;
84 		bcopy(mtodo(m, hlen), mtod(m, caddr_t), skip);
85 		m->m_len += hlen;
86 		*off = skip;
87 	} else if (hlen > M_TRAILINGSPACE(m)) {
88 		struct mbuf *n0, *n, **np;
89 		int todo, len, done, alloc;
90 
91 		n0 = NULL;
92 		np = &n0;
93 		alloc = 0;
94 		done = 0;
95 		todo = remain;
96 		while (todo > 0) {
97 			if (todo > MHLEN) {
98 				n = m_getcl(M_NOWAIT, m->m_type, 0);
99 				len = MCLBYTES;
100 			}
101 			else {
102 				n = m_get(M_NOWAIT, m->m_type);
103 				len = MHLEN;
104 			}
105 			if (n == NULL) {
106 				m_freem(n0);
107 				return NULL;
108 			}
109 			*np = n;
110 			np = &n->m_next;
111 			alloc++;
112 			len = min(todo, len);
113 			memcpy(n->m_data, mtod(m, char *) + skip + done, len);
114 			n->m_len = len;
115 			done += len;
116 			todo -= len;
117 		}
118 
119 		if (hlen <= M_TRAILINGSPACE(m) + remain) {
120 			m->m_len = skip + hlen;
121 			*off = skip;
122 			if (n0 != NULL) {
123 				*np = m->m_next;
124 				m->m_next = n0;
125 			}
126 		}
127 		else {
128 			n = m_get(M_NOWAIT, m->m_type);
129 			if (n == NULL) {
130 				m_freem(n0);
131 				return NULL;
132 			}
133 			alloc++;
134 
135 			if ((n->m_next = n0) == NULL)
136 				np = &n->m_next;
137 			n0 = n;
138 
139 			*np = m->m_next;
140 			m->m_next = n0;
141 
142 			n->m_len = hlen;
143 			m->m_len = skip;
144 
145 			m = n;			/* header is at front ... */
146 			*off = 0;		/* ... of new mbuf */
147 		}
148 		IPSECSTAT_INC(ips_mbinserted);
149 	} else {
150 		/*
151 		 * Copy the remainder to the back of the mbuf
152 		 * so there's space to write the new header.
153 		 */
154 		bcopy(mtod(m, caddr_t) + skip,
155 		    mtod(m, caddr_t) + skip + hlen, remain);
156 		m->m_len += hlen;
157 		*off = skip;
158 	}
159 	m0->m_pkthdr.len += hlen;		/* adjust packet length */
160 	return m;
161 }
162 
163 /*
164  * m_pad(m, n) pads <m> with <n> bytes at the end. The packet header
165  * length is updated, and a pointer to the first byte of the padding
166  * (which is guaranteed to be all in one mbuf) is returned.
167  */
168 caddr_t
169 m_pad(struct mbuf *m, int n)
170 {
171 	register struct mbuf *m0, *m1;
172 	register int len, pad;
173 	caddr_t retval;
174 
175 	if (n <= 0) {  /* No stupid arguments. */
176 		DPRINTF(("%s: pad length invalid (%d)\n", __func__, n));
177 		m_freem(m);
178 		return NULL;
179 	}
180 
181 	len = m->m_pkthdr.len;
182 	pad = n;
183 	m0 = m;
184 
185 	while (m0->m_len < len) {
186 		len -= m0->m_len;
187 		m0 = m0->m_next;
188 	}
189 
190 	if (m0->m_len != len) {
191 		DPRINTF(("%s: length mismatch (should be %d instead of %d)\n",
192 			__func__, m->m_pkthdr.len,
193 			m->m_pkthdr.len + m0->m_len - len));
194 
195 		m_freem(m);
196 		return NULL;
197 	}
198 
199 	/* Check for zero-length trailing mbufs, and find the last one. */
200 	for (m1 = m0; m1->m_next; m1 = m1->m_next) {
201 		if (m1->m_next->m_len != 0) {
202 			DPRINTF(("%s: length mismatch (should be %d instead "
203 				"of %d)\n", __func__,
204 				m->m_pkthdr.len,
205 				m->m_pkthdr.len + m1->m_next->m_len));
206 
207 			m_freem(m);
208 			return NULL;
209 		}
210 
211 		m0 = m1->m_next;
212 	}
213 
214 	if (pad > M_TRAILINGSPACE(m0)) {
215 		/* Add an mbuf to the chain. */
216 		MGET(m1, M_NOWAIT, MT_DATA);
217 		if (m1 == NULL) {
218 			m_freem(m0);
219 			DPRINTF(("%s: unable to get extra mbuf\n", __func__));
220 			return NULL;
221 		}
222 
223 		m0->m_next = m1;
224 		m0 = m1;
225 		m0->m_len = 0;
226 	}
227 
228 	retval = m0->m_data + m0->m_len;
229 	m0->m_len += pad;
230 	m->m_pkthdr.len += pad;
231 
232 	return retval;
233 }
234 
235 /*
236  * Remove hlen data at offset skip in the packet.  This is used by
237  * the protocols strip protocol headers and associated data (e.g. IV,
238  * authenticator) on input.
239  */
240 int
241 m_striphdr(struct mbuf *m, int skip, int hlen)
242 {
243 	struct mbuf *m1;
244 	int roff;
245 
246 	/* Find beginning of header */
247 	m1 = m_getptr(m, skip, &roff);
248 	if (m1 == NULL)
249 		return (EINVAL);
250 
251 	/* Remove the header and associated data from the mbuf. */
252 	if (roff == 0) {
253 		/* The header was at the beginning of the mbuf */
254 		IPSECSTAT_INC(ips_input_front);
255 		m_adj(m1, hlen);
256 		if ((m1->m_flags & M_PKTHDR) == 0)
257 			m->m_pkthdr.len -= hlen;
258 	} else if (roff + hlen >= m1->m_len) {
259 		struct mbuf *mo;
260 
261 		/*
262 		 * Part or all of the header is at the end of this mbuf,
263 		 * so first let's remove the remainder of the header from
264 		 * the beginning of the remainder of the mbuf chain, if any.
265 		 */
266 		IPSECSTAT_INC(ips_input_end);
267 		if (roff + hlen > m1->m_len) {
268 			/* Adjust the next mbuf by the remainder */
269 			m_adj(m1->m_next, roff + hlen - m1->m_len);
270 
271 			/* The second mbuf is guaranteed not to have a pkthdr... */
272 			m->m_pkthdr.len -= (roff + hlen - m1->m_len);
273 		}
274 
275 		/* Now, let's unlink the mbuf chain for a second...*/
276 		mo = m1->m_next;
277 		m1->m_next = NULL;
278 
279 		/* ...and trim the end of the first part of the chain...sick */
280 		m_adj(m1, -(m1->m_len - roff));
281 		if ((m1->m_flags & M_PKTHDR) == 0)
282 			m->m_pkthdr.len -= (m1->m_len - roff);
283 
284 		/* Finally, let's relink */
285 		m1->m_next = mo;
286 	} else {
287 		/*
288 		 * The header lies in the "middle" of the mbuf; copy
289 		 * the remainder of the mbuf down over the header.
290 		 */
291 		IPSECSTAT_INC(ips_input_middle);
292 		bcopy(mtod(m1, u_char *) + roff + hlen,
293 		      mtod(m1, u_char *) + roff,
294 		      m1->m_len - (roff + hlen));
295 		m1->m_len -= hlen;
296 		m->m_pkthdr.len -= hlen;
297 	}
298 	return (0);
299 }
300 
301 /*
302  * Diagnostic routine to check mbuf alignment as required by the
303  * crypto device drivers (that use DMA).
304  */
305 void
306 m_checkalignment(const char* where, struct mbuf *m0, int off, int len)
307 {
308 	int roff;
309 	struct mbuf *m = m_getptr(m0, off, &roff);
310 	caddr_t addr;
311 
312 	if (m == NULL)
313 		return;
314 	printf("%s (off %u len %u): ", where, off, len);
315 	addr = mtod(m, caddr_t) + roff;
316 	do {
317 		int mlen;
318 
319 		if (((uintptr_t) addr) & 3) {
320 			printf("addr misaligned %p,", addr);
321 			break;
322 		}
323 		mlen = m->m_len;
324 		if (mlen > len)
325 			mlen = len;
326 		len -= mlen;
327 		if (len && (mlen & 3)) {
328 			printf("len mismatch %u,", mlen);
329 			break;
330 		}
331 		m = m->m_next;
332 		addr = m ? mtod(m, caddr_t) : NULL;
333 	} while (m && len > 0);
334 	for (m = m0; m; m = m->m_next)
335 		printf(" [%p:%u]", mtod(m, caddr_t), m->m_len);
336 	printf("\n");
337 }
338