1aaea26efSSam Leffler /*- 2fe267a55SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3fe267a55SPedro F. Giffuni * 4aaea26efSSam Leffler * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting 5aaea26efSSam Leffler * All rights reserved. 6aaea26efSSam Leffler * 7aaea26efSSam Leffler * Redistribution and use in source and binary forms, with or without 8aaea26efSSam Leffler * modification, are permitted provided that the following conditions 9aaea26efSSam Leffler * are met: 10aaea26efSSam Leffler * 1. Redistributions of source code must retain the above copyright 11aaea26efSSam Leffler * notice, this list of conditions and the following disclaimer. 12aaea26efSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright 13aaea26efSSam Leffler * notice, this list of conditions and the following disclaimer in the 14aaea26efSSam Leffler * documentation and/or other materials provided with the distribution. 15aaea26efSSam Leffler * 16aaea26efSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17aaea26efSSam Leffler * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18aaea26efSSam Leffler * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19aaea26efSSam Leffler * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20aaea26efSSam Leffler * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21aaea26efSSam Leffler * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22aaea26efSSam Leffler * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23aaea26efSSam Leffler * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24aaea26efSSam Leffler * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25aaea26efSSam Leffler * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26aaea26efSSam Leffler * SUCH DAMAGE. 27aaea26efSSam Leffler * 28aaea26efSSam Leffler * $FreeBSD$ 29aaea26efSSam Leffler */ 3088768458SSam Leffler 3188768458SSam Leffler /* 3288768458SSam Leffler * IPsec-specific mbuf routines. 3388768458SSam Leffler */ 3488768458SSam Leffler 35*28d2a72bSJohn Baldwin #include "opt_ipsec.h" 36*28d2a72bSJohn Baldwin 3788768458SSam Leffler #include <sys/param.h> 3888768458SSam Leffler #include <sys/systm.h> 398ec07310SGleb Smirnoff #include <sys/malloc.h> 4088768458SSam Leffler #include <sys/mbuf.h> 4188768458SSam Leffler #include <sys/socket.h> 4288768458SSam Leffler 43eddfbb76SRobert Watson #include <net/vnet.h> 4488768458SSam Leffler #include <netinet/in.h> 4588768458SSam Leffler #include <netipsec/ipsec.h> 4688768458SSam Leffler 4788768458SSam Leffler /* 48972136faSSam Leffler * Make space for a new header of length hlen at skip bytes 49972136faSSam Leffler * into the packet. When doing this we allocate new mbufs only 5088768458SSam Leffler * when absolutely necessary. The mbuf where the new header 5188768458SSam Leffler * is to go is returned together with an offset into the mbuf. 5288768458SSam Leffler * If NULL is returned then the mbuf chain may have been modified; 5388768458SSam Leffler * the caller is assumed to always free the chain. 5488768458SSam Leffler */ 5588768458SSam Leffler struct mbuf * 5688768458SSam Leffler m_makespace(struct mbuf *m0, int skip, int hlen, int *off) 5788768458SSam Leffler { 5888768458SSam Leffler struct mbuf *m; 5988768458SSam Leffler unsigned remain; 6088768458SSam Leffler 619ffa9677SSam Leffler IPSEC_ASSERT(m0 != NULL, ("null mbuf")); 629ffa9677SSam Leffler IPSEC_ASSERT(hlen < MHLEN, ("hlen too big: %u", hlen)); 6388768458SSam Leffler 6488768458SSam Leffler for (m = m0; m && skip > m->m_len; m = m->m_next) 6588768458SSam Leffler skip -= m->m_len; 6688768458SSam Leffler if (m == NULL) 6788768458SSam Leffler return (NULL); 6888768458SSam Leffler /* 6988768458SSam Leffler * At this point skip is the offset into the mbuf m 7088768458SSam Leffler * where the new header should be placed. Figure out 7188768458SSam Leffler * if there's space to insert the new header. If so, 72023795f0SBjoern A. Zeeb * and copying the remainder makes sense then do so. 7388768458SSam Leffler * Otherwise insert a new mbuf in the chain, splitting 7488768458SSam Leffler * the contents of m as needed. 7588768458SSam Leffler */ 7688768458SSam Leffler remain = m->m_len - skip; /* data to move */ 77fcf59617SAndrey V. Elsukov if (remain > skip && 78fcf59617SAndrey V. Elsukov hlen + max_linkhdr < M_LEADINGSPACE(m)) { 79fcf59617SAndrey V. Elsukov /* 80fcf59617SAndrey V. Elsukov * mbuf has enough free space at the beginning. 81fcf59617SAndrey V. Elsukov * XXX: which operation is the most heavy - copying of 82fcf59617SAndrey V. Elsukov * possible several hundred of bytes or allocation 83fcf59617SAndrey V. Elsukov * of new mbuf? We can remove max_linkhdr check 84fcf59617SAndrey V. Elsukov * here, but it is possible that this will lead 85fcf59617SAndrey V. Elsukov * to allocation of new mbuf in Layer 2 code. 86fcf59617SAndrey V. Elsukov */ 87fcf59617SAndrey V. Elsukov m->m_data -= hlen; 88fcf59617SAndrey V. Elsukov bcopy(mtodo(m, hlen), mtod(m, caddr_t), skip); 89fcf59617SAndrey V. Elsukov m->m_len += hlen; 90fcf59617SAndrey V. Elsukov *off = skip; 91fcf59617SAndrey V. Elsukov } else if (hlen > M_TRAILINGSPACE(m)) { 92357c11c9SVANHULLEBUS Yvan struct mbuf *n0, *n, **np; 93357c11c9SVANHULLEBUS Yvan int todo, len, done, alloc; 9488768458SSam Leffler 95357c11c9SVANHULLEBUS Yvan n0 = NULL; 96357c11c9SVANHULLEBUS Yvan np = &n0; 97357c11c9SVANHULLEBUS Yvan alloc = 0; 98357c11c9SVANHULLEBUS Yvan done = 0; 99357c11c9SVANHULLEBUS Yvan todo = remain; 100357c11c9SVANHULLEBUS Yvan while (todo > 0) { 101357c11c9SVANHULLEBUS Yvan if (todo > MHLEN) { 102eb1b1807SGleb Smirnoff n = m_getcl(M_NOWAIT, m->m_type, 0); 103357c11c9SVANHULLEBUS Yvan len = MCLBYTES; 104357c11c9SVANHULLEBUS Yvan } 105357c11c9SVANHULLEBUS Yvan else { 106eb1b1807SGleb Smirnoff n = m_get(M_NOWAIT, m->m_type); 107357c11c9SVANHULLEBUS Yvan len = MHLEN; 108357c11c9SVANHULLEBUS Yvan } 109357c11c9SVANHULLEBUS Yvan if (n == NULL) { 110357c11c9SVANHULLEBUS Yvan m_freem(n0); 111357c11c9SVANHULLEBUS Yvan return NULL; 112357c11c9SVANHULLEBUS Yvan } 113357c11c9SVANHULLEBUS Yvan *np = n; 114357c11c9SVANHULLEBUS Yvan np = &n->m_next; 115357c11c9SVANHULLEBUS Yvan alloc++; 116357c11c9SVANHULLEBUS Yvan len = min(todo, len); 117357c11c9SVANHULLEBUS Yvan memcpy(n->m_data, mtod(m, char *) + skip + done, len); 118357c11c9SVANHULLEBUS Yvan n->m_len = len; 119357c11c9SVANHULLEBUS Yvan done += len; 120357c11c9SVANHULLEBUS Yvan todo -= len; 121357c11c9SVANHULLEBUS Yvan } 122357c11c9SVANHULLEBUS Yvan 12388768458SSam Leffler if (hlen <= M_TRAILINGSPACE(m) + remain) { 12488768458SSam Leffler m->m_len = skip + hlen; 12588768458SSam Leffler *off = skip; 126357c11c9SVANHULLEBUS Yvan if (n0 != NULL) { 127357c11c9SVANHULLEBUS Yvan *np = m->m_next; 128357c11c9SVANHULLEBUS Yvan m->m_next = n0; 12988768458SSam Leffler } 130357c11c9SVANHULLEBUS Yvan } 131357c11c9SVANHULLEBUS Yvan else { 132eb1b1807SGleb Smirnoff n = m_get(M_NOWAIT, m->m_type); 133357c11c9SVANHULLEBUS Yvan if (n == NULL) { 134357c11c9SVANHULLEBUS Yvan m_freem(n0); 135357c11c9SVANHULLEBUS Yvan return NULL; 136357c11c9SVANHULLEBUS Yvan } 137357c11c9SVANHULLEBUS Yvan alloc++; 138357c11c9SVANHULLEBUS Yvan 139357c11c9SVANHULLEBUS Yvan if ((n->m_next = n0) == NULL) 140357c11c9SVANHULLEBUS Yvan np = &n->m_next; 141357c11c9SVANHULLEBUS Yvan n0 = n; 142357c11c9SVANHULLEBUS Yvan 143357c11c9SVANHULLEBUS Yvan *np = m->m_next; 144357c11c9SVANHULLEBUS Yvan m->m_next = n0; 145357c11c9SVANHULLEBUS Yvan 146357c11c9SVANHULLEBUS Yvan n->m_len = hlen; 147357c11c9SVANHULLEBUS Yvan m->m_len = skip; 148357c11c9SVANHULLEBUS Yvan 14988768458SSam Leffler m = n; /* header is at front ... */ 15088768458SSam Leffler *off = 0; /* ... of new mbuf */ 15188768458SSam Leffler } 1526659296cSAndrey V. Elsukov IPSECSTAT_INC(ips_mbinserted); 15388768458SSam Leffler } else { 15488768458SSam Leffler /* 15588768458SSam Leffler * Copy the remainder to the back of the mbuf 15688768458SSam Leffler * so there's space to write the new header. 15788768458SSam Leffler */ 1582aebee88SDag-Erling Smørgrav bcopy(mtod(m, caddr_t) + skip, 15988768458SSam Leffler mtod(m, caddr_t) + skip + hlen, remain); 16088768458SSam Leffler m->m_len += hlen; 16188768458SSam Leffler *off = skip; 16288768458SSam Leffler } 16388768458SSam Leffler m0->m_pkthdr.len += hlen; /* adjust packet length */ 16488768458SSam Leffler return m; 16588768458SSam Leffler } 16688768458SSam Leffler 16788768458SSam Leffler /* 16888768458SSam Leffler * m_pad(m, n) pads <m> with <n> bytes at the end. The packet header 16988768458SSam Leffler * length is updated, and a pointer to the first byte of the padding 17088768458SSam Leffler * (which is guaranteed to be all in one mbuf) is returned. 17188768458SSam Leffler */ 17288768458SSam Leffler caddr_t 17388768458SSam Leffler m_pad(struct mbuf *m, int n) 17488768458SSam Leffler { 1753e85b721SEd Maste struct mbuf *m0, *m1; 1763e85b721SEd Maste int len, pad; 17788768458SSam Leffler caddr_t retval; 17888768458SSam Leffler 17988768458SSam Leffler if (n <= 0) { /* No stupid arguments. */ 1809ffa9677SSam Leffler DPRINTF(("%s: pad length invalid (%d)\n", __func__, n)); 18188768458SSam Leffler m_freem(m); 18288768458SSam Leffler return NULL; 18388768458SSam Leffler } 18488768458SSam Leffler 18588768458SSam Leffler len = m->m_pkthdr.len; 18688768458SSam Leffler pad = n; 18788768458SSam Leffler m0 = m; 18888768458SSam Leffler 18988768458SSam Leffler while (m0->m_len < len) { 19088768458SSam Leffler len -= m0->m_len; 19188768458SSam Leffler m0 = m0->m_next; 19288768458SSam Leffler } 19388768458SSam Leffler 19488768458SSam Leffler if (m0->m_len != len) { 1959ffa9677SSam Leffler DPRINTF(("%s: length mismatch (should be %d instead of %d)\n", 1969ffa9677SSam Leffler __func__, m->m_pkthdr.len, 1979ffa9677SSam Leffler m->m_pkthdr.len + m0->m_len - len)); 19888768458SSam Leffler 19988768458SSam Leffler m_freem(m); 20088768458SSam Leffler return NULL; 20188768458SSam Leffler } 20288768458SSam Leffler 20388768458SSam Leffler /* Check for zero-length trailing mbufs, and find the last one. */ 20488768458SSam Leffler for (m1 = m0; m1->m_next; m1 = m1->m_next) { 20588768458SSam Leffler if (m1->m_next->m_len != 0) { 2069ffa9677SSam Leffler DPRINTF(("%s: length mismatch (should be %d instead " 2079ffa9677SSam Leffler "of %d)\n", __func__, 20888768458SSam Leffler m->m_pkthdr.len, 20988768458SSam Leffler m->m_pkthdr.len + m1->m_next->m_len)); 21088768458SSam Leffler 21188768458SSam Leffler m_freem(m); 21288768458SSam Leffler return NULL; 21388768458SSam Leffler } 21488768458SSam Leffler 21588768458SSam Leffler m0 = m1->m_next; 21688768458SSam Leffler } 21788768458SSam Leffler 21888768458SSam Leffler if (pad > M_TRAILINGSPACE(m0)) { 21988768458SSam Leffler /* Add an mbuf to the chain. */ 220eb1b1807SGleb Smirnoff MGET(m1, M_NOWAIT, MT_DATA); 221155d72c4SPedro F. Giffuni if (m1 == NULL) { 22288768458SSam Leffler m_freem(m0); 2239ffa9677SSam Leffler DPRINTF(("%s: unable to get extra mbuf\n", __func__)); 22488768458SSam Leffler return NULL; 22588768458SSam Leffler } 22688768458SSam Leffler 22788768458SSam Leffler m0->m_next = m1; 22888768458SSam Leffler m0 = m1; 22988768458SSam Leffler m0->m_len = 0; 23088768458SSam Leffler } 23188768458SSam Leffler 23288768458SSam Leffler retval = m0->m_data + m0->m_len; 23388768458SSam Leffler m0->m_len += pad; 23488768458SSam Leffler m->m_pkthdr.len += pad; 23588768458SSam Leffler 23688768458SSam Leffler return retval; 23788768458SSam Leffler } 23888768458SSam Leffler 23988768458SSam Leffler /* 24088768458SSam Leffler * Remove hlen data at offset skip in the packet. This is used by 24188768458SSam Leffler * the protocols strip protocol headers and associated data (e.g. IV, 24288768458SSam Leffler * authenticator) on input. 24388768458SSam Leffler */ 24488768458SSam Leffler int 24588768458SSam Leffler m_striphdr(struct mbuf *m, int skip, int hlen) 24688768458SSam Leffler { 24788768458SSam Leffler struct mbuf *m1; 24888768458SSam Leffler int roff; 24988768458SSam Leffler 25088768458SSam Leffler /* Find beginning of header */ 25188768458SSam Leffler m1 = m_getptr(m, skip, &roff); 25288768458SSam Leffler if (m1 == NULL) 25388768458SSam Leffler return (EINVAL); 25488768458SSam Leffler 25588768458SSam Leffler /* Remove the header and associated data from the mbuf. */ 25688768458SSam Leffler if (roff == 0) { 25788768458SSam Leffler /* The header was at the beginning of the mbuf */ 2586659296cSAndrey V. Elsukov IPSECSTAT_INC(ips_input_front); 25988768458SSam Leffler m_adj(m1, hlen); 260bf18dfa2SAndrey V. Elsukov if (m1 != m) 26188768458SSam Leffler m->m_pkthdr.len -= hlen; 26288768458SSam Leffler } else if (roff + hlen >= m1->m_len) { 26388768458SSam Leffler struct mbuf *mo; 264bf18dfa2SAndrey V. Elsukov int adjlen; 26588768458SSam Leffler 26688768458SSam Leffler /* 26788768458SSam Leffler * Part or all of the header is at the end of this mbuf, 26888768458SSam Leffler * so first let's remove the remainder of the header from 26988768458SSam Leffler * the beginning of the remainder of the mbuf chain, if any. 27088768458SSam Leffler */ 2716659296cSAndrey V. Elsukov IPSECSTAT_INC(ips_input_end); 27288768458SSam Leffler if (roff + hlen > m1->m_len) { 273bf18dfa2SAndrey V. Elsukov adjlen = roff + hlen - m1->m_len; 274bf18dfa2SAndrey V. Elsukov 27588768458SSam Leffler /* Adjust the next mbuf by the remainder */ 276bf18dfa2SAndrey V. Elsukov m_adj(m1->m_next, adjlen); 27788768458SSam Leffler 27888768458SSam Leffler /* The second mbuf is guaranteed not to have a pkthdr... */ 279bf18dfa2SAndrey V. Elsukov m->m_pkthdr.len -= adjlen; 28088768458SSam Leffler } 28188768458SSam Leffler 28288768458SSam Leffler /* Now, let's unlink the mbuf chain for a second...*/ 28388768458SSam Leffler mo = m1->m_next; 28488768458SSam Leffler m1->m_next = NULL; 28588768458SSam Leffler 28688768458SSam Leffler /* ...and trim the end of the first part of the chain...sick */ 287bf18dfa2SAndrey V. Elsukov adjlen = m1->m_len - roff; 288bf18dfa2SAndrey V. Elsukov m_adj(m1, -adjlen); 289bf18dfa2SAndrey V. Elsukov if (m1 != m) 290bf18dfa2SAndrey V. Elsukov m->m_pkthdr.len -= adjlen; 29188768458SSam Leffler 29288768458SSam Leffler /* Finally, let's relink */ 29388768458SSam Leffler m1->m_next = mo; 29488768458SSam Leffler } else { 29588768458SSam Leffler /* 29688768458SSam Leffler * The header lies in the "middle" of the mbuf; copy 29788768458SSam Leffler * the remainder of the mbuf down over the header. 29888768458SSam Leffler */ 2996659296cSAndrey V. Elsukov IPSECSTAT_INC(ips_input_middle); 30088768458SSam Leffler bcopy(mtod(m1, u_char *) + roff + hlen, 30188768458SSam Leffler mtod(m1, u_char *) + roff, 30288768458SSam Leffler m1->m_len - (roff + hlen)); 30388768458SSam Leffler m1->m_len -= hlen; 30488768458SSam Leffler m->m_pkthdr.len -= hlen; 30588768458SSam Leffler } 30688768458SSam Leffler return (0); 30788768458SSam Leffler } 30888768458SSam Leffler 30988768458SSam Leffler /* 31088768458SSam Leffler * Diagnostic routine to check mbuf alignment as required by the 31188768458SSam Leffler * crypto device drivers (that use DMA). 31288768458SSam Leffler */ 31388768458SSam Leffler void 31488768458SSam Leffler m_checkalignment(const char* where, struct mbuf *m0, int off, int len) 31588768458SSam Leffler { 31688768458SSam Leffler int roff; 31788768458SSam Leffler struct mbuf *m = m_getptr(m0, off, &roff); 31888768458SSam Leffler caddr_t addr; 31988768458SSam Leffler 32088768458SSam Leffler if (m == NULL) 32188768458SSam Leffler return; 32288768458SSam Leffler printf("%s (off %u len %u): ", where, off, len); 32388768458SSam Leffler addr = mtod(m, caddr_t) + roff; 32488768458SSam Leffler do { 32588768458SSam Leffler int mlen; 32688768458SSam Leffler 32788768458SSam Leffler if (((uintptr_t) addr) & 3) { 32888768458SSam Leffler printf("addr misaligned %p,", addr); 32988768458SSam Leffler break; 33088768458SSam Leffler } 33188768458SSam Leffler mlen = m->m_len; 33288768458SSam Leffler if (mlen > len) 33388768458SSam Leffler mlen = len; 33488768458SSam Leffler len -= mlen; 33588768458SSam Leffler if (len && (mlen & 3)) { 33688768458SSam Leffler printf("len mismatch %u,", mlen); 33788768458SSam Leffler break; 33888768458SSam Leffler } 33988768458SSam Leffler m = m->m_next; 34088768458SSam Leffler addr = m ? mtod(m, caddr_t) : NULL; 34188768458SSam Leffler } while (m && len > 0); 34288768458SSam Leffler for (m = m0; m; m = m->m_next) 34388768458SSam Leffler printf(" [%p:%u]", mtod(m, caddr_t), m->m_len); 34488768458SSam Leffler printf("\n"); 34588768458SSam Leffler } 346