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