ip_encap.c revision 95023
162587Sitojun/* $FreeBSD: head/sys/netinet/ip_encap.c 95023 2002-04-19 04:46:24Z suz $ */ 278064Sume/* $KAME: ip_encap.c,v 1.41 2001/03/15 08:35:08 itojun Exp $ */ 362587Sitojun 462587Sitojun/* 562587Sitojun * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 662587Sitojun * All rights reserved. 762587Sitojun * 862587Sitojun * Redistribution and use in source and binary forms, with or without 962587Sitojun * modification, are permitted provided that the following conditions 1062587Sitojun * are met: 1162587Sitojun * 1. Redistributions of source code must retain the above copyright 1262587Sitojun * notice, this list of conditions and the following disclaimer. 1362587Sitojun * 2. Redistributions in binary form must reproduce the above copyright 1462587Sitojun * notice, this list of conditions and the following disclaimer in the 1562587Sitojun * documentation and/or other materials provided with the distribution. 1662587Sitojun * 3. Neither the name of the project nor the names of its contributors 1762587Sitojun * may be used to endorse or promote products derived from this software 1862587Sitojun * without specific prior written permission. 1962587Sitojun * 2062587Sitojun * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2162587Sitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2262587Sitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2362587Sitojun * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2462587Sitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2562587Sitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2662587Sitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2762587Sitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2862587Sitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2962587Sitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3062587Sitojun * SUCH DAMAGE. 3162587Sitojun */ 3262587Sitojun/* 3362587Sitojun * My grandfather said that there's a devil inside tunnelling technology... 3462587Sitojun * 3562587Sitojun * We have surprisingly many protocols that want packets with IP protocol 3662587Sitojun * #4 or #41. Here's a list of protocols that want protocol #41: 3762587Sitojun * RFC1933 configured tunnel 3862587Sitojun * RFC1933 automatic tunnel 3962587Sitojun * RFC2401 IPsec tunnel 4062587Sitojun * RFC2473 IPv6 generic packet tunnelling 4162587Sitojun * RFC2529 6over4 tunnel 4262587Sitojun * mobile-ip6 (uses RFC2473) 4395023Ssuz * RFC3056 6to4 tunnel 4495023Ssuz * isatap tunnel 4562587Sitojun * Here's a list of protocol that want protocol #4: 4662587Sitojun * RFC1853 IPv4-in-IPv4 tunnelling 4762587Sitojun * RFC2003 IPv4 encapsulation within IPv4 4862587Sitojun * RFC2344 reverse tunnelling for mobile-ip4 4962587Sitojun * RFC2401 IPsec tunnel 5062587Sitojun * Well, what can I say. They impose different en/decapsulation mechanism 5162587Sitojun * from each other, so they need separate protocol handler. The only one 5262587Sitojun * we can easily determine by protocol # is IPsec, which always has 5362587Sitojun * AH/ESP/IPComp header right after outer IP header. 5462587Sitojun * 5562587Sitojun * So, clearly good old protosw does not work for protocol #4 and #41. 5662587Sitojun * The code will let you match protocol via src/dst address pair. 5762587Sitojun */ 5862587Sitojun/* XXX is M_NETADDR correct? */ 5962587Sitojun 6062587Sitojun#include "opt_mrouting.h" 6162587Sitojun#include "opt_inet.h" 6262587Sitojun#include "opt_inet6.h" 6362587Sitojun 6462587Sitojun#include <sys/param.h> 6562587Sitojun#include <sys/systm.h> 6662587Sitojun#include <sys/socket.h> 6762587Sitojun#include <sys/sockio.h> 6862587Sitojun#include <sys/mbuf.h> 6962587Sitojun#include <sys/errno.h> 7062587Sitojun#include <sys/protosw.h> 7178064Sume#include <sys/queue.h> 7262587Sitojun 7362587Sitojun#include <net/if.h> 7462587Sitojun#include <net/route.h> 7562587Sitojun 7662587Sitojun#include <netinet/in.h> 7762587Sitojun#include <netinet/in_systm.h> 7862587Sitojun#include <netinet/ip.h> 7962587Sitojun#include <netinet/ip_var.h> 8062587Sitojun#include <netinet/ip_encap.h> 8162587Sitojun 8262587Sitojun#ifdef INET6 8362587Sitojun#include <netinet/ip6.h> 8462587Sitojun#include <netinet6/ip6_var.h> 8562587Sitojun#include <netinet6/ip6protosw.h> 8662587Sitojun#endif 8762587Sitojun 8862587Sitojun#include <machine/stdarg.h> 8962587Sitojun 9062587Sitojun#include <net/net_osdep.h> 9162587Sitojun 9262587Sitojun#include <sys/kernel.h> 9362587Sitojun#include <sys/malloc.h> 9469774Sphkstatic MALLOC_DEFINE(M_NETADDR, "Export Host", "Export host address structure"); 9562587Sitojun 9692723Salfredstatic void encap_add(struct encaptab *); 9792723Salfredstatic int mask_match(const struct encaptab *, const struct sockaddr *, 9892723Salfred const struct sockaddr *); 9992723Salfredstatic void encap_fillarg(struct mbuf *, const struct encaptab *); 10062587Sitojun 10178064Sume#ifndef LIST_HEAD_INITIALIZER 10262587Sitojun/* rely upon BSS initialization */ 10362587SitojunLIST_HEAD(, encaptab) encaptab; 10478064Sume#else 10578064SumeLIST_HEAD(, encaptab) encaptab = LIST_HEAD_INITIALIZER(&encaptab); 10678064Sume#endif 10762587Sitojun 10862587Sitojunvoid 10962587Sitojunencap_init() 11062587Sitojun{ 11178064Sume static int initialized = 0; 11278064Sume 11378064Sume if (initialized) 11478064Sume return; 11578064Sume initialized++; 11662587Sitojun#if 0 11762587Sitojun /* 11862587Sitojun * we cannot use LIST_INIT() here, since drivers may want to call 11962587Sitojun * encap_attach(), on driver attach. encap_init() will be called 12062587Sitojun * on AF_INET{,6} initialization, which happens after driver 12162587Sitojun * initialization - using LIST_INIT() here can nuke encap_attach() 12262587Sitojun * from drivers. 12362587Sitojun */ 12462587Sitojun LIST_INIT(&encaptab); 12562587Sitojun#endif 12662587Sitojun} 12762587Sitojun 12878064Sume#ifdef INET 12962587Sitojunvoid 13083187Sjulianencap4_input(m, off) 13162587Sitojun struct mbuf *m; 13283187Sjulian int off; 13362587Sitojun{ 13462587Sitojun struct ip *ip; 13583187Sjulian int proto; 13662587Sitojun struct sockaddr_in s, d; 13782884Sjulian const struct protosw *psw; 13862587Sitojun struct encaptab *ep, *match; 13962587Sitojun int prio, matchprio; 14062587Sitojun 14162587Sitojun ip = mtod(m, struct ip *); 14282884Sjulian proto = ip->ip_p; 14362587Sitojun 14462587Sitojun bzero(&s, sizeof(s)); 14562587Sitojun s.sin_family = AF_INET; 14662587Sitojun s.sin_len = sizeof(struct sockaddr_in); 14762587Sitojun s.sin_addr = ip->ip_src; 14862587Sitojun bzero(&d, sizeof(d)); 14962587Sitojun d.sin_family = AF_INET; 15062587Sitojun d.sin_len = sizeof(struct sockaddr_in); 15162587Sitojun d.sin_addr = ip->ip_dst; 15262587Sitojun 15362587Sitojun match = NULL; 15462587Sitojun matchprio = 0; 15571999Sphk LIST_FOREACH(ep, &encaptab, chain) { 15662587Sitojun if (ep->af != AF_INET) 15762587Sitojun continue; 15862587Sitojun if (ep->proto >= 0 && ep->proto != proto) 15962587Sitojun continue; 16062587Sitojun if (ep->func) 16162587Sitojun prio = (*ep->func)(m, off, proto, ep->arg); 16262587Sitojun else { 16362587Sitojun /* 16462587Sitojun * it's inbound traffic, we need to match in reverse 16562587Sitojun * order 16662587Sitojun */ 16762587Sitojun prio = mask_match(ep, (struct sockaddr *)&d, 16862587Sitojun (struct sockaddr *)&s); 16962587Sitojun } 17062587Sitojun 17162587Sitojun /* 17262587Sitojun * We prioritize the matches by using bit length of the 17362587Sitojun * matches. mask_match() and user-supplied matching function 17462587Sitojun * should return the bit length of the matches (for example, 17562587Sitojun * if both src/dst are matched for IPv4, 64 should be returned). 17662587Sitojun * 0 or negative return value means "it did not match". 17762587Sitojun * 17862587Sitojun * The question is, since we have two "mask" portion, we 17962587Sitojun * cannot really define total order between entries. 18062587Sitojun * For example, which of these should be preferred? 18162587Sitojun * mask_match() returns 48 (32 + 16) for both of them. 18262587Sitojun * src=3ffe::/16, dst=3ffe:501::/32 18362587Sitojun * src=3ffe:501::/32, dst=3ffe::/16 18462587Sitojun * 18562587Sitojun * We need to loop through all the possible candidates 18662587Sitojun * to get the best match - the search takes O(n) for 18762587Sitojun * n attachments (i.e. interfaces). 18862587Sitojun */ 18962587Sitojun if (prio <= 0) 19062587Sitojun continue; 19162587Sitojun if (prio > matchprio) { 19262587Sitojun matchprio = prio; 19362587Sitojun match = ep; 19462587Sitojun } 19562587Sitojun } 19662587Sitojun 19762587Sitojun if (match) { 19862587Sitojun /* found a match, "match" has the best one */ 19982884Sjulian psw = match->psw; 20062587Sitojun if (psw && psw->pr_input) { 20162587Sitojun encap_fillarg(m, match); 20282884Sjulian (*psw->pr_input)(m, off); 20362587Sitojun } else 20462587Sitojun m_freem(m); 20562587Sitojun return; 20662587Sitojun } 20762587Sitojun 20862587Sitojun /* last resort: inject to raw socket */ 20982884Sjulian rip_input(m, off); 21062587Sitojun} 21178064Sume#endif 21262587Sitojun 21362587Sitojun#ifdef INET6 21462587Sitojunint 21562587Sitojunencap6_input(mp, offp, proto) 21662587Sitojun struct mbuf **mp; 21762587Sitojun int *offp; 21862587Sitojun int proto; 21962587Sitojun{ 22062587Sitojun struct mbuf *m = *mp; 22162587Sitojun struct ip6_hdr *ip6; 22262587Sitojun struct sockaddr_in6 s, d; 22362587Sitojun const struct ip6protosw *psw; 22462587Sitojun struct encaptab *ep, *match; 22562587Sitojun int prio, matchprio; 22662587Sitojun 22762587Sitojun ip6 = mtod(m, struct ip6_hdr *); 22862587Sitojun 22962587Sitojun bzero(&s, sizeof(s)); 23062587Sitojun s.sin6_family = AF_INET6; 23162587Sitojun s.sin6_len = sizeof(struct sockaddr_in6); 23262587Sitojun s.sin6_addr = ip6->ip6_src; 23362587Sitojun bzero(&d, sizeof(d)); 23462587Sitojun d.sin6_family = AF_INET6; 23562587Sitojun d.sin6_len = sizeof(struct sockaddr_in6); 23662587Sitojun d.sin6_addr = ip6->ip6_dst; 23762587Sitojun 23862587Sitojun match = NULL; 23962587Sitojun matchprio = 0; 24071999Sphk LIST_FOREACH(ep, &encaptab, chain) { 24162587Sitojun if (ep->af != AF_INET6) 24262587Sitojun continue; 24362587Sitojun if (ep->proto >= 0 && ep->proto != proto) 24462587Sitojun continue; 24562587Sitojun if (ep->func) 24662587Sitojun prio = (*ep->func)(m, *offp, proto, ep->arg); 24762587Sitojun else { 24862587Sitojun /* 24962587Sitojun * it's inbound traffic, we need to match in reverse 25062587Sitojun * order 25162587Sitojun */ 25262587Sitojun prio = mask_match(ep, (struct sockaddr *)&d, 25362587Sitojun (struct sockaddr *)&s); 25462587Sitojun } 25562587Sitojun 25662587Sitojun /* see encap4_input() for issues here */ 25762587Sitojun if (prio <= 0) 25862587Sitojun continue; 25962587Sitojun if (prio > matchprio) { 26062587Sitojun matchprio = prio; 26162587Sitojun match = ep; 26262587Sitojun } 26362587Sitojun } 26462587Sitojun 26562587Sitojun if (match) { 26662587Sitojun /* found a match */ 26762587Sitojun psw = (const struct ip6protosw *)match->psw; 26862587Sitojun if (psw && psw->pr_input) { 26962587Sitojun encap_fillarg(m, match); 27062587Sitojun return (*psw->pr_input)(mp, offp, proto); 27162587Sitojun } else { 27262587Sitojun m_freem(m); 27362587Sitojun return IPPROTO_DONE; 27462587Sitojun } 27562587Sitojun } 27662587Sitojun 27762587Sitojun /* last resort: inject to raw socket */ 27862587Sitojun return rip6_input(mp, offp, proto); 27962587Sitojun} 28062587Sitojun#endif 28162587Sitojun 28262587Sitojunstatic void 28362587Sitojunencap_add(ep) 28462587Sitojun struct encaptab *ep; 28562587Sitojun{ 28662587Sitojun 28762587Sitojun LIST_INSERT_HEAD(&encaptab, ep, chain); 28862587Sitojun} 28962587Sitojun 29062587Sitojun/* 29162587Sitojun * sp (src ptr) is always my side, and dp (dst ptr) is always remote side. 29262587Sitojun * length of mask (sm and dm) is assumed to be same as sp/dp. 29362587Sitojun * Return value will be necessary as input (cookie) for encap_detach(). 29462587Sitojun */ 29562587Sitojunconst struct encaptab * 29662587Sitojunencap_attach(af, proto, sp, sm, dp, dm, psw, arg) 29762587Sitojun int af; 29862587Sitojun int proto; 29962587Sitojun const struct sockaddr *sp, *sm; 30062587Sitojun const struct sockaddr *dp, *dm; 30162587Sitojun const struct protosw *psw; 30262587Sitojun void *arg; 30362587Sitojun{ 30462587Sitojun struct encaptab *ep; 30562587Sitojun int error; 30662587Sitojun int s; 30762587Sitojun 30862587Sitojun s = splnet(); 30962587Sitojun /* sanity check on args */ 31062587Sitojun if (sp->sa_len > sizeof(ep->src) || dp->sa_len > sizeof(ep->dst)) { 31162587Sitojun error = EINVAL; 31262587Sitojun goto fail; 31362587Sitojun } 31462587Sitojun if (sp->sa_len != dp->sa_len) { 31562587Sitojun error = EINVAL; 31662587Sitojun goto fail; 31762587Sitojun } 31862587Sitojun if (af != sp->sa_family || af != dp->sa_family) { 31962587Sitojun error = EINVAL; 32062587Sitojun goto fail; 32162587Sitojun } 32262587Sitojun 32362587Sitojun /* check if anyone have already attached with exactly same config */ 32471999Sphk LIST_FOREACH(ep, &encaptab, chain) { 32562587Sitojun if (ep->af != af) 32662587Sitojun continue; 32762587Sitojun if (ep->proto != proto) 32862587Sitojun continue; 32962587Sitojun if (ep->src.ss_len != sp->sa_len || 33062587Sitojun bcmp(&ep->src, sp, sp->sa_len) != 0 || 33162587Sitojun bcmp(&ep->srcmask, sm, sp->sa_len) != 0) 33262587Sitojun continue; 33362587Sitojun if (ep->dst.ss_len != dp->sa_len || 33462587Sitojun bcmp(&ep->dst, dp, dp->sa_len) != 0 || 33562587Sitojun bcmp(&ep->dstmask, dm, dp->sa_len) != 0) 33662587Sitojun continue; 33762587Sitojun 33862587Sitojun error = EEXIST; 33962587Sitojun goto fail; 34062587Sitojun } 34162587Sitojun 34262587Sitojun ep = malloc(sizeof(*ep), M_NETADDR, M_NOWAIT); /*XXX*/ 34362587Sitojun if (ep == NULL) { 34462587Sitojun error = ENOBUFS; 34562587Sitojun goto fail; 34662587Sitojun } 34762587Sitojun bzero(ep, sizeof(*ep)); 34862587Sitojun 34962587Sitojun ep->af = af; 35062587Sitojun ep->proto = proto; 35162587Sitojun bcopy(sp, &ep->src, sp->sa_len); 35262587Sitojun bcopy(sm, &ep->srcmask, sp->sa_len); 35362587Sitojun bcopy(dp, &ep->dst, dp->sa_len); 35462587Sitojun bcopy(dm, &ep->dstmask, dp->sa_len); 35562587Sitojun ep->psw = psw; 35662587Sitojun ep->arg = arg; 35762587Sitojun 35862587Sitojun encap_add(ep); 35962587Sitojun 36062587Sitojun error = 0; 36162587Sitojun splx(s); 36262587Sitojun return ep; 36362587Sitojun 36462587Sitojunfail: 36562587Sitojun splx(s); 36662587Sitojun return NULL; 36762587Sitojun} 36862587Sitojun 36962587Sitojunconst struct encaptab * 37062587Sitojunencap_attach_func(af, proto, func, psw, arg) 37162587Sitojun int af; 37262587Sitojun int proto; 37392723Salfred int (*func)(const struct mbuf *, int, int, void *); 37462587Sitojun const struct protosw *psw; 37562587Sitojun void *arg; 37662587Sitojun{ 37762587Sitojun struct encaptab *ep; 37862587Sitojun int error; 37962587Sitojun int s; 38062587Sitojun 38162587Sitojun s = splnet(); 38262587Sitojun /* sanity check on args */ 38362587Sitojun if (!func) { 38462587Sitojun error = EINVAL; 38562587Sitojun goto fail; 38662587Sitojun } 38762587Sitojun 38862587Sitojun ep = malloc(sizeof(*ep), M_NETADDR, M_NOWAIT); /*XXX*/ 38962587Sitojun if (ep == NULL) { 39062587Sitojun error = ENOBUFS; 39162587Sitojun goto fail; 39262587Sitojun } 39362587Sitojun bzero(ep, sizeof(*ep)); 39462587Sitojun 39562587Sitojun ep->af = af; 39662587Sitojun ep->proto = proto; 39762587Sitojun ep->func = func; 39862587Sitojun ep->psw = psw; 39962587Sitojun ep->arg = arg; 40062587Sitojun 40162587Sitojun encap_add(ep); 40262587Sitojun 40362587Sitojun error = 0; 40462587Sitojun splx(s); 40562587Sitojun return ep; 40662587Sitojun 40762587Sitojunfail: 40862587Sitojun splx(s); 40962587Sitojun return NULL; 41062587Sitojun} 41162587Sitojun 41262587Sitojunint 41362587Sitojunencap_detach(cookie) 41462587Sitojun const struct encaptab *cookie; 41562587Sitojun{ 41662587Sitojun const struct encaptab *ep = cookie; 41762587Sitojun struct encaptab *p; 41862587Sitojun 41971999Sphk LIST_FOREACH(p, &encaptab, chain) { 42062587Sitojun if (p == ep) { 42162587Sitojun LIST_REMOVE(p, chain); 42262587Sitojun free(p, M_NETADDR); /*XXX*/ 42362587Sitojun return 0; 42462587Sitojun } 42562587Sitojun } 42662587Sitojun 42762587Sitojun return EINVAL; 42862587Sitojun} 42962587Sitojun 43062587Sitojunstatic int 43162587Sitojunmask_match(ep, sp, dp) 43262587Sitojun const struct encaptab *ep; 43362587Sitojun const struct sockaddr *sp; 43462587Sitojun const struct sockaddr *dp; 43562587Sitojun{ 43662587Sitojun struct sockaddr_storage s; 43762587Sitojun struct sockaddr_storage d; 43862587Sitojun int i; 43962587Sitojun const u_int8_t *p, *q; 44062587Sitojun u_int8_t *r; 44162587Sitojun int matchlen; 44262587Sitojun 44362587Sitojun if (sp->sa_len > sizeof(s) || dp->sa_len > sizeof(d)) 44462587Sitojun return 0; 44562587Sitojun if (sp->sa_family != ep->af || dp->sa_family != ep->af) 44662587Sitojun return 0; 44762587Sitojun if (sp->sa_len != ep->src.ss_len || dp->sa_len != ep->dst.ss_len) 44862587Sitojun return 0; 44962587Sitojun 45062587Sitojun matchlen = 0; 45162587Sitojun 45262587Sitojun p = (const u_int8_t *)sp; 45362587Sitojun q = (const u_int8_t *)&ep->srcmask; 45462587Sitojun r = (u_int8_t *)&s; 45562587Sitojun for (i = 0 ; i < sp->sa_len; i++) { 45662587Sitojun r[i] = p[i] & q[i]; 45762587Sitojun /* XXX estimate */ 45862587Sitojun matchlen += (q[i] ? 8 : 0); 45962587Sitojun } 46062587Sitojun 46162587Sitojun p = (const u_int8_t *)dp; 46262587Sitojun q = (const u_int8_t *)&ep->dstmask; 46362587Sitojun r = (u_int8_t *)&d; 46462587Sitojun for (i = 0 ; i < dp->sa_len; i++) { 46562587Sitojun r[i] = p[i] & q[i]; 46662587Sitojun /* XXX rough estimate */ 46762587Sitojun matchlen += (q[i] ? 8 : 0); 46862587Sitojun } 46962587Sitojun 47062587Sitojun /* need to overwrite len/family portion as we don't compare them */ 47162587Sitojun s.ss_len = sp->sa_len; 47262587Sitojun s.ss_family = sp->sa_family; 47362587Sitojun d.ss_len = dp->sa_len; 47462587Sitojun d.ss_family = dp->sa_family; 47562587Sitojun 47662587Sitojun if (bcmp(&s, &ep->src, ep->src.ss_len) == 0 && 47762587Sitojun bcmp(&d, &ep->dst, ep->dst.ss_len) == 0) { 47862587Sitojun return matchlen; 47962587Sitojun } else 48062587Sitojun return 0; 48162587Sitojun} 48262587Sitojun 48362587Sitojunstatic void 48462587Sitojunencap_fillarg(m, ep) 48562587Sitojun struct mbuf *m; 48662587Sitojun const struct encaptab *ep; 48762587Sitojun{ 48862587Sitojun#if 0 48962587Sitojun m->m_pkthdr.aux = ep->arg; 49062587Sitojun#else 49162587Sitojun struct mbuf *n; 49262587Sitojun 49362587Sitojun n = m_aux_add(m, AF_INET, IPPROTO_IPV4); 49462587Sitojun if (n) { 49562587Sitojun *mtod(n, void **) = ep->arg; 49662587Sitojun n->m_len = sizeof(void *); 49762587Sitojun } 49862587Sitojun#endif 49962587Sitojun} 50062587Sitojun 50162587Sitojunvoid * 50262587Sitojunencap_getarg(m) 50362587Sitojun struct mbuf *m; 50462587Sitojun{ 50562587Sitojun void *p; 50662587Sitojun#if 0 50762587Sitojun p = m->m_pkthdr.aux; 50862587Sitojun m->m_pkthdr.aux = NULL; 50962587Sitojun return p; 51062587Sitojun#else 51162587Sitojun struct mbuf *n; 51262587Sitojun 51362587Sitojun p = NULL; 51462587Sitojun n = m_aux_find(m, AF_INET, IPPROTO_IPV4); 51562587Sitojun if (n) { 51662587Sitojun if (n->m_len == sizeof(void *)) 51762587Sitojun p = *mtod(n, void **); 51862587Sitojun m_aux_delete(m, n); 51962587Sitojun } 52062587Sitojun return p; 52162587Sitojun#endif 52262587Sitojun} 523