]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/netipsec/ipsec_mbuf.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / netipsec / ipsec_mbuf.c
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 "opt_param.h"
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/mbuf.h>
38 #include <sys/socket.h>
39
40 #include <net/vnet.h>
41 #include <netinet/in.h>
42 #include <netipsec/ipsec.h>
43
44 /*
45  * Make space for a new header of length hlen at skip bytes
46  * into the packet.  When doing this we allocate new mbufs only
47  * when absolutely necessary.  The mbuf where the new header
48  * is to go is returned together with an offset into the mbuf.
49  * If NULL is returned then the mbuf chain may have been modified;
50  * the caller is assumed to always free the chain.
51  */
52 struct mbuf *
53 m_makespace(struct mbuf *m0, int skip, int hlen, int *off)
54 {
55         struct mbuf *m;
56         unsigned remain;
57
58         IPSEC_ASSERT(m0 != NULL, ("null mbuf"));
59         IPSEC_ASSERT(hlen < MHLEN, ("hlen too big: %u", hlen));
60
61         for (m = m0; m && skip > m->m_len; m = m->m_next)
62                 skip -= m->m_len;
63         if (m == NULL)
64                 return (NULL);
65         /*
66          * At this point skip is the offset into the mbuf m
67          * where the new header should be placed.  Figure out
68          * if there's space to insert the new header.  If so,
69          * and copying the remainder makes sense then do so.
70          * Otherwise insert a new mbuf in the chain, splitting
71          * the contents of m as needed.
72          */
73         remain = m->m_len - skip;               /* data to move */
74         if (hlen > M_TRAILINGSPACE(m)) {
75                 struct mbuf *n0, *n, **np;
76                 int todo, len, done, alloc;
77
78                 n0 = NULL;
79                 np = &n0;
80                 alloc = 0;
81                 done = 0;
82                 todo = remain;
83                 while (todo > 0) {
84                         if (todo > MHLEN) {
85                                 n = m_getcl(M_NOWAIT, m->m_type, 0);
86                                 len = MCLBYTES;
87                         }
88                         else {
89                                 n = m_get(M_NOWAIT, m->m_type);
90                                 len = MHLEN;
91                         }
92                         if (n == NULL) {
93                                 m_freem(n0);
94                                 return NULL;
95                         }
96                         *np = n;
97                         np = &n->m_next;
98                         alloc++;
99                         len = min(todo, len);
100                         memcpy(n->m_data, mtod(m, char *) + skip + done, len);
101                         n->m_len = len;
102                         done += len;
103                         todo -= len;
104                 }
105
106                 if (hlen <= M_TRAILINGSPACE(m) + remain) {
107                         m->m_len = skip + hlen;
108                         *off = skip;
109                         if (n0 != NULL) {
110                                 *np = m->m_next;
111                                 m->m_next = n0;
112                         }
113                 }
114                 else {
115                         n = m_get(M_NOWAIT, m->m_type);
116                         if (n == NULL) {
117                                 m_freem(n0);
118                                 return NULL;
119                         }
120                         alloc++;
121
122                         if ((n->m_next = n0) == NULL)
123                                 np = &n->m_next;
124                         n0 = n;
125
126                         *np = m->m_next;
127                         m->m_next = n0;
128
129                         n->m_len = hlen;
130                         m->m_len = skip;
131
132                         m = n;                  /* header is at front ... */
133                         *off = 0;               /* ... of new mbuf */
134                 }
135                 IPSECSTAT_INC(ips_mbinserted);
136         } else {
137                 /*
138                  * Copy the remainder to the back of the mbuf
139                  * so there's space to write the new header.
140                  */
141                 bcopy(mtod(m, caddr_t) + skip,
142                       mtod(m, caddr_t) + skip + hlen, remain);
143                 m->m_len += hlen;
144                 *off = skip;
145         }
146         m0->m_pkthdr.len += hlen;               /* adjust packet length */
147         return m;
148 }
149
150 /*
151  * m_pad(m, n) pads <m> with <n> bytes at the end. The packet header
152  * length is updated, and a pointer to the first byte of the padding
153  * (which is guaranteed to be all in one mbuf) is returned.
154  */
155 caddr_t
156 m_pad(struct mbuf *m, int n)
157 {
158         register struct mbuf *m0, *m1;
159         register int len, pad;
160         caddr_t retval;
161
162         if (n <= 0) {  /* No stupid arguments. */
163                 DPRINTF(("%s: pad length invalid (%d)\n", __func__, n));
164                 m_freem(m);
165                 return NULL;
166         }
167
168         len = m->m_pkthdr.len;
169         pad = n;
170         m0 = m;
171
172         while (m0->m_len < len) {
173                 len -= m0->m_len;
174                 m0 = m0->m_next;
175         }
176
177         if (m0->m_len != len) {
178                 DPRINTF(("%s: length mismatch (should be %d instead of %d)\n",
179                         __func__, m->m_pkthdr.len,
180                         m->m_pkthdr.len + m0->m_len - len));
181
182                 m_freem(m);
183                 return NULL;
184         }
185
186         /* Check for zero-length trailing mbufs, and find the last one. */
187         for (m1 = m0; m1->m_next; m1 = m1->m_next) {
188                 if (m1->m_next->m_len != 0) {
189                         DPRINTF(("%s: length mismatch (should be %d instead "
190                                 "of %d)\n", __func__,
191                                 m->m_pkthdr.len,
192                                 m->m_pkthdr.len + m1->m_next->m_len));
193
194                         m_freem(m);
195                         return NULL;
196                 }
197
198                 m0 = m1->m_next;
199         }
200
201         if (pad > M_TRAILINGSPACE(m0)) {
202                 /* Add an mbuf to the chain. */
203                 MGET(m1, M_NOWAIT, MT_DATA);
204                 if (m1 == 0) {
205                         m_freem(m0);
206                         DPRINTF(("%s: unable to get extra mbuf\n", __func__));
207                         return NULL;
208                 }
209
210                 m0->m_next = m1;
211                 m0 = m1;
212                 m0->m_len = 0;
213         }
214
215         retval = m0->m_data + m0->m_len;
216         m0->m_len += pad;
217         m->m_pkthdr.len += pad;
218
219         return retval;
220 }
221
222 /*
223  * Remove hlen data at offset skip in the packet.  This is used by
224  * the protocols strip protocol headers and associated data (e.g. IV,
225  * authenticator) on input.
226  */
227 int
228 m_striphdr(struct mbuf *m, int skip, int hlen)
229 {
230         struct mbuf *m1;
231         int roff;
232
233         /* Find beginning of header */
234         m1 = m_getptr(m, skip, &roff);
235         if (m1 == NULL)
236                 return (EINVAL);
237
238         /* Remove the header and associated data from the mbuf. */
239         if (roff == 0) {
240                 /* The header was at the beginning of the mbuf */
241                 IPSECSTAT_INC(ips_input_front);
242                 m_adj(m1, hlen);
243                 if ((m1->m_flags & M_PKTHDR) == 0)
244                         m->m_pkthdr.len -= hlen;
245         } else if (roff + hlen >= m1->m_len) {
246                 struct mbuf *mo;
247
248                 /*
249                  * Part or all of the header is at the end of this mbuf,
250                  * so first let's remove the remainder of the header from
251                  * the beginning of the remainder of the mbuf chain, if any.
252                  */
253                 IPSECSTAT_INC(ips_input_end);
254                 if (roff + hlen > m1->m_len) {
255                         /* Adjust the next mbuf by the remainder */
256                         m_adj(m1->m_next, roff + hlen - m1->m_len);
257
258                         /* The second mbuf is guaranteed not to have a pkthdr... */
259                         m->m_pkthdr.len -= (roff + hlen - m1->m_len);
260                 }
261
262                 /* Now, let's unlink the mbuf chain for a second...*/
263                 mo = m1->m_next;
264                 m1->m_next = NULL;
265
266                 /* ...and trim the end of the first part of the chain...sick */
267                 m_adj(m1, -(m1->m_len - roff));
268                 if ((m1->m_flags & M_PKTHDR) == 0)
269                         m->m_pkthdr.len -= (m1->m_len - roff);
270
271                 /* Finally, let's relink */
272                 m1->m_next = mo;
273         } else {
274                 /*
275                  * The header lies in the "middle" of the mbuf; copy
276                  * the remainder of the mbuf down over the header.
277                  */
278                 IPSECSTAT_INC(ips_input_middle);
279                 bcopy(mtod(m1, u_char *) + roff + hlen,
280                       mtod(m1, u_char *) + roff,
281                       m1->m_len - (roff + hlen));
282                 m1->m_len -= hlen;
283                 m->m_pkthdr.len -= hlen;
284         }
285         return (0);
286 }
287
288 /*
289  * Diagnostic routine to check mbuf alignment as required by the
290  * crypto device drivers (that use DMA).
291  */
292 void
293 m_checkalignment(const char* where, struct mbuf *m0, int off, int len)
294 {
295         int roff;
296         struct mbuf *m = m_getptr(m0, off, &roff);
297         caddr_t addr;
298
299         if (m == NULL)
300                 return;
301         printf("%s (off %u len %u): ", where, off, len);
302         addr = mtod(m, caddr_t) + roff;
303         do {
304                 int mlen;
305
306                 if (((uintptr_t) addr) & 3) {
307                         printf("addr misaligned %p,", addr);
308                         break;
309                 }
310                 mlen = m->m_len;
311                 if (mlen > len)
312                         mlen = len;
313                 len -= mlen;
314                 if (len && (mlen & 3)) {
315                         printf("len mismatch %u,", mlen);
316                         break;
317                 }
318                 m = m->m_next;
319                 addr = m ? mtod(m, caddr_t) : NULL;
320         } while (m && len > 0);
321         for (m = m0; m; m = m->m_next)
322                 printf(" [%p:%u]", mtod(m, caddr_t), m->m_len);
323         printf("\n");
324 }