Return to ip6_input.c CVS log | Up to [cvs.NetBSD.org] / src / sys / netinet6 |
Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. |
version 1.31, 2000/12/22 20:01:18 | version 1.64.2.2, 2004/09/18 14:55:14 | ||
---|---|---|---|
|
|
||
/* $NetBSD$ */ | /* $NetBSD$ */ | ||
/* $KAME: ip6_input.c,v 1.121 2000/08/31 06:07:29 itojun Exp $ */ | /* $KAME: ip6_input.c,v 1.188 2001/03/29 05:34:31 itojun Exp $ */ | ||
/* | /* | ||
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. | ||
|
|
||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||
* notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||
* documentation and/or other materials provided with the distribution. | * documentation and/or other materials provided with the distribution. | ||
* 3. All advertising materials mentioning features or use of this software | * 3. Neither the name of the University nor the names of its contributors | ||
* must display the following acknowledgement: | |||
* This product includes software developed by the University of | |||
* California, Berkeley and its contributors. | |||
* 4. Neither the name of the University nor the names of its contributors | |||
* may be used to endorse or promote products derived from this software | * may be used to endorse or promote products derived from this software | ||
* without specific prior written permission. | * without specific prior written permission. | ||
* | * | ||
|
|
||
* @(#)ip_input.c 8.2 (Berkeley) 1/4/94 | * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 | ||
*/ | */ | ||
#include <sys/cdefs.h> | |||
__KERNEL_RCSID(0, "$NetBSD$"); | |||
#include "opt_inet.h" | #include "opt_inet.h" | ||
#include "opt_ipsec.h" | #include "opt_ipsec.h" | ||
#include "opt_pfil_hooks.h" | #include "opt_pfil_hooks.h" | ||
|
|
||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||
#include <sys/syslog.h> | #include <sys/syslog.h> | ||
#include <sys/proc.h> | #include <sys/proc.h> | ||
#include <sys/sysctl.h> | |||
#include <net/if.h> | #include <net/if.h> | ||
#include <net/if_types.h> | #include <net/if_types.h> | ||
|
|
||
#ifdef INET | #ifdef INET | ||
#include <netinet/ip.h> | #include <netinet/ip.h> | ||
#include <netinet/ip_icmp.h> | #include <netinet/ip_icmp.h> | ||
#endif /*INET*/ | #endif /* INET */ | ||
#include <netinet/ip6.h> | #include <netinet/ip6.h> | ||
#include <netinet6/in6_var.h> | #include <netinet6/in6_var.h> | ||
#include <netinet6/ip6_var.h> | #include <netinet6/ip6_var.h> | ||
|
|
||
#include <netinet/icmp6.h> | #include <netinet/icmp6.h> | ||
#include <netinet6/in6_ifattach.h> | #include <netinet6/in6_ifattach.h> | ||
#include <netinet6/nd6.h> | #include <netinet6/nd6.h> | ||
#include <netinet6/in6_prefix.h> | |||
#ifdef IPV6FIREWALL | #ifdef IPSEC | ||
#include <netinet6/ip6_fw.h> | #include <netinet6/ipsec.h> | ||
#endif | #endif | ||
#include <netinet6/ip6protosw.h> | #include <netinet6/ip6protosw.h> | ||
|
|
||
/* we need it for NLOOP. */ | /* we need it for NLOOP. */ | ||
#include "loop.h" | #include "loop.h" | ||
#include "faith.h" | #include "faith.h" | ||
#include "gif.h" | #include "gif.h" | ||
#include "bpfilter.h" | #include "bpfilter.h" | ||
#if NGIF > 0 | |||
#include <netinet6/in6_gif.h> | |||
#endif | |||
#include <net/net_osdep.h> | #include <net/net_osdep.h> | ||
extern struct domain inet6domain; | extern struct domain inet6domain; | ||
|
|
||
int ip6_sourcecheck; /* XXX */ | int ip6_sourcecheck; /* XXX */ | ||
int ip6_sourcecheck_interval; /* XXX */ | int ip6_sourcecheck_interval; /* XXX */ | ||
#ifdef IPV6FIREWALL | |||
/* firewall hooks */ | |||
ip6_fw_chk_t *ip6_fw_chk_ptr; | |||
ip6_fw_ctl_t *ip6_fw_ctl_ptr; | |||
#endif | |||
#ifdef PFIL_HOOKS | #ifdef PFIL_HOOKS | ||
struct pfil_head inet6_pfil_hook; | struct pfil_head inet6_pfil_hook; | ||
#endif | #endif | ||
|
|
||
static void ip6_init2 __P((void *)); | static void ip6_init2 __P((void *)); | ||
static int ip6_hopopts_input __P((u_int32_t *, u_int32_t *, struct mbuf **, int *)); | static int ip6_hopopts_input __P((u_int32_t *, u_int32_t *, struct mbuf **, int *)); | ||
static struct mbuf *ip6_pullexthdr __P((struct mbuf *, size_t, int)); | |||
/* | /* | ||
* IP6 initialization: fill in IP6 protocol switch table. | * IP6 initialization: fill in IP6 protocol switch table. | ||
|
|
||
void | void | ||
ip6_init() | ip6_init() | ||
{ | { | ||
register struct ip6protosw *pr; | struct ip6protosw *pr; | ||
register int i; | int i; | ||
struct timeval tv; | |||
pr = (struct ip6protosw *)pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW); | pr = (struct ip6protosw *)pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW); | ||
if (pr == 0) | if (pr == 0) | ||
|
|
||
ip6intrq.ifq_maxlen = ip6qmaxlen; | ip6intrq.ifq_maxlen = ip6qmaxlen; | ||
nd6_init(); | nd6_init(); | ||
frag6_init(); | frag6_init(); | ||
#ifdef IPV6FIREWALL | |||
ip6_fw_init(); | |||
#endif | |||
/* | |||
* in many cases, random() here does NOT return random number | |||
* as initialization during bootstrap time occur in fixed order. | |||
*/ | |||
microtime(&tv); | |||
ip6_flow_seq = random() ^ tv.tv_usec; | |||
ip6_init2((void *)0); | ip6_init2((void *)0); | ||
|
|
||
ip6_init2(dummy) | ip6_init2(dummy) | ||
void *dummy; | void *dummy; | ||
{ | { | ||
/* | |||
* to route local address of p2p link to loopback, | |||
* assign loopback address first. | |||
*/ | |||
in6_ifattach(&loif[0], NULL); | |||
/* nd6_timer_init */ | /* nd6_timer_init */ | ||
callout_init(&nd6_timer_ch); | callout_init(&nd6_timer_ch); | ||
callout_reset(&nd6_timer_ch, hz, nd6_timer, NULL); | callout_reset(&nd6_timer_ch, hz, nd6_timer, NULL); | ||
/* router renumbering prefix list maintenance */ | |||
callout_init(&in6_rr_timer_ch); | |||
callout_reset(&in6_rr_timer_ch, hz, in6_rr_timer, NULL); | |||
} | } | ||
/* | /* | ||
|
|
||
struct mbuf *m; | struct mbuf *m; | ||
for (;;) { | for (;;) { | ||
s = splimp(); | s = splnet(); | ||
IF_DEQUEUE(&ip6intrq, m); | IF_DEQUEUE(&ip6intrq, m); | ||
splx(s); | splx(s); | ||
if (m == 0) | if (m == 0) | ||
|
|
||
u_int32_t rtalert = ~0; | u_int32_t rtalert = ~0; | ||
int nxt, ours = 0; | int nxt, ours = 0; | ||
struct ifnet *deliverifp = NULL; | struct ifnet *deliverifp = NULL; | ||
int srcrt = 0; | |||
#ifdef IPSEC | #ifdef IPSEC | ||
/* | /* | ||
* should the inner packet be considered authentic? | * should the inner packet be considered authentic? | ||
* see comment in ah4_input(). | * see comment in ah4_input(). | ||
*/ | */ | ||
if (m) { | m->m_flags &= ~M_AUTHIPHDR; | ||
m->m_flags &= ~M_AUTHIPHDR; | m->m_flags &= ~M_AUTHIPDGM; | ||
m->m_flags &= ~M_AUTHIPDGM; | |||
} | |||
#endif | #endif | ||
/* | /* | ||
* mbuf statistics by kazu | * mbuf statistics | ||
*/ | */ | ||
if (m->m_flags & M_EXT) { | if (m->m_flags & M_EXT) { | ||
if (m->m_next) | if (m->m_next) | ||
|
|
||
else | else | ||
ip6stat.ip6s_mext1++; | ip6stat.ip6s_mext1++; | ||
} else { | } else { | ||
#define M2MMAX (sizeof(ip6stat.ip6s_m2m)/sizeof(ip6stat.ip6s_m2m[0])) | |||
if (m->m_next) { | if (m->m_next) { | ||
if (m->m_flags & M_LOOP) { | if (m->m_flags & M_LOOP) { | ||
ip6stat.ip6s_m2m[loif[0].if_index]++; /*XXX*/ | ip6stat.ip6s_m2m[loif[0].if_index]++; /* XXX */ | ||
} else if (m->m_pkthdr.rcvif->if_index <= 31) | } else if (m->m_pkthdr.rcvif->if_index < M2MMAX) | ||
ip6stat.ip6s_m2m[m->m_pkthdr.rcvif->if_index]++; | ip6stat.ip6s_m2m[m->m_pkthdr.rcvif->if_index]++; | ||
else | else | ||
ip6stat.ip6s_m2m[0]++; | ip6stat.ip6s_m2m[0]++; | ||
} else | } else | ||
ip6stat.ip6s_m1++; | ip6stat.ip6s_m1++; | ||
#undef M2MMAX | |||
} | } | ||
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_receive); | in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_receive); | ||
ip6stat.ip6s_total++; | ip6stat.ip6s_total++; | ||
#ifndef PULLDOWN_TEST | /* | ||
/* XXX is the line really necessary? */ | * If the IPv6 header is not aligned, slurp it up into a new | ||
IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), /*nothing*/); | * mbuf with space for link headers, in the event we forward | ||
#endif | * it. OTherwise, if it is aligned, make sure the entire base | ||
* IPv6 header is in the first mbuf of the chain. | |||
if (m->m_len < sizeof(struct ip6_hdr)) { | */ | ||
struct ifnet *inifp; | if (IP6_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) { | ||
inifp = m->m_pkthdr.rcvif; | struct ifnet *inifp = m->m_pkthdr.rcvif; | ||
if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == 0) { | if ((m = m_copyup(m, sizeof(struct ip6_hdr), | ||
(max_linkhdr + 3) & ~3)) == NULL) { | |||
/* XXXJRT new stat, please */ | |||
ip6stat.ip6s_toosmall++; | |||
in6_ifstat_inc(inifp, ifs6_in_hdrerr); | |||
return; | |||
} | |||
} else if (__predict_false(m->m_len < sizeof(struct ip6_hdr))) { | |||
struct ifnet *inifp = m->m_pkthdr.rcvif; | |||
if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) { | |||
ip6stat.ip6s_toosmall++; | ip6stat.ip6s_toosmall++; | ||
in6_ifstat_inc(inifp, ifs6_in_hdrerr); | in6_ifstat_inc(inifp, ifs6_in_hdrerr); | ||
return; | return; | ||
|
|
||
* Note that filters must _never_ set this flag, as another filter | * Note that filters must _never_ set this flag, as another filter | ||
* in the list may have previously cleared it. | * in the list may have previously cleared it. | ||
*/ | */ | ||
if (pfil_run_hooks(&inet6_pfil_hook, &m, m->m_pkthdr.rcvif, | |||
PFIL_IN) != 0) | |||
return; | |||
if (m == NULL) | |||
return; | |||
ip6 = mtod(m, struct ip6_hdr *); | |||
#endif /* PFIL_HOOKS */ | |||
ip6stat.ip6s_nxthist[ip6->ip6_nxt]++; | |||
#ifdef IPV6FIREWALL | |||
/* | /* | ||
* Check with the firewall... | * let ipfilter look at packet on the wire, | ||
* not the decapsulated packet. | |||
*/ | */ | ||
if (ip6_fw_chk_ptr) { | #ifdef IPSEC | ||
u_short port = 0; | if (!ipsec_getnhist(m)) | ||
/* If ipfw says divert, we have to just drop packet */ | #else | ||
/* use port as a dummy argument */ | if (1) | ||
if ((*ip6_fw_chk_ptr)(&ip6, NULL, &port, &m)) { | #endif | ||
m_freem(m); | { | ||
m = NULL; | struct in6_addr odst; | ||
} | |||
if (!m) | odst = ip6->ip6_dst; | ||
if (pfil_run_hooks(&inet6_pfil_hook, &m, m->m_pkthdr.rcvif, | |||
PFIL_IN) != 0) | |||
return; | return; | ||
if (m == NULL) | |||
return; | |||
ip6 = mtod(m, struct ip6_hdr *); | |||
srcrt = !IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst); | |||
} | } | ||
#endif | #endif /* PFIL_HOOKS */ | ||
ip6stat.ip6s_nxthist[ip6->ip6_nxt]++; | |||
#ifdef ALTQ | #ifdef ALTQ | ||
/* XXX Temporary until ALTQ is changed to use a pfil hook */ | |||
if (altq_input != NULL && (*altq_input)(m, AF_INET6) == 0) { | if (altq_input != NULL && (*altq_input)(m, AF_INET6) == 0) { | ||
/* packet is dropped by traffic conditioner */ | /* packet is dropped by traffic conditioner */ | ||
return; | return; | ||
|
|
||
#endif | #endif | ||
/* | /* | ||
* Scope check | * Check against address spoofing/corruption. | ||
*/ | */ | ||
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src) || | if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src) || | ||
IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) { | IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) { | ||
/* | |||
* XXX: "badscope" is not very suitable for a multicast source. | |||
*/ | |||
ip6stat.ip6s_badscope++; | ip6stat.ip6s_badscope++; | ||
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); | in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); | ||
goto bad; | goto bad; | ||
} | } | ||
/* | /* | ||
* The following check is not documented in the spec. Malicious party | * The following check is not documented in specs. A malicious | ||
* may be able to use IPv4 mapped addr to confuse tcp/udp stack and | * party may be able to use IPv4 mapped addr to confuse tcp/udp stack | ||
* bypass security checks (act as if it was from 127.0.0.1 by using | * and bypass security checks (act as if it was from 127.0.0.1 by using | ||
* IPv6 src ::ffff:127.0.0.1). Be cautious. | * IPv6 src ::ffff:127.0.0.1). Be cautious. | ||
* | |||
* This check chokes if we are in an SIIT cloud. As none of BSDs | |||
* support IPv4-less kernel compilation, we cannot support SIIT | |||
* environment at all. So, it makes more sense for us to reject any | |||
* malicious packets for non-SIIT environment, than try to do a | |||
* partial support for SIIT environment. | |||
*/ | */ | ||
if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) || | if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) || | ||
IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) { | IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) { | ||
|
|
||
goto bad; | goto bad; | ||
} | } | ||
#endif | #endif | ||
if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) || | if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) || | ||
IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) { | IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) { | ||
if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) { | if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) { | ||
|
|
||
} | } | ||
} | } | ||
#ifndef FAKE_LOOPBACK_IF | /* drop packets if interface ID portion is already filled */ | ||
if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) | if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { | ||
#else | if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src) && | ||
if (1) | ip6->ip6_src.s6_addr16[1]) { | ||
#endif | ip6stat.ip6s_badscope++; | ||
{ | goto bad; | ||
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) | } | ||
ip6->ip6_src.s6_addr16[1] | if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst) && | ||
= htons(m->m_pkthdr.rcvif->if_index); | ip6->ip6_dst.s6_addr16[1]) { | ||
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) | ip6stat.ip6s_badscope++; | ||
ip6->ip6_dst.s6_addr16[1] | goto bad; | ||
= htons(m->m_pkthdr.rcvif->if_index); | } | ||
} | } | ||
/* | if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) | ||
* XXX we need this since we do not have "goto ours" hack route | ip6->ip6_src.s6_addr16[1] | ||
* for some of our ifaddrs on loopback interface. | = htons(m->m_pkthdr.rcvif->if_index); | ||
* we should correct it by changing in6_ifattach to install | if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) | ||
* "goto ours" hack route. | ip6->ip6_dst.s6_addr16[1] | ||
*/ | = htons(m->m_pkthdr.rcvif->if_index); | ||
if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) != 0) { | |||
if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) { | /* | ||
ours = 1; | * We use rt->rt_ifp to determine if the address is ours or not. | ||
deliverifp = m->m_pkthdr.rcvif; | * If rt_ifp is lo0, the address is ours. | ||
goto hbhcheck; | * The problem here is, rt->rt_ifp for fe80::%lo0/64 is set to lo0, | ||
* so any address under fe80::%lo0/64 will be mistakenly considered | |||
* local. The special case is supplied to handle the case properly | |||
* by actually looking at interface addresses | |||
* (using in6ifa_ifpwithaddr). | |||
*/ | |||
if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) != 0 && | |||
IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) { | |||
if (!in6ifa_ifpwithaddr(m->m_pkthdr.rcvif, &ip6->ip6_dst)) { | |||
icmp6_error(m, ICMP6_DST_UNREACH, | |||
ICMP6_DST_UNREACH_ADDR, 0); | |||
/* m is already freed */ | |||
return; | |||
} | } | ||
ours = 1; | |||
deliverifp = m->m_pkthdr.rcvif; | |||
goto hbhcheck; | |||
} | } | ||
/* | /* | ||
|
|
||
* Unicast check | * Unicast check | ||
*/ | */ | ||
if (ip6_forward_rt.ro_rt != NULL && | if (ip6_forward_rt.ro_rt != NULL && | ||
(ip6_forward_rt.ro_rt->rt_flags & RTF_UP) != 0 && | (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) != 0 && | ||
IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, | IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, | ||
&ip6_forward_rt.ro_dst.sin6_addr)) | &((struct sockaddr_in6 *)(&ip6_forward_rt.ro_dst))->sin6_addr)) | ||
ip6stat.ip6s_forward_cachehit++; | ip6stat.ip6s_forward_cachehit++; | ||
else { | else { | ||
struct sockaddr_in6 *dst6; | |||
if (ip6_forward_rt.ro_rt) { | if (ip6_forward_rt.ro_rt) { | ||
/* route is down or destination is different */ | /* route is down or destination is different */ | ||
ip6stat.ip6s_forward_cachemiss++; | ip6stat.ip6s_forward_cachemiss++; | ||
|
|
||
} | } | ||
bzero(&ip6_forward_rt.ro_dst, sizeof(struct sockaddr_in6)); | bzero(&ip6_forward_rt.ro_dst, sizeof(struct sockaddr_in6)); | ||
ip6_forward_rt.ro_dst.sin6_len = sizeof(struct sockaddr_in6); | dst6 = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst; | ||
ip6_forward_rt.ro_dst.sin6_family = AF_INET6; | dst6->sin6_len = sizeof(struct sockaddr_in6); | ||
ip6_forward_rt.ro_dst.sin6_addr = ip6->ip6_dst; | dst6->sin6_family = AF_INET6; | ||
dst6->sin6_addr = ip6->ip6_dst; | |||
rtalloc((struct route *)&ip6_forward_rt); | rtalloc((struct route *)&ip6_forward_rt); | ||
} | } | ||
|
|
||
if (ip6_forward_rt.ro_rt && | if (ip6_forward_rt.ro_rt && | ||
(ip6_forward_rt.ro_rt->rt_flags & | (ip6_forward_rt.ro_rt->rt_flags & | ||
(RTF_HOST|RTF_GATEWAY)) == RTF_HOST && | (RTF_HOST|RTF_GATEWAY)) == RTF_HOST && | ||
!(ip6_forward_rt.ro_rt->rt_flags & RTF_CLONED) && | |||
#if 0 | #if 0 | ||
/* | /* | ||
* The check below is redundant since the comparison of | * The check below is redundant since the comparison of | ||
|
|
||
* already done through looking up the routing table. | * already done through looking up the routing table. | ||
*/ | */ | ||
IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, | IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, | ||
&rt6_key(ip6_forward_rt.ro_rt)->sin6_addr) && | &rt6_key(ip6_forward_rt.ro_rt)->sin6_addr) && | ||
#endif | #endif | ||
ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_LOOP) { | ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_LOOP) { | ||
struct in6_ifaddr *ia6 = | struct in6_ifaddr *ia6 = | ||
|
|
||
goto hbhcheck; | goto hbhcheck; | ||
} else { | } else { | ||
/* address is not ready, so discard the packet. */ | /* address is not ready, so discard the packet. */ | ||
log(LOG_INFO, | nd6log((LOG_INFO, | ||
"ip6_input: packet to an unready address %s->%s\n", | "ip6_input: packet to an unready address %s->%s\n", | ||
ip6_sprintf(&ip6->ip6_src), | ip6_sprintf(&ip6->ip6_src), | ||
ip6_sprintf(&ip6->ip6_dst)); | ip6_sprintf(&ip6->ip6_dst))); | ||
goto bad; | goto bad; | ||
} | } | ||
} | } | ||
/* | /* | ||
* FAITH(Firewall Aided Internet Translator) | * FAITH (Firewall Aided Internet Translator) | ||
*/ | */ | ||
#if defined(NFAITH) && 0 < NFAITH | #if defined(NFAITH) && 0 < NFAITH | ||
if (ip6_keepfaith) { | if (ip6_keepfaith) { | ||
if (ip6_forward_rt.ro_rt && ip6_forward_rt.ro_rt->rt_ifp | if (ip6_forward_rt.ro_rt && ip6_forward_rt.ro_rt->rt_ifp && | ||
&& ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_FAITH) { | ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_FAITH) { | ||
/* XXX do we need more sanity checks? */ | /* XXX do we need more sanity checks? */ | ||
ours = 1; | ours = 1; | ||
deliverifp = ip6_forward_rt.ro_rt->rt_ifp; /*faith*/ | deliverifp = ip6_forward_rt.ro_rt->rt_ifp; /* faith */ | ||
goto hbhcheck; | goto hbhcheck; | ||
} | } | ||
} | } | ||
|
|
||
ip6 = mtod(m, struct ip6_hdr *); | ip6 = mtod(m, struct ip6_hdr *); | ||
/* | /* | ||
* if the payload length field is 0 and the next header field | * if the payload length field is 0 and the next header field | ||
* indicates Hop-by-Hop Options header, then a Jumbo Payload | * indicates Hop-by-Hop Options header, then a Jumbo Payload | ||
* option MUST be included. | * option MUST be included. | ||
*/ | */ | ||
|
|
||
/* | /* | ||
* Note that if a valid jumbo payload option is | * Note that if a valid jumbo payload option is | ||
* contained, ip6_hoptops_input() must set a valid | * contained, ip6_hoptops_input() must set a valid | ||
* (non-zero) payload length to the variable plen. | * (non-zero) payload length to the variable plen. | ||
*/ | */ | ||
ip6stat.ip6s_badoptions++; | ip6stat.ip6s_badoptions++; | ||
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); | in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); | ||
|
|
||
(caddr_t)&ip6->ip6_plen - (caddr_t)ip6); | (caddr_t)&ip6->ip6_plen - (caddr_t)ip6); | ||
return; | return; | ||
} | } | ||
#ifndef PULLDOWN_TEST | |||
/* ip6_hopopts_input() ensures that mbuf is contiguous */ | |||
hbh = (struct ip6_hbh *)(ip6 + 1); | |||
#else | |||
IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr), | IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr), | ||
sizeof(struct ip6_hbh)); | sizeof(struct ip6_hbh)); | ||
if (hbh == NULL) { | if (hbh == NULL) { | ||
ip6stat.ip6s_tooshort++; | ip6stat.ip6s_tooshort++; | ||
return; | return; | ||
} | } | ||
#endif | KASSERT(IP6_HDR_ALIGNED_P(hbh)); | ||
nxt = hbh->ip6h_nxt; | nxt = hbh->ip6h_nxt; | ||
/* | /* | ||
|
|
||
return; | return; | ||
} | } | ||
} else if (!ours) { | } else if (!ours) { | ||
ip6_forward(m, 0); | ip6_forward(m, srcrt); | ||
return; | return; | ||
} | } | ||
ip6 = mtod(m, struct ip6_hdr *); | ip6 = mtod(m, struct ip6_hdr *); | ||
|
|
||
ip6stat.ip6s_delivered++; | ip6stat.ip6s_delivered++; | ||
in6_ifstat_inc(deliverifp, ifs6_in_deliver); | in6_ifstat_inc(deliverifp, ifs6_in_deliver); | ||
nest = 0; | nest = 0; | ||
while (nxt != IPPROTO_DONE) { | while (nxt != IPPROTO_DONE) { | ||
if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) { | if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) { | ||
ip6stat.ip6s_toomanyhdr++; | ip6stat.ip6s_toomanyhdr++; | ||
|
|
||
goto bad; | goto bad; | ||
} | } | ||
#ifdef IPSEC | |||
/* | |||
* enforce IPsec policy checking if we are seeing last header. | |||
* note that we do not visit this with protocols with pcb layer | |||
* code - like udp/tcp/raw ip. | |||
*/ | |||
if ((inet6sw[ip6_protox[nxt]].pr_flags & PR_LASTHDR) != 0 && | |||
ipsec6_in_reject(m, NULL)) { | |||
ipsec6stat.in_polvio++; | |||
goto bad; | |||
} | |||
#endif | |||
nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt); | nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt); | ||
} | } | ||
return; | return; | ||
|
|
||
struct mbuf **mp; | struct mbuf **mp; | ||
int *offp; | int *offp; | ||
{ | { | ||
register struct mbuf *m = *mp; | struct mbuf *m = *mp; | ||
int off = *offp, hbhlen; | int off = *offp, hbhlen; | ||
struct ip6_hbh *hbh; | struct ip6_hbh *hbh; | ||
u_int8_t *opt; | |||
/* validation of the length of the header */ | /* validation of the length of the header */ | ||
#ifndef PULLDOWN_TEST | |||
IP6_EXTHDR_CHECK(m, off, sizeof(*hbh), -1); | |||
hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off); | |||
hbhlen = (hbh->ip6h_len + 1) << 3; | |||
IP6_EXTHDR_CHECK(m, off, hbhlen, -1); | |||
hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off); | |||
#else | |||
IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, | IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, | ||
sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); | sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); | ||
if (hbh == NULL) { | if (hbh == NULL) { | ||
|
|
||
ip6stat.ip6s_tooshort++; | ip6stat.ip6s_tooshort++; | ||
return -1; | return -1; | ||
} | } | ||
#endif | KASSERT(IP6_HDR_ALIGNED_P(hbh)); | ||
off += hbhlen; | off += hbhlen; | ||
hbhlen -= sizeof(struct ip6_hbh); | hbhlen -= sizeof(struct ip6_hbh); | ||
opt = (u_int8_t *)hbh + sizeof(struct ip6_hbh); | |||
if (ip6_process_hopopts(m, (u_int8_t *)hbh + sizeof(struct ip6_hbh), | if (ip6_process_hopopts(m, (u_int8_t *)hbh + sizeof(struct ip6_hbh), | ||
hbhlen, rtalertp, plenp) < 0) | hbhlen, rtalertp, plenp) < 0) | ||
return(-1); | return (-1); | ||
*offp = off; | *offp = off; | ||
*mp = m; | *mp = m; | ||
return(0); | return (0); | ||
} | } | ||
/* | /* | ||
|
|
||
* This function is separate from ip6_hopopts_input() in order to | * This function is separate from ip6_hopopts_input() in order to | ||
* handle a case where the sending node itself process its hop-by-hop | * handle a case where the sending node itself process its hop-by-hop | ||
* options header. In such a case, the function is called from ip6_output(). | * options header. In such a case, the function is called from ip6_output(). | ||
* | |||
* The function assumes that hbh header is located right after the IPv6 header | |||
* (RFC2460 p7), opthead is pointer into data content in m, and opthead to | |||
* opthead + hbhlen is located in continuous memory region. | |||
*/ | */ | ||
int | int | ||
ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) | ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) | ||
|
|
||
u_int8_t *opt = opthead; | u_int8_t *opt = opthead; | ||
u_int16_t rtalert_val; | u_int16_t rtalert_val; | ||
u_int32_t jumboplen; | u_int32_t jumboplen; | ||
const int erroff = sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh); | |||
for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) { | for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) { | ||
switch(*opt) { | switch (*opt) { | ||
case IP6OPT_PAD1: | case IP6OPT_PAD1: | ||
optlen = 1; | optlen = 1; | ||
break; | break; | ||
case IP6OPT_PADN: | case IP6OPT_PADN: | ||
if (hbhlen < IP6OPT_MINLEN) { | if (hbhlen < IP6OPT_MINLEN) { | ||
ip6stat.ip6s_toosmall++; | ip6stat.ip6s_toosmall++; | ||
goto bad; | goto bad; | ||
} | } | ||
optlen = *(opt + 1) + 2; | optlen = *(opt + 1) + 2; | ||
break; | break; | ||
case IP6OPT_RTALERT: | case IP6OPT_RTALERT: | ||
/* XXX may need check for alignment */ | /* XXX may need check for alignment */ | ||
if (hbhlen < IP6OPT_RTALERT_LEN) { | if (hbhlen < IP6OPT_RTALERT_LEN) { | ||
ip6stat.ip6s_toosmall++; | ip6stat.ip6s_toosmall++; | ||
goto bad; | goto bad; | ||
} | } | ||
if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) | if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) { | ||
/* XXX: should we discard the packet? */ | /* XXX stat */ | ||
log(LOG_ERR, "length of router alert opt is inconsitent(%d)", | icmp6_error(m, ICMP6_PARAM_PROB, | ||
*(opt + 1)); | ICMP6_PARAMPROB_HEADER, | ||
optlen = IP6OPT_RTALERT_LEN; | erroff + opt + 1 - opthead); | ||
bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2); | return (-1); | ||
*rtalertp = ntohs(rtalert_val); | } | ||
break; | optlen = IP6OPT_RTALERT_LEN; | ||
case IP6OPT_JUMBO: | bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2); | ||
*rtalertp = ntohs(rtalert_val); | |||
break; | |||
case IP6OPT_JUMBO: | |||
/* XXX may need check for alignment */ | /* XXX may need check for alignment */ | ||
if (hbhlen < IP6OPT_JUMBO_LEN) { | if (hbhlen < IP6OPT_JUMBO_LEN) { | ||
ip6stat.ip6s_toosmall++; | ip6stat.ip6s_toosmall++; | ||
goto bad; | goto bad; | ||
} | } | ||
if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) | if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) { | ||
/* XXX: should we discard the packet? */ | /* XXX stat */ | ||
log(LOG_ERR, "length of jumbopayload opt " | icmp6_error(m, ICMP6_PARAM_PROB, | ||
"is inconsistent(%d)\n", | ICMP6_PARAMPROB_HEADER, | ||
*(opt + 1)); | erroff + opt + 1 - opthead); | ||
return (-1); | |||
} | |||
optlen = IP6OPT_JUMBO_LEN; | optlen = IP6OPT_JUMBO_LEN; | ||
/* | /* | ||
* IPv6 packets that have non 0 payload length | * IPv6 packets that have non 0 payload length | ||
* must not contain a jumbo paylod option. | * must not contain a jumbo payload option. | ||
*/ | */ | ||
ip6 = mtod(m, struct ip6_hdr *); | ip6 = mtod(m, struct ip6_hdr *); | ||
if (ip6->ip6_plen) { | if (ip6->ip6_plen) { | ||
ip6stat.ip6s_badoptions++; | ip6stat.ip6s_badoptions++; | ||
icmp6_error(m, ICMP6_PARAM_PROB, | icmp6_error(m, ICMP6_PARAM_PROB, | ||
ICMP6_PARAMPROB_HEADER, | ICMP6_PARAMPROB_HEADER, | ||
sizeof(struct ip6_hdr) + | erroff + opt - opthead); | ||
sizeof(struct ip6_hbh) + | return (-1); | ||
opt - opthead); | |||
return(-1); | |||
} | } | ||
/* | /* | ||
|
|
||
if (*plenp != 0) { | if (*plenp != 0) { | ||
ip6stat.ip6s_badoptions++; | ip6stat.ip6s_badoptions++; | ||
icmp6_error(m, ICMP6_PARAM_PROB, | icmp6_error(m, ICMP6_PARAM_PROB, | ||
ICMP6_PARAMPROB_HEADER, | ICMP6_PARAMPROB_HEADER, | ||
sizeof(struct ip6_hdr) + | erroff + opt + 2 - opthead); | ||
sizeof(struct ip6_hbh) + | return (-1); | ||
opt + 2 - opthead); | |||
return(-1); | |||
} | } | ||
#endif | #endif | ||
|
|
||
if (jumboplen <= IPV6_MAXPACKET) { | if (jumboplen <= IPV6_MAXPACKET) { | ||
ip6stat.ip6s_badoptions++; | ip6stat.ip6s_badoptions++; | ||
icmp6_error(m, ICMP6_PARAM_PROB, | icmp6_error(m, ICMP6_PARAM_PROB, | ||
ICMP6_PARAMPROB_HEADER, | ICMP6_PARAMPROB_HEADER, | ||
sizeof(struct ip6_hdr) + | erroff + opt + 2 - opthead); | ||
sizeof(struct ip6_hbh) + | return (-1); | ||
opt + 2 - opthead); | |||
return(-1); | |||
} | } | ||
*plenp = jumboplen; | *plenp = jumboplen; | ||
break; | break; | ||
default: /* unknown option */ | default: /* unknown option */ | ||
if (hbhlen < IP6OPT_MINLEN) { | if (hbhlen < IP6OPT_MINLEN) { | ||
ip6stat.ip6s_toosmall++; | ip6stat.ip6s_toosmall++; | ||
goto bad; | goto bad; | ||
} | } | ||
if ((optlen = ip6_unknown_opt(opt, m, | optlen = ip6_unknown_opt(opt, m, | ||
sizeof(struct ip6_hdr) + | erroff + opt - opthead); | ||
sizeof(struct ip6_hbh) + | if (optlen == -1) | ||
opt - opthead)) == -1) | return (-1); | ||
return(-1); | optlen += 2; | ||
optlen += 2; | break; | ||
break; | |||
} | } | ||
} | } | ||
return(0); | return (0); | ||
bad: | bad: | ||
m_freem(m); | m_freem(m); | ||
return(-1); | return (-1); | ||
} | } | ||
/* | /* | ||
|
|
||
{ | { | ||
struct ip6_hdr *ip6; | struct ip6_hdr *ip6; | ||
switch(IP6OPT_TYPE(*optp)) { | switch (IP6OPT_TYPE(*optp)) { | ||
case IP6OPT_TYPE_SKIP: /* ignore the option */ | case IP6OPT_TYPE_SKIP: /* ignore the option */ | ||
return((int)*(optp + 1)); | return ((int)*(optp + 1)); | ||
case IP6OPT_TYPE_DISCARD: /* silently discard */ | case IP6OPT_TYPE_DISCARD: /* silently discard */ | ||
m_freem(m); | m_freem(m); | ||
return(-1); | return (-1); | ||
case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */ | case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */ | ||
ip6stat.ip6s_badoptions++; | ip6stat.ip6s_badoptions++; | ||
icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off); | icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off); | ||
return(-1); | return (-1); | ||
case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */ | case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */ | ||
ip6stat.ip6s_badoptions++; | ip6stat.ip6s_badoptions++; | ||
ip6 = mtod(m, struct ip6_hdr *); | ip6 = mtod(m, struct ip6_hdr *); | ||
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || | if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || | ||
(m->m_flags & (M_BCAST|M_MCAST))) | (m->m_flags & (M_BCAST|M_MCAST))) | ||
m_freem(m); | m_freem(m); | ||
else | else | ||
icmp6_error(m, ICMP6_PARAM_PROB, | icmp6_error(m, ICMP6_PARAM_PROB, | ||
ICMP6_PARAMPROB_OPTION, off); | ICMP6_PARAMPROB_OPTION, off); | ||
return(-1); | return (-1); | ||
} | } | ||
m_freem(m); /* XXX: NOTREACHED */ | m_freem(m); /* XXX: NOTREACHED */ | ||
return(-1); | return (-1); | ||
} | } | ||
/* | /* | ||
|
|
||
*/ | */ | ||
void | void | ||
ip6_savecontrol(in6p, mp, ip6, m) | ip6_savecontrol(in6p, mp, ip6, m) | ||
register struct in6pcb *in6p; | struct in6pcb *in6p; | ||
register struct mbuf **mp; | struct mbuf **mp; | ||
register struct ip6_hdr *ip6; | struct ip6_hdr *ip6; | ||
register struct mbuf *m; | struct mbuf *m; | ||
{ | { | ||
struct proc *p = curproc; /* XXX */ | |||
int privileged; | |||
privileged = 0; | |||
if (p && !suser(p->p_ucred, &p->p_acflag)) | |||
privileged++; | |||
#ifdef SO_TIMESTAMP | #ifdef SO_TIMESTAMP | ||
if (in6p->in6p_socket->so_options & SO_TIMESTAMP) { | if (in6p->in6p_socket->so_options & SO_TIMESTAMP) { | ||
|
|
||
microtime(&tv); | microtime(&tv); | ||
*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv), | *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv), | ||
SCM_TIMESTAMP, SOL_SOCKET); | SCM_TIMESTAMP, SOL_SOCKET); | ||
if (*mp) | if (*mp) | ||
mp = &(*mp)->m_next; | mp = &(*mp)->m_next; | ||
} | } | ||
#endif | #endif | ||
if (in6p->in6p_flags & IN6P_RECVDSTADDR) { | if (in6p->in6p_flags & IN6P_RECVDSTADDR) { | ||
*mp = sbcreatecontrol((caddr_t) &ip6->ip6_dst, | *mp = sbcreatecontrol((caddr_t) &ip6->ip6_dst, | ||
sizeof(struct in6_addr), IPV6_RECVDSTADDR, | sizeof(struct in6_addr), IPV6_RECVDSTADDR, IPPROTO_IPV6); | ||
IPPROTO_IPV6); | |||
if (*mp) | if (*mp) | ||
mp = &(*mp)->m_next; | mp = &(*mp)->m_next; | ||
} | } | ||
|
|
||
#endif | #endif | ||
/* RFC 2292 sec. 5 */ | /* RFC 2292 sec. 5 */ | ||
if (in6p->in6p_flags & IN6P_PKTINFO) { | if ((in6p->in6p_flags & IN6P_PKTINFO) != 0) { | ||
struct in6_pktinfo pi6; | struct in6_pktinfo pi6; | ||
bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr)); | bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr)); | ||
if (IN6_IS_SCOPE_LINKLOCAL(&pi6.ipi6_addr)) | if (IN6_IS_SCOPE_LINKLOCAL(&pi6.ipi6_addr)) | ||
|
|
||
? m->m_pkthdr.rcvif->if_index | ? m->m_pkthdr.rcvif->if_index | ||
: 0; | : 0; | ||
*mp = sbcreatecontrol((caddr_t) &pi6, | *mp = sbcreatecontrol((caddr_t) &pi6, | ||
sizeof(struct in6_pktinfo), IPV6_PKTINFO, | sizeof(struct in6_pktinfo), IPV6_PKTINFO, IPPROTO_IPV6); | ||
IPPROTO_IPV6); | |||
if (*mp) | if (*mp) | ||
mp = &(*mp)->m_next; | mp = &(*mp)->m_next; | ||
} | } | ||
if (in6p->in6p_flags & IN6P_HOPLIMIT) { | if (in6p->in6p_flags & IN6P_HOPLIMIT) { | ||
int hlim = ip6->ip6_hlim & 0xff; | int hlim = ip6->ip6_hlim & 0xff; | ||
*mp = sbcreatecontrol((caddr_t) &hlim, | *mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int), | ||
sizeof(int), IPV6_HOPLIMIT, IPPROTO_IPV6); | IPV6_HOPLIMIT, IPPROTO_IPV6); | ||
if (*mp) | if (*mp) | ||
mp = &(*mp)->m_next; | mp = &(*mp)->m_next; | ||
} | } | ||
/* IN6P_NEXTHOP - for outgoing packet only */ | /* IN6P_NEXTHOP - for outgoing packet only */ | ||
/* | /* | ||
* IPV6_HOPOPTS socket option. We require super-user privilege | * IPV6_HOPOPTS socket option. Recall that we required super-user | ||
* for the option, but it might be too strict, since there might | * privilege for the option (see ip6_ctloutput), but it might be too | ||
* be some hop-by-hop options which can be returned to normal user. | * strict, since there might be some hop-by-hop options which can be | ||
* See RFC 2292 section 6. | * returned to normal user. | ||
* See also RFC 2292 section 6. | |||
*/ | */ | ||
if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) { | if ((in6p->in6p_flags & IN6P_HOPOPTS) != 0) { | ||
/* | /* | ||
* Check if a hop-by-hop options header is contatined in the | * Check if a hop-by-hop options header is contatined in the | ||
* received packet, and if so, store the options as ancillary | * received packet, and if so, store the options as ancillary | ||
|
|
||
if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { | if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { | ||
struct ip6_hbh *hbh; | struct ip6_hbh *hbh; | ||
int hbhlen; | int hbhlen; | ||
struct mbuf *ext; | |||
#ifndef PULLDOWN_TEST | ext = ip6_pullexthdr(m, sizeof(struct ip6_hdr), | ||
hbh = (struct ip6_hbh *)(ip6 + 1); | ip6->ip6_nxt); | ||
hbhlen = (hbh->ip6h_len + 1) << 3; | if (ext == NULL) { | ||
#else | |||
IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, | |||
sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); | |||
if (hbh == NULL) { | |||
ip6stat.ip6s_tooshort++; | ip6stat.ip6s_tooshort++; | ||
return; | return; | ||
} | } | ||
hbh = mtod(ext, struct ip6_hbh *); | |||
hbhlen = (hbh->ip6h_len + 1) << 3; | hbhlen = (hbh->ip6h_len + 1) << 3; | ||
IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, | if (hbhlen != ext->m_len) { | ||
sizeof(struct ip6_hdr), hbhlen); | m_freem(ext); | ||
if (hbh == NULL) { | |||
ip6stat.ip6s_tooshort++; | ip6stat.ip6s_tooshort++; | ||
return; | return; | ||
} | } | ||
#endif | |||
/* | /* | ||
* XXX: We copy whole the header even if a jumbo | * XXX: We copy whole the header even if a jumbo | ||
|
|
||
* But it's too painful operation... | * But it's too painful operation... | ||
*/ | */ | ||
*mp = sbcreatecontrol((caddr_t)hbh, hbhlen, | *mp = sbcreatecontrol((caddr_t)hbh, hbhlen, | ||
IPV6_HOPOPTS, IPPROTO_IPV6); | IPV6_HOPOPTS, IPPROTO_IPV6); | ||
if (*mp) | if (*mp) | ||
mp = &(*mp)->m_next; | mp = &(*mp)->m_next; | ||
m_freem(ext); | |||
} | } | ||
} | } | ||
/* IPV6_DSTOPTS and IPV6_RTHDR socket options */ | /* IPV6_DSTOPTS and IPV6_RTHDR socket options */ | ||
if (in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDR)) { | if (in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDR)) { | ||
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); | ||
int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr);; | int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr); | ||
/* | /* | ||
* Search for destination options headers or routing | * Search for destination options headers or routing | ||
|
|
||
* Note that the order of the headers remains in | * Note that the order of the headers remains in | ||
* the chain of ancillary data. | * the chain of ancillary data. | ||
*/ | */ | ||
while(1) { /* is explicit loop prevention necessary? */ | while (1) { /* is explicit loop prevention necessary? */ | ||
struct ip6_ext *ip6e; | struct ip6_ext *ip6e = NULL; | ||
int elen; | int elen; | ||
struct mbuf *ext = NULL; | |||
#ifndef PULLDOWN_TEST | /* | ||
ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); | * if it is not an extension header, don't try to | ||
if (nxt == IPPROTO_AH) | * pull it from the chain. | ||
elen = (ip6e->ip6e_len + 2) << 2; | */ | ||
else | switch (nxt) { | ||
elen = (ip6e->ip6e_len + 1) << 3; | case IPPROTO_DSTOPTS: | ||
#else | case IPPROTO_ROUTING: | ||
IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off, | case IPPROTO_HOPOPTS: | ||
sizeof(struct ip6_ext)); | case IPPROTO_AH: /* is it possible? */ | ||
if (ip6e == NULL) { | break; | ||
default: | |||
goto loopend; | |||
} | |||
ext = ip6_pullexthdr(m, off, nxt); | |||
if (ext == NULL) { | |||
ip6stat.ip6s_tooshort++; | ip6stat.ip6s_tooshort++; | ||
return; | return; | ||
} | } | ||
ip6e = mtod(ext, struct ip6_ext *); | |||
if (nxt == IPPROTO_AH) | if (nxt == IPPROTO_AH) | ||
elen = (ip6e->ip6e_len + 2) << 2; | elen = (ip6e->ip6e_len + 2) << 2; | ||
else | else | ||
elen = (ip6e->ip6e_len + 1) << 3; | elen = (ip6e->ip6e_len + 1) << 3; | ||
IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off, elen); | if (elen != ext->m_len) { | ||
if (ip6e == NULL) { | m_freem(ext); | ||
ip6stat.ip6s_tooshort++; | ip6stat.ip6s_tooshort++; | ||
return; | return; | ||
} | } | ||
#endif | KASSERT(IP6_HDR_ALIGNED_P(ip6e)); | ||
switch (nxt) { | |||
case IPPROTO_DSTOPTS: | |||
if (!in6p->in6p_flags & IN6P_DSTOPTS) | |||
break; | |||
*mp = sbcreatecontrol((caddr_t)ip6e, elen, | |||
IPV6_DSTOPTS, IPPROTO_IPV6); | |||
if (*mp) | |||
mp = &(*mp)->m_next; | |||
break; | |||
case IPPROTO_ROUTING: | |||
if (!in6p->in6p_flags & IN6P_RTHDR) | |||
break; | |||
*mp = sbcreatecontrol((caddr_t)ip6e, elen, | |||
IPV6_RTHDR, IPPROTO_IPV6); | |||
if (*mp) | |||
mp = &(*mp)->m_next; | |||
break; | |||
case IPPROTO_HOPOPTS: | |||
case IPPROTO_AH: /* is it possible? */ | |||
break; | |||
default: | |||
/* | |||
* other cases have been filtered in the above. | |||
* none will visit this case. here we supply | |||
* the code just in case (nxt overwritten or | |||
* other cases). | |||
*/ | |||
m_freem(ext); | |||
goto loopend; | |||
switch(nxt) { | |||
case IPPROTO_DSTOPTS: | |||
if (!in6p->in6p_flags & IN6P_DSTOPTS) | |||
break; | |||
/* | |||
* We also require super-user privilege for | |||
* the option. | |||
* See the comments on IN6_HOPOPTS. | |||
*/ | |||
if (!privileged) | |||
break; | |||
*mp = sbcreatecontrol((caddr_t)ip6e, elen, | |||
IPV6_DSTOPTS, | |||
IPPROTO_IPV6); | |||
if (*mp) | |||
mp = &(*mp)->m_next; | |||
break; | |||
case IPPROTO_ROUTING: | |||
if (!in6p->in6p_flags & IN6P_RTHDR) | |||
break; | |||
*mp = sbcreatecontrol((caddr_t)ip6e, elen, | |||
IPV6_RTHDR, | |||
IPPROTO_IPV6); | |||
if (*mp) | |||
mp = &(*mp)->m_next; | |||
break; | |||
case IPPROTO_UDP: | |||
case IPPROTO_TCP: | |||
case IPPROTO_ICMPV6: | |||
default: | |||
/* | |||
* stop search if we encounter an upper | |||
* layer protocol headers. | |||
*/ | |||
goto loopend; | |||
case IPPROTO_HOPOPTS: | |||
case IPPROTO_AH: /* is it possible? */ | |||
break; | |||
} | } | ||
/* proceed with the next header. */ | /* proceed with the next header. */ | ||
off += elen; | off += elen; | ||
nxt = ip6e->ip6e_nxt; | nxt = ip6e->ip6e_nxt; | ||
ip6e = NULL; | |||
m_freem(ext); | |||
ext = NULL; | |||
} | } | ||
loopend: | loopend: | ||
; | |||
} | |||
} | |||
/* | |||
* pull single extension header from mbuf chain. returns single mbuf that | |||
* contains the result, or NULL on error. | |||
*/ | |||
static struct mbuf * | |||
ip6_pullexthdr(m, off, nxt) | |||
struct mbuf *m; | |||
size_t off; | |||
int nxt; | |||
{ | |||
struct ip6_ext ip6e; | |||
size_t elen; | |||
struct mbuf *n; | |||
#ifdef DIAGNOSTIC | |||
switch (nxt) { | |||
case IPPROTO_DSTOPTS: | |||
case IPPROTO_ROUTING: | |||
case IPPROTO_HOPOPTS: | |||
case IPPROTO_AH: /* is it possible? */ | |||
break; | |||
default: | |||
printf("ip6_pullexthdr: invalid nxt=%d\n", nxt); | |||
} | } | ||
if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) { | #endif | ||
/* to be done */ | |||
m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e); | |||
if (nxt == IPPROTO_AH) | |||
elen = (ip6e.ip6e_len + 2) << 2; | |||
else | |||
elen = (ip6e.ip6e_len + 1) << 3; | |||
MGET(n, M_DONTWAIT, MT_DATA); | |||
if (n && elen >= MLEN) { | |||
MCLGET(n, M_DONTWAIT); | |||
if ((n->m_flags & M_EXT) == 0) { | |||
m_free(n); | |||
n = NULL; | |||
} | |||
} | } | ||
if ((in6p->in6p_flags & IN6P_DSTOPTS) && privileged) { | if (!n) | ||
/* to be done */ | return NULL; | ||
n->m_len = 0; | |||
if (elen >= M_TRAILINGSPACE(n)) { | |||
m_free(n); | |||
return NULL; | |||
} | } | ||
/* IN6P_RTHDR - to be done */ | |||
m_copydata(m, off, elen, mtod(n, caddr_t)); | |||
n->m_len = elen; | |||
return n; | |||
} | } | ||
/* | /* | ||
|
|
||
* carefully. Moreover, it will not be used in the near future when | * carefully. Moreover, it will not be used in the near future when | ||
* we develop `neater' mechanism to process extension headers. | * we develop `neater' mechanism to process extension headers. | ||
*/ | */ | ||
char * | u_int8_t * | ||
ip6_get_prevhdr(m, off) | ip6_get_prevhdr(m, off) | ||
struct mbuf *m; | struct mbuf *m; | ||
int off; | int off; | ||
|
|
||
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); | ||
if (off == sizeof(struct ip6_hdr)) | if (off == sizeof(struct ip6_hdr)) | ||
return(&ip6->ip6_nxt); | return (&ip6->ip6_nxt); | ||
else { | else { | ||
int len, nxt; | int len, nxt; | ||
struct ip6_ext *ip6e = NULL; | struct ip6_ext *ip6e = NULL; | ||
|
|
||
while (len < off) { | while (len < off) { | ||
ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + len); | ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + len); | ||
switch(nxt) { | switch (nxt) { | ||
case IPPROTO_FRAGMENT: | case IPPROTO_FRAGMENT: | ||
len += sizeof(struct ip6_frag); | len += sizeof(struct ip6_frag); | ||
break; | break; | ||
|
|
||
nxt = ip6e->ip6e_nxt; | nxt = ip6e->ip6e_nxt; | ||
} | } | ||
if (ip6e) | if (ip6e) | ||
return(&ip6e->ip6e_nxt); | return (&ip6e->ip6e_nxt); | ||
else | else | ||
return NULL; | return NULL; | ||
} | } | ||
|
|
||
if (m->m_pkthdr.len < off + sizeof(fh)) | if (m->m_pkthdr.len < off + sizeof(fh)) | ||
return -1; | return -1; | ||
m_copydata(m, off, sizeof(fh), (caddr_t)&fh); | m_copydata(m, off, sizeof(fh), (caddr_t)&fh); | ||
if ((ntohs(fh.ip6f_offlg) & IP6F_OFF_MASK) != 0) | if ((fh.ip6f_offlg & IP6F_OFF_MASK) != 0) | ||
return -1; | return -1; | ||
if (nxtp) | if (nxtp) | ||
*nxtp = fh.ip6f_nxt; | *nxtp = fh.ip6f_nxt; | ||
|
|
||
if (nxtp) | if (nxtp) | ||
*nxtp = ip6e.ip6e_nxt; | *nxtp = ip6e.ip6e_nxt; | ||
off += (ip6e.ip6e_len + 2) << 2; | off += (ip6e.ip6e_len + 2) << 2; | ||
if (m->m_pkthdr.len < off) | |||
return -1; | |||
return off; | return off; | ||
case IPPROTO_HOPOPTS: | case IPPROTO_HOPOPTS: | ||
|
|
||
if (nxtp) | if (nxtp) | ||
*nxtp = ip6e.ip6e_nxt; | *nxtp = ip6e.ip6e_nxt; | ||
off += (ip6e.ip6e_len + 1) << 3; | off += (ip6e.ip6e_len + 1) << 3; | ||
if (m->m_pkthdr.len < off) | |||
return -1; | |||
return off; | return off; | ||
case IPPROTO_NONE: | case IPPROTO_NONE: | ||
|
|
||
default: | default: | ||
return -1; | return -1; | ||
} | } | ||
return -1; | |||
} | } | ||
/* | /* | ||
|
|
||
ENOPROTOOPT | ENOPROTOOPT | ||
}; | }; | ||
#include <uvm/uvm_extern.h> | SYSCTL_SETUP(sysctl_net_inet6_ip6_setup, "sysctl net.inet6.ip6 subtree setup") | ||
#include <sys/sysctl.h> | |||
int | |||
ip6_sysctl(name, namelen, oldp, oldlenp, newp, newlen) | |||
int *name; | |||
u_int namelen; | |||
void *oldp; | |||
size_t *oldlenp; | |||
void *newp; | |||
size_t newlen; | |||
{ | { | ||
int old, error; | |||
/* All sysctl names at this level are terminal. */ | sysctl_createv(clog, 0, NULL, NULL, | ||
if (namelen != 1) | CTLFLAG_PERMANENT, | ||
return ENOTDIR; | CTLTYPE_NODE, "net", NULL, | ||
NULL, 0, NULL, 0, | |||
switch (name[0]) { | CTL_NET, CTL_EOL); | ||
sysctl_createv(clog, 0, NULL, NULL, | |||
case IPV6CTL_FORWARDING: | CTLFLAG_PERMANENT, | ||
return sysctl_int(oldp, oldlenp, newp, newlen, | CTLTYPE_NODE, "inet6", | ||
&ip6_forwarding); | SYSCTL_DESCR("PF_INET6 related settings"), | ||
case IPV6CTL_SENDREDIRECTS: | NULL, 0, NULL, 0, | ||
return sysctl_int(oldp, oldlenp, newp, newlen, | CTL_NET, PF_INET6, CTL_EOL); | ||
&ip6_sendredirects); | sysctl_createv(clog, 0, NULL, NULL, | ||
case IPV6CTL_DEFHLIM: | CTLFLAG_PERMANENT, | ||
return sysctl_int(oldp, oldlenp, newp, newlen, &ip6_defhlim); | CTLTYPE_NODE, "ip6", | ||
case IPV6CTL_MAXFRAGPACKETS: | SYSCTL_DESCR("IPv6 related settings"), | ||
return sysctl_int(oldp, oldlenp, newp, newlen, | NULL, 0, NULL, 0, | ||
&ip6_maxfragpackets); | CTL_NET, PF_INET6, IPPROTO_IPV6, CTL_EOL); | ||
case IPV6CTL_ACCEPT_RTADV: | |||
return sysctl_int(oldp, oldlenp, newp, newlen, | sysctl_createv(clog, 0, NULL, NULL, | ||
&ip6_accept_rtadv); | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | ||
case IPV6CTL_KEEPFAITH: | CTLTYPE_INT, "forwarding", | ||
return sysctl_int(oldp, oldlenp, newp, newlen, &ip6_keepfaith); | SYSCTL_DESCR("Enable forwarding of INET6 datagrams"), | ||
case IPV6CTL_LOG_INTERVAL: | NULL, 0, &ip6_forwarding, 0, | ||
return sysctl_int(oldp, oldlenp, newp, newlen, | CTL_NET, PF_INET6, IPPROTO_IPV6, | ||
&ip6_log_interval); | IPV6CTL_FORWARDING, CTL_EOL); | ||
case IPV6CTL_HDRNESTLIMIT: | sysctl_createv(clog, 0, NULL, NULL, | ||
return sysctl_int(oldp, oldlenp, newp, newlen, | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | ||
&ip6_hdrnestlimit); | CTLTYPE_INT, "redirect", | ||
case IPV6CTL_DAD_COUNT: | SYSCTL_DESCR("Enable sending of ICMPv6 redirect messages"), | ||
return sysctl_int(oldp, oldlenp, newp, newlen, &ip6_dad_count); | NULL, 0, &ip6_sendredirects, 0, | ||
case IPV6CTL_AUTO_FLOWLABEL: | CTL_NET, PF_INET6, IPPROTO_IPV6, | ||
return sysctl_int(oldp, oldlenp, newp, newlen, | IPV6CTL_SENDREDIRECTS, CTL_EOL); | ||
&ip6_auto_flowlabel); | sysctl_createv(clog, 0, NULL, NULL, | ||
case IPV6CTL_DEFMCASTHLIM: | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | ||
return sysctl_int(oldp, oldlenp, newp, newlen, | CTLTYPE_INT, "hlim", | ||
&ip6_defmcasthlim); | SYSCTL_DESCR("Hop limit for an INET6 datagram"), | ||
case IPV6CTL_GIF_HLIM: | NULL, 0, &ip6_defhlim, 0, | ||
return sysctl_int(oldp, oldlenp, newp, newlen, | CTL_NET, PF_INET6, IPPROTO_IPV6, | ||
&ip6_gif_hlim); | IPV6CTL_DEFHLIM, CTL_EOL); | ||
case IPV6CTL_KAME_VERSION: | #ifdef notyet | ||
return sysctl_rdstring(oldp, oldlenp, newp, __KAME_VERSION); | sysctl_createv(clog, 0, NULL, NULL, | ||
case IPV6CTL_USE_DEPRECATED: | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | ||
return sysctl_int(oldp, oldlenp, newp, newlen, | CTLTYPE_INT, "mtu", NULL, | ||
&ip6_use_deprecated); | NULL, 0, &, 0, | ||
case IPV6CTL_RR_PRUNE: | CTL_NET, PF_INET6, IPPROTO_IPV6, | ||
return sysctl_int(oldp, oldlenp, newp, newlen, &ip6_rr_prune); | IPV6CTL_DEFMTU, CTL_EOL); | ||
#endif | |||
#ifdef __no_idea__ | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "forwsrcrt", NULL, | |||
NULL, 0, &?, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_FORWSRCRT, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_STRUCT, "stats", NULL, | |||
NULL, 0, &?, sizeof(?), | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_STATS, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_STRUCT, "mrtstats", NULL, | |||
NULL, 0, &?, sizeof(?), | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_MRTSTATS, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_?, "mrtproto", NULL, | |||
NULL, 0, &?, sizeof(?), | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_MRTPROTO, CTL_EOL); | |||
#endif | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "maxfragpackets", | |||
SYSCTL_DESCR("Maximum number of fragments to buffer " | |||
"for reassembly"), | |||
NULL, 0, &ip6_maxfragpackets, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_MAXFRAGPACKETS, CTL_EOL); | |||
#ifdef __no_idea__ | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "sourcecheck", NULL, | |||
NULL, 0, &?, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_SOURCECHECK, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "sourcecheck_logint", NULL, | |||
NULL, 0, &?, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_SOURCECHECK_LOGINT, CTL_EOL); | |||
#endif | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "accept_rtadv", | |||
SYSCTL_DESCR("Accept router advertisements"), | |||
NULL, 0, &ip6_accept_rtadv, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_ACCEPT_RTADV, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "keepfaith", | |||
SYSCTL_DESCR("Activate faith interface"), | |||
NULL, 0, &ip6_keepfaith, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_KEEPFAITH, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "log_interval", | |||
SYSCTL_DESCR("Minumum interval between logging " | |||
"unroutable packets"), | |||
NULL, 0, &ip6_log_interval, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_LOG_INTERVAL, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "hdrnestlimit", | |||
SYSCTL_DESCR("Maximum number of nested IPv6 headers"), | |||
NULL, 0, &ip6_hdrnestlimit, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_HDRNESTLIMIT, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "dad_count", | |||
SYSCTL_DESCR("Number of Duplicate Address Detection " | |||
"probes to send"), | |||
NULL, 0, &ip6_dad_count, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_DAD_COUNT, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "auto_flowlabel", | |||
SYSCTL_DESCR("Assign random IPv6 flow labels"), | |||
NULL, 0, &ip6_auto_flowlabel, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_AUTO_FLOWLABEL, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "defmcasthlim", | |||
SYSCTL_DESCR("Default multicast hop limit"), | |||
NULL, 0, &ip6_defmcasthlim, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_DEFMCASTHLIM, CTL_EOL); | |||
#if NGIF > 0 | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "gifhlim", | |||
SYSCTL_DESCR("Default hop limit for a gif tunnel datagram"), | |||
NULL, 0, &ip6_gif_hlim, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_GIF_HLIM, CTL_EOL); | |||
#endif /* NGIF */ | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT, | |||
CTLTYPE_STRING, "kame_version", | |||
SYSCTL_DESCR("KAME Version"), | |||
NULL, 0, __KAME_VERSION, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_KAME_VERSION, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "use_deprecated", | |||
SYSCTL_DESCR("Allow use of deprecated addresses as " | |||
"source addresses"), | |||
NULL, 0, &ip6_use_deprecated, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_USE_DEPRECATED, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "rr_prune", NULL, | |||
NULL, 0, &ip6_rr_prune, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_RR_PRUNE, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT | |||
#ifndef INET6_BINDV6ONLY | #ifndef INET6_BINDV6ONLY | ||
case IPV6CTL_BINDV6ONLY: | |CTLFLAG_READWRITE, | ||
return sysctl_int(oldp, oldlenp, newp, newlen, | |||
&ip6_bindv6only); | |||
#endif | |||
case IPV6CTL_ANONPORTMIN: | |||
old = ip6_anonportmin; | |||
error = sysctl_int(oldp, oldlenp, newp, newlen, | |||
&ip6_anonportmin); | |||
if (ip6_anonportmin >= ip6_anonportmax || ip6_anonportmin < 0 || | |||
ip6_anonportmin > 65535 | |||
#ifndef IPNOPRIVPORTS | |||
|| ip6_anonportmin < IPV6PORT_RESERVED | |||
#endif | |||
) { | |||
ip6_anonportmin = old; | |||
return (EINVAL); | |||
} | |||
return (error); | |||
case IPV6CTL_ANONPORTMAX: | |||
old = ip6_anonportmax; | |||
error = sysctl_int(oldp, oldlenp, newp, newlen, | |||
&ip6_anonportmax); | |||
if (ip6_anonportmin >= ip6_anonportmax || ip6_anonportmax < 0 || | |||
ip6_anonportmax > 65535 | |||
#ifndef IPNOPRIVPORTS | |||
|| ip6_anonportmax < IPV6PORT_RESERVED | |||
#endif | #endif | ||
) { | CTLTYPE_INT, "v6only", | ||
ip6_anonportmax = old; | SYSCTL_DESCR("Disallow PF_INET6 sockets from connecting " | ||
return (EINVAL); | "to PF_INET sockets"), | ||
} | NULL, 0, &ip6_v6only, 0, | ||
return (error); | CTL_NET, PF_INET6, IPPROTO_IPV6, | ||
IPV6CTL_V6ONLY, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "anonportmin", | |||
SYSCTL_DESCR("Lowest ephemeral port number to assign"), | |||
sysctl_net_inet_ip_ports, 0, &ip6_anonportmin, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_ANONPORTMIN, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "anonportmax", | |||
SYSCTL_DESCR("Highest ephemeral port number to assign"), | |||
sysctl_net_inet_ip_ports, 0, &ip6_anonportmax, 0, | |||
CTL_NET, PF_INET6, IPPROTO_IPV6, | |||
IPV6CTL_ANONPORTMAX, CTL_EOL); | |||
#ifndef IPNOPRIVPORTS | #ifndef IPNOPRIVPORTS | ||
case IPV6CTL_LOWPORTMIN: | sysctl_createv(clog, 0, NULL, NULL, | ||
old = ip6_lowportmin; | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | ||
error = sysctl_int(oldp, oldlenp, newp, newlen, | CTLTYPE_INT, "lowportmin", | ||
&ip6_lowportmin); | SYSCTL_DESCR("Lowest privileged ephemeral port number " | ||
if (ip6_lowportmin >= ip6_lowportmax || | "to assign"), | ||
ip6_lowportmin > IPV6PORT_RESERVEDMAX || | sysctl_net_inet_ip_ports, 0, &ip6_lowportmin, 0, | ||
ip6_lowportmin < IPV6PORT_RESERVEDMIN) { | CTL_NET, PF_INET6, IPPROTO_IPV6, | ||
ip6_lowportmin = old; | IPV6CTL_LOWPORTMIN, CTL_EOL); | ||
return (EINVAL); | sysctl_createv(clog, 0, NULL, NULL, | ||
} | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | ||
return (error); | CTLTYPE_INT, "lowportmax", | ||
case IPV6CTL_LOWPORTMAX: | SYSCTL_DESCR("Highest privileged ephemeral port number " | ||
old = ip6_lowportmax; | "to assign"), | ||
error = sysctl_int(oldp, oldlenp, newp, newlen, | sysctl_net_inet_ip_ports, 0, &ip6_lowportmax, 0, | ||
&ip6_lowportmax); | CTL_NET, PF_INET6, IPPROTO_IPV6, | ||
if (ip6_lowportmin >= ip6_lowportmax || | IPV6CTL_LOWPORTMAX, CTL_EOL); | ||
ip6_lowportmax > IPV6PORT_RESERVEDMAX || | #endif /* IPNOPRIVPORTS */ | ||
ip6_lowportmax < IPV6PORT_RESERVEDMIN) { | sysctl_createv(clog, 0, NULL, NULL, | ||
ip6_lowportmax = old; | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | ||
return (EINVAL); | CTLTYPE_INT, "maxfrags", | ||
} | SYSCTL_DESCR("Maximum fragments in reassembly queue"), | ||
return (error); | NULL, 0, &ip6_maxfrags, 0, | ||
#endif | CTL_NET, PF_INET6, IPPROTO_IPV6, | ||
default: | IPV6CTL_MAXFRAGS, CTL_EOL); | ||
return EOPNOTSUPP; | |||
} | |||
/* NOTREACHED */ | |||
} | } |