summaryrefslogtreecommitdiffstats
path: root/sys/netinet6/ip6_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6/ip6_input.c')
-rw-r--r--sys/netinet6/ip6_input.c833
1 files changed, 555 insertions, 278 deletions
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index 4745347..4b10d8e 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -1,5 +1,5 @@
/* $FreeBSD$ */
-/* $KAME: ip6_input.c,v 1.95 2000/07/02 07:49:37 jinmei Exp $ */
+/* $KAME: ip6_input.c,v 1.194 2001/05/27 13:28:35 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -73,6 +73,7 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/protosw.h>
@@ -108,6 +109,13 @@
#include <netinet6/nd6.h>
#include <netinet6/in6_prefix.h>
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif
+#endif
+
#include <netinet6/ip6_fw.h>
#include <netinet6/ip6protosw.h>
@@ -124,11 +132,16 @@ u_char ip6_protox[IPPROTO_MAX];
static int ip6qmaxlen = IFQ_MAXLEN;
struct in6_ifaddr *in6_ifaddr;
+extern struct callout in6_tmpaddrtimer_ch;
+
int ip6_forward_srcrt; /* XXX */
int ip6_sourcecheck; /* XXX */
int ip6_sourcecheck_interval; /* XXX */
const int int6intrq_present = 1;
+int ip6_ours_check_algorithm;
+
+
/* firewall hooks */
ip6_fw_chk_t *ip6_fw_chk_ptr;
ip6_fw_ctl_t *ip6_fw_ctl_ptr;
@@ -137,12 +150,14 @@ int ip6_fw_enable = 1;
struct ip6stat ip6stat;
static void ip6_init2 __P((void *));
+static struct mbuf *ip6_setdstifaddr __P((struct mbuf *, struct in6_ifaddr *));
static int ip6_hopopts_input __P((u_int32_t *, u_int32_t *, struct mbuf **, int *));
#ifdef PULLDOWN_TEST
static struct mbuf *ip6_pullexthdr __P((struct mbuf *, size_t, int));
#endif
+
/*
* IP6 initialization: fill in IP6 protocol switch table.
* All protocols not implemented in kernel go to raw IP6 protocol handler.
@@ -150,10 +165,14 @@ static struct mbuf *ip6_pullexthdr __P((struct mbuf *, size_t, int));
void
ip6_init()
{
- register struct ip6protosw *pr;
- register int i;
+ struct ip6protosw *pr;
+ int i;
struct timeval tv;
+#ifdef DIAGNOSTIC
+ if (sizeof(struct protosw) != sizeof(struct ip6protosw))
+ panic("sizeof(protosw) != sizeof(ip6protosw)");
+#endif
pr = (struct ip6protosw *)pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW);
if (pr == 0)
panic("ip6_init");
@@ -175,6 +194,8 @@ ip6_init()
*/
microtime(&tv);
ip6_flow_seq = random() ^ tv.tv_usec;
+ microtime(&tv);
+ ip6_desync_factor = (random() ^ tv.tv_usec) % MAX_TEMP_DESYNC_FACTOR;
}
static void
@@ -189,9 +210,18 @@ ip6_init2(dummy)
in6_ifattach(&loif[0], NULL);
/* nd6_timer_init */
- timeout(nd6_timer, (caddr_t)0, hz);
+ callout_init(&nd6_timer_ch, 0);
+ callout_reset(&nd6_timer_ch, hz, nd6_timer, NULL);
+
/* router renumbering prefix list maintenance */
- timeout(in6_rr_timer, (caddr_t)0, hz);
+ callout_init(&in6_rr_timer_ch, 0);
+ callout_reset(&in6_rr_timer_ch, hz, in6_rr_timer, NULL);
+
+ /* timer for regeneranation of temporary addresses randomize ID */
+ callout_reset(&in6_tmpaddrtimer_ch,
+ (ip6_temp_preferred_lifetime - ip6_desync_factor -
+ ip6_temp_regen_advance) * hz,
+ in6_tmpaddrtimer, NULL);
}
/* cheat */
@@ -247,6 +277,11 @@ ip6_input(m)
#endif
/*
+ * make sure we don't have onion peering information into m_aux.
+ */
+ ip6_delaux(m);
+
+ /*
* mbuf statistics by kazu
*/
if (m->m_flags & M_EXT) {
@@ -255,15 +290,17 @@ ip6_input(m)
else
ip6stat.ip6s_mext1++;
} else {
+#define M2MMAX (sizeof(ip6stat.ip6s_m2m)/sizeof(ip6stat.ip6s_m2m[0]))
if (m->m_next) {
if (m->m_flags & M_LOOP) {
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]++;
else
ip6stat.ip6s_m2m[0]++;
} else
ip6stat.ip6s_m1++;
+#undef M2MMAX
}
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_receive);
@@ -360,20 +397,42 @@ ip6_input(m)
}
/*
- * Scope check
+ * Check against address spoofing/corruption.
*/
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src) ||
IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) {
+ /*
+ * XXX: "badscope" is not very suitable for a multicast source.
+ */
+ ip6stat.ip6s_badscope++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr);
+ goto bad;
+ }
+ if ((IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) ||
+ IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) &&
+ (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) {
ip6stat.ip6s_badscope++;
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr);
goto bad;
}
-
/*
- * Don't check IPv4 mapped address here. SIIT assumes that
- * routers would forward IPv6 native packets with IPv4 mapped
- * address normally.
+ * The following check is not documented in specs. A malicious
+ * party may be able to use IPv4 mapped addr to confuse tcp/udp stack
+ * 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.
+ *
+ * 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
+ * partical support for SIIT environment.
*/
+ if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) ||
+ IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) {
+ ip6stat.ip6s_badscope++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr);
+ goto bad;
+ }
#if 0
/*
* Reject packets with IPv4 compatible addresses (auto tunnel).
@@ -389,105 +448,52 @@ ip6_input(m)
goto bad;
}
#endif
- if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) ||
- IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) {
- if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) {
- struct in6_ifaddr *ia6;
-
- if ((ia6 = in6ifa_ifpwithaddr(m->m_pkthdr.rcvif,
- &ip6->ip6_dst)) != NULL) {
- ia6->ia_ifa.if_ipackets++;
- ia6->ia_ifa.if_ibytes += m->m_pkthdr.len;
- } else {
- /*
- * The packet is looped back, but we do not
- * have the destination address for some
- * reason.
- * XXX: should we return an icmp6 error?
- */
- goto bad;
- }
- ours = 1;
- deliverifp = m->m_pkthdr.rcvif;
- goto hbhcheck;
- } else {
+
+ /* drop packets if interface ID portion is already filled */
+ if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) {
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src) &&
+ ip6->ip6_src.s6_addr16[1]) {
+ ip6stat.ip6s_badscope++;
+ goto bad;
+ }
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst) &&
+ ip6->ip6_dst.s6_addr16[1]) {
ip6stat.ip6s_badscope++;
- in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr);
goto bad;
}
}
-#ifndef FAKE_LOOPBACK_IF
- if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0)
-#else
- if (1)
-#endif
- {
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
- ip6->ip6_src.s6_addr16[1]
- = htons(m->m_pkthdr.rcvif->if_index);
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
- ip6->ip6_dst.s6_addr16[1]
- = htons(m->m_pkthdr.rcvif->if_index);
- }
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
+ ip6->ip6_src.s6_addr16[1]
+ = htons(m->m_pkthdr.rcvif->if_index);
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
+ ip6->ip6_dst.s6_addr16[1]
+ = htons(m->m_pkthdr.rcvif->if_index);
+#if 0 /* this case seems to be unnecessary. (jinmei, 20010401) */
/*
- * XXX we need this since we do not have "goto ours" hack route
- * for some of our ifaddrs on loopback interface.
- * we should correct it by changing in6_ifattach to install
- * "goto ours" hack route.
+ * We use rt->rt_ifp to determine if the address is ours or not.
+ * If rt_ifp is lo0, the address is ours.
+ * 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) {
- if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) {
- struct in6_ifaddr *ia6;
-#ifndef FAKE_LOOPBACK_IF
- int deliverifid;
-
- /*
- * Get the "real" delivered interface, which should be
- * embedded in the second 16 bits of the destination
- * address. We can probably trust the value, but we
- * add validation for the value just for safety.
- */
- deliverifid = ntohs(ip6->ip6_dst.s6_addr16[1]);
- if (deliverifid > 0 && deliverifid <= if_index) {
- deliverifp = ifindex2ifnet[deliverifid];
-
- /*
- * XXX: fake the rcvif to the real interface.
- * Since m_pkthdr.rcvif should be lo0 (or a
- * variant), it would confuse scope handling
- * code later.
- */
- m->m_pkthdr.rcvif = deliverifp;
- }
- else {
- /*
- * Last resort; just use rcvif.
- * XXX: the packet would be discarded by the
- * succeeding check.
- */
- deliverifp = m->m_pkthdr.rcvif;
- }
-#else
- deliverifp = m->m_pkthdr.rcvif;
-#endif
- if ((ia6 = in6ifa_ifpwithaddr(deliverifp,
- &ip6->ip6_dst)) != NULL) {
- ia6->ia_ifa.if_ipackets++;
- ia6->ia_ifa.if_ibytes += m->m_pkthdr.len;
- } else {
- /*
- * We do not have the link-local address
- * specified as the destination.
- * XXX: should we return an icmp6 error?
- */
- goto bad;
- }
- ours = 1;
- goto hbhcheck;
+ 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;
}
+#endif
/*
* Multicast check
@@ -516,12 +522,21 @@ ip6_input(m)
/*
* Unicast check
*/
+ switch (ip6_ours_check_algorithm) {
+ default:
+ /*
+ * XXX: I intentionally broke our indentation rule here,
+ * since this switch-case is just for measurement and
+ * therefore should soon be removed.
+ */
if (ip6_forward_rt.ro_rt != NULL &&
(ip6_forward_rt.ro_rt->rt_flags & RTF_UP) != 0 &&
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++;
else {
+ struct sockaddr_in6 *dst6;
+
if (ip6_forward_rt.ro_rt) {
/* route is down or destination is different */
ip6stat.ip6s_forward_cachemiss++;
@@ -530,9 +545,10 @@ ip6_input(m)
}
bzero(&ip6_forward_rt.ro_dst, sizeof(struct sockaddr_in6));
- ip6_forward_rt.ro_dst.sin6_len = sizeof(struct sockaddr_in6);
- ip6_forward_rt.ro_dst.sin6_family = AF_INET6;
- ip6_forward_rt.ro_dst.sin6_addr = ip6->ip6_dst;
+ dst6 = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst;
+ dst6->sin6_len = sizeof(struct sockaddr_in6);
+ dst6->sin6_family = AF_INET6;
+ dst6->sin6_addr = ip6->ip6_dst;
#ifdef SCOPEDROUTING
ip6_forward_rt.ro_dst.sin6_scope_id =
in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_dst);
@@ -551,10 +567,27 @@ ip6_input(m)
* route to the loopback interface for the destination of the packet.
* But we think it's even useful in some situations, e.g. when using
* a special daemon which wants to intercept the packet.
+ *
+ * XXX: some OSes automatically make a cloned route for the destination
+ * of an outgoing packet. If the outgoing interface of the packet
+ * is a loopback one, the kernel would consider the packet to be
+ * accepted, even if we have no such address assinged on the interface.
+ * We check the cloned flag of the route entry to reject such cases,
+ * assuming that route entries for our own addresses are not made by
+ * cloning (it should be true because in6_addloop explicitly installs
+ * the host route). However, we might have to do an explicit check
+ * while it would be less efficient. Or, should we rather install a
+ * reject route for such a case?
*/
if (ip6_forward_rt.ro_rt &&
(ip6_forward_rt.ro_rt->rt_flags &
(RTF_HOST|RTF_GATEWAY)) == RTF_HOST &&
+#ifdef RTF_WASCLONED
+ !(ip6_forward_rt.ro_rt->rt_flags & RTF_WASCLONED) &&
+#endif
+#ifdef RTF_CLONED
+ !(ip6_forward_rt.ro_rt->rt_flags & RTF_CLONED) &&
+#endif
#if 0
/*
* The check below is redundant since the comparison of
@@ -562,13 +595,17 @@ ip6_input(m)
* already done through looking up the routing table.
*/
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
ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_LOOP) {
struct in6_ifaddr *ia6 =
(struct in6_ifaddr *)ip6_forward_rt.ro_rt->rt_ifa;
- if (ia6->ia6_flags & IN6_IFF_ANYCAST)
- m->m_flags |= M_ANYCAST6;
+
+ /*
+ * record address information into m_aux.
+ */
+ (void)ip6_setdstifaddr(m, ia6);
+
/*
* packets to a tentative, duplicated, or somehow invalid
* address must not be accepted.
@@ -577,22 +614,21 @@ ip6_input(m)
/* this address is ready */
ours = 1;
deliverifp = ia6->ia_ifp; /* correct? */
-
/* Count the packet in the ip address stats */
ia6->ia_ifa.if_ipackets++;
ia6->ia_ifa.if_ibytes += m->m_pkthdr.len;
-
goto hbhcheck;
} else {
/* address is not ready, so discard the packet. */
- log(LOG_INFO,
- "ip6_input: packet to an unready address %s->%s",
+ nd6log((LOG_INFO,
+ "ip6_input: packet to an unready address %s->%s\n",
ip6_sprintf(&ip6->ip6_src),
- ip6_sprintf(&ip6->ip6_dst));
+ ip6_sprintf(&ip6->ip6_dst)));
goto bad;
}
}
+ } /* XXX indentation (see above) */
/*
* FAITH(Firewall Aided Internet Translator)
@@ -621,6 +657,27 @@ ip6_input(m)
hbhcheck:
/*
+ * record address information into m_aux, if we don't have one yet.
+ * note that we are unable to record it, if the address is not listed
+ * as our interface address (e.g. multicast addresses, addresses
+ * within FAITH prefixes and such).
+ */
+ if (deliverifp && !ip6_getdstifaddr(m)) {
+ struct in6_ifaddr *ia6;
+
+ ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst);
+ if (ia6) {
+ if (!ip6_setdstifaddr(m, ia6)) {
+ /*
+ * XXX maybe we should drop the packet here,
+ * as we could not provide enough information
+ * to the upper layers.
+ */
+ }
+ }
+ }
+
+ /*
* Process Hop-by-Hop options header if it's contained.
* m may be modified in ip6_hopopts_input().
* If a JumboPayload option is included, plen will also be modified.
@@ -749,6 +806,7 @@ ip6_input(m)
ip6stat.ip6s_delivered++;
in6_ifstat_inc(deliverifp, ifs6_in_deliver);
nest = 0;
+
while (nxt != IPPROTO_DONE) {
if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) {
ip6stat.ip6s_toomanyhdr++;
@@ -765,6 +823,35 @@ ip6_input(m)
goto bad;
}
+#if 0
+ /*
+ * do we need to do it for every header? yeah, other
+ * functions can play with it (like re-allocate and copy).
+ */
+ mhist = ip6_addaux(m);
+ if (mhist && M_TRAILINGSPACE(mhist) >= sizeof(nxt)) {
+ hist = mtod(mhist, caddr_t) + mhist->m_len;
+ bcopy(&nxt, hist, sizeof(nxt));
+ mhist->m_len += sizeof(nxt);
+ } else {
+ ip6stat.ip6s_toomanyhdr++;
+ goto bad;
+ }
+#endif
+
+#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);
}
return;
@@ -773,6 +860,36 @@ ip6_input(m)
}
/*
+ * set/grab in6_ifaddr correspond to IPv6 destination address.
+ * XXX backward compatibility wrapper
+ */
+static struct mbuf *
+ip6_setdstifaddr(m, ia6)
+ struct mbuf *m;
+ struct in6_ifaddr *ia6;
+{
+ struct mbuf *n;
+
+ n = ip6_addaux(m);
+ if (n)
+ mtod(n, struct ip6aux *)->ip6a_dstia6 = ia6;
+ return n; /* NULL if failed to set */
+}
+
+struct in6_ifaddr *
+ip6_getdstifaddr(m)
+ struct mbuf *m;
+{
+ struct mbuf *n;
+
+ n = ip6_findaux(m);
+ if (n)
+ return mtod(n, struct ip6aux *)->ip6a_dstia6;
+ else
+ return NULL;
+}
+
+/*
* Hop-by-Hop options header processing. If a valid jumbo payload option is
* included, the real payload length will be stored in plenp.
*/
@@ -783,7 +900,7 @@ ip6_hopopts_input(plenp, rtalertp, mp, offp)
struct mbuf **mp;
int *offp;
{
- register struct mbuf *m = *mp;
+ struct mbuf *m = *mp;
int off = *offp, hbhlen;
struct ip6_hbh *hbh;
u_int8_t *opt;
@@ -829,6 +946,10 @@ ip6_hopopts_input(plenp, rtalertp, mp, offp)
* This function is separate from ip6_hopopts_input() in order to
* 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().
+ *
+ * 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
ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
@@ -843,58 +964,62 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
u_int8_t *opt = opthead;
u_int16_t rtalert_val;
u_int32_t jumboplen;
+ const int erroff = sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh);
for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) {
- switch(*opt) {
- case IP6OPT_PAD1:
- optlen = 1;
- break;
- case IP6OPT_PADN:
- if (hbhlen < IP6OPT_MINLEN) {
- ip6stat.ip6s_toosmall++;
- goto bad;
- }
- optlen = *(opt + 1) + 2;
- break;
- case IP6OPT_RTALERT:
- /* XXX may need check for alignment */
- if (hbhlen < IP6OPT_RTALERT_LEN) {
- ip6stat.ip6s_toosmall++;
- goto bad;
- }
- if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2)
- /* XXX: should we discard the packet? */
- log(LOG_ERR, "length of router alert opt is inconsitent(%d)",
- *(opt + 1));
- optlen = IP6OPT_RTALERT_LEN;
- bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2);
- *rtalertp = ntohs(rtalert_val);
- break;
- case IP6OPT_JUMBO:
+ switch (*opt) {
+ case IP6OPT_PAD1:
+ optlen = 1;
+ break;
+ case IP6OPT_PADN:
+ if (hbhlen < IP6OPT_MINLEN) {
+ ip6stat.ip6s_toosmall++;
+ goto bad;
+ }
+ optlen = *(opt + 1) + 2;
+ break;
+ case IP6OPT_RTALERT:
+ /* XXX may need check for alignment */
+ if (hbhlen < IP6OPT_RTALERT_LEN) {
+ ip6stat.ip6s_toosmall++;
+ goto bad;
+ }
+ if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) {
+ /* XXX stat */
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ erroff + opt + 1 - opthead);
+ return(-1);
+ }
+ optlen = IP6OPT_RTALERT_LEN;
+ bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2);
+ *rtalertp = ntohs(rtalert_val);
+ break;
+ case IP6OPT_JUMBO:
/* XXX may need check for alignment */
if (hbhlen < IP6OPT_JUMBO_LEN) {
ip6stat.ip6s_toosmall++;
goto bad;
}
- if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2)
- /* XXX: should we discard the packet? */
- log(LOG_ERR, "length of jumbopayload opt "
- "is inconsistent(%d)",
- *(opt + 1));
+ if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) {
+ /* XXX stat */
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ erroff + opt + 1 - opthead);
+ return(-1);
+ }
optlen = IP6OPT_JUMBO_LEN;
/*
* 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 *);
if (ip6->ip6_plen) {
ip6stat.ip6s_badoptions++;
icmp6_error(m, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
- sizeof(struct ip6_hdr) +
- sizeof(struct ip6_hbh) +
- opt - opthead);
+ erroff + opt - opthead);
return(-1);
}
@@ -918,9 +1043,7 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
ip6stat.ip6s_badoptions++;
icmp6_error(m, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
- sizeof(struct ip6_hdr) +
- sizeof(struct ip6_hbh) +
- opt + 2 - opthead);
+ erroff + opt + 2 - opthead);
return(-1);
}
#endif
@@ -932,26 +1055,23 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
ip6stat.ip6s_badoptions++;
icmp6_error(m, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
- sizeof(struct ip6_hdr) +
- sizeof(struct ip6_hbh) +
- opt + 2 - opthead);
+ erroff + opt + 2 - opthead);
return(-1);
}
*plenp = jumboplen;
break;
- default: /* unknown option */
- if (hbhlen < IP6OPT_MINLEN) {
- ip6stat.ip6s_toosmall++;
- goto bad;
- }
- if ((optlen = ip6_unknown_opt(opt, m,
- sizeof(struct ip6_hdr) +
- sizeof(struct ip6_hbh) +
- opt - opthead)) == -1)
- return(-1);
- optlen += 2;
- break;
+ default: /* unknown option */
+ if (hbhlen < IP6OPT_MINLEN) {
+ ip6stat.ip6s_toosmall++;
+ goto bad;
+ }
+ optlen = ip6_unknown_opt(opt, m,
+ erroff + opt - opthead);
+ if (optlen == -1)
+ return(-1);
+ optlen += 2;
+ break;
}
}
@@ -976,26 +1096,26 @@ ip6_unknown_opt(optp, m, off)
{
struct ip6_hdr *ip6;
- switch(IP6OPT_TYPE(*optp)) {
- case IP6OPT_TYPE_SKIP: /* ignore the option */
- return((int)*(optp + 1));
- case IP6OPT_TYPE_DISCARD: /* silently discard */
- m_freem(m);
- return(-1);
- case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */
- ip6stat.ip6s_badoptions++;
- icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off);
- return(-1);
- case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */
- ip6stat.ip6s_badoptions++;
- ip6 = mtod(m, struct ip6_hdr *);
- if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
- (m->m_flags & (M_BCAST|M_MCAST)))
- m_freem(m);
- else
- icmp6_error(m, ICMP6_PARAM_PROB,
- ICMP6_PARAMPROB_OPTION, off);
- return(-1);
+ switch (IP6OPT_TYPE(*optp)) {
+ case IP6OPT_TYPE_SKIP: /* ignore the option */
+ return((int)*(optp + 1));
+ case IP6OPT_TYPE_DISCARD: /* silently discard */
+ m_freem(m);
+ return(-1);
+ case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off);
+ return(-1);
+ case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */
+ ip6stat.ip6s_badoptions++;
+ ip6 = mtod(m, struct ip6_hdr *);
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
+ (m->m_flags & (M_BCAST|M_MCAST)))
+ m_freem(m);
+ else
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_OPTION, off);
+ return(-1);
}
m_freem(m); /* XXX: NOTREACHED */
@@ -1004,50 +1124,44 @@ ip6_unknown_opt(optp, m, off)
/*
* Create the "control" list for this pcb.
+ * The function will not modify mbuf chain at all.
*
+ * with KAME mbuf chain restriction:
* The routine will be called from upper layer handlers like tcp6_input().
* Thus the routine assumes that the caller (tcp6_input) have already
* called IP6_EXTHDR_CHECK() and all the extension headers are located in the
* very first mbuf on the mbuf chain.
- * We may want to add some infinite loop prevention or sanity checks for safety.
- * (This applies only when you are using KAME mbuf chain restriction, i.e.
- * you are using IP6_EXTHDR_CHECK() not m_pulldown())
*/
void
ip6_savecontrol(in6p, mp, ip6, m)
- register struct in6pcb *in6p;
- register struct mbuf **mp;
- register struct ip6_hdr *ip6;
- register struct mbuf *m;
+ struct inpcb *in6p;
+ struct mbuf **mp;
+ struct ip6_hdr *ip6;
+ struct mbuf *m;
{
struct proc *p = curproc; /* XXX */
- int privileged;
+ int privileged = 0;
+ int rthdr_exist = 0;
+
- privileged = 0;
if (p && !suser(p))
- privileged++;
+ privileged++;
- if (in6p->in6p_socket->so_options & SO_TIMESTAMP) {
+#ifdef SO_TIMESTAMP
+ if ((in6p->in6p_socket->so_options & SO_TIMESTAMP) != 0) {
struct timeval tv;
microtime(&tv);
*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
- SCM_TIMESTAMP, SOL_SOCKET);
- if (*mp)
+ SCM_TIMESTAMP, SOL_SOCKET);
+ if (*mp) {
mp = &(*mp)->m_next;
+ }
}
-
-#ifdef noyet
- /* options were tossed above */
- if (in6p->in6p_flags & IN6P_RECVOPTS)
- /* broken */
- /* ip6_srcroute doesn't do what we want here, need to fix */
- if (in6p->in6p_flags & IPV6P_RECVRETOPTS)
- /* broken */
#endif
/* RFC 2292 sec. 5 */
- if (in6p->in6p_flags & IN6P_PKTINFO) {
+ if ((in6p->in6p_flags & IN6P_PKTINFO) != 0) {
struct in6_pktinfo pi6;
bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr));
if (IN6_IS_SCOPE_LINKLOCAL(&pi6.ipi6_addr))
@@ -1061,14 +1175,14 @@ ip6_savecontrol(in6p, mp, ip6, m)
if (*mp)
mp = &(*mp)->m_next;
}
- if (in6p->in6p_flags & IN6P_HOPLIMIT) {
+
+ if ((in6p->in6p_flags & IN6P_HOPLIMIT) != 0) {
int hlim = ip6->ip6_hlim & 0xff;
*mp = sbcreatecontrol((caddr_t) &hlim,
sizeof(int), IPV6_HOPLIMIT, IPPROTO_IPV6);
if (*mp)
mp = &(*mp)->m_next;
}
- /* IN6P_NEXTHOP - for outgoing packet only */
/*
* IPV6_HOPOPTS socket option. We require super-user privilege
@@ -1076,7 +1190,7 @@ ip6_savecontrol(in6p, mp, ip6, m)
* be some hop-by-hop options which can be returned to normal user.
* See RFC 2292 section 6.
*/
- if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) {
+ if ((in6p->in6p_flags & IN6P_HOPOPTS) != 0 && privileged) {
/*
* Check if a hop-by-hop options header is contatined in the
* received packet, and if so, store the options as ancillary
@@ -1087,22 +1201,25 @@ ip6_savecontrol(in6p, mp, ip6, m)
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
if (ip6->ip6_nxt == IPPROTO_HOPOPTS) {
struct ip6_hbh *hbh;
- int hbhlen;
+ int hbhlen = 0;
+#ifdef PULLDOWN_TEST
+ struct mbuf *ext;
+#endif
#ifndef PULLDOWN_TEST
hbh = (struct ip6_hbh *)(ip6 + 1);
hbhlen = (hbh->ip6h_len + 1) << 3;
#else
- IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m,
- sizeof(struct ip6_hdr), sizeof(struct ip6_hbh));
- if (hbh == NULL) {
+ ext = ip6_pullexthdr(m, sizeof(struct ip6_hdr),
+ ip6->ip6_nxt);
+ if (ext == NULL) {
ip6stat.ip6s_tooshort++;
return;
}
+ hbh = mtod(ext, struct ip6_hbh *);
hbhlen = (hbh->ip6h_len + 1) << 3;
- IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m,
- sizeof(struct ip6_hdr), hbhlen);
- if (hbh == NULL) {
+ if (hbhlen != ext->m_len) {
+ m_freem(ext);
ip6stat.ip6s_tooshort++;
return;
}
@@ -1112,19 +1229,53 @@ ip6_savecontrol(in6p, mp, ip6, m)
* XXX: We copy whole the header even if a jumbo
* payload option is included, which option is to
* be removed before returning in the RFC 2292.
- * But it's too painful operation...
+ * Note: this constraint is removed in 2292bis.
*/
*mp = sbcreatecontrol((caddr_t)hbh, hbhlen,
IPV6_HOPOPTS, IPPROTO_IPV6);
if (*mp)
mp = &(*mp)->m_next;
+#ifdef PULLDOWN_TEST
+ m_freem(ext);
+#endif
}
}
/* IPV6_DSTOPTS and IPV6_RTHDR socket options */
- if (in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDR)) {
+ if ((in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDRDSTOPTS)) != 0) {
+ int proto, off, nxt;
+
+ /*
+ * go through the header chain to see if a routing header is
+ * contained in the packet. We need this information to store
+ * destination options headers (if any) properly.
+ * XXX: performance issue. We should record this info when
+ * processing extension headers in incoming routine.
+ * (todo) use m_aux?
+ */
+ proto = IPPROTO_IPV6;
+ off = 0;
+ nxt = -1;
+ while (1) {
+ int newoff;
+
+ newoff = ip6_nexthdr(m, off, proto, &nxt);
+ if (newoff < 0)
+ break;
+ if (newoff < off) /* invalid, check for safety */
+ break;
+ if ((proto = nxt) == IPPROTO_ROUTING) {
+ rthdr_exist = 1;
+ break;
+ }
+ off = newoff;
+ }
+ }
+
+ if ((in6p->in6p_flags &
+ (IN6P_RTHDR | IN6P_DSTOPTS | IN6P_RTHDRDSTOPTS)) != 0) {
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
@@ -1133,95 +1284,172 @@ ip6_savecontrol(in6p, mp, ip6, m)
* Note that the order of the headers remains in
* the chain of ancillary data.
*/
- while(1) { /* is explicit loop prevention necessary? */
- struct ip6_ext *ip6e;
+ while (1) { /* is explicit loop prevention necessary? */
+ struct ip6_ext *ip6e = NULL;
int elen;
+#ifdef PULLDOWN_TEST
+ struct mbuf *ext = NULL;
+#endif
+
+ /*
+ * if it is not an extension header, don't try to
+ * pull it from the chain.
+ */
+ switch (nxt) {
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_AH: /* is it possible? */
+ break;
+ default:
+ goto loopend;
+ }
#ifndef PULLDOWN_TEST
+ if (off + sizeof(*ip6e) > m->m_len)
+ goto loopend;
ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
if (nxt == IPPROTO_AH)
elen = (ip6e->ip6e_len + 2) << 2;
else
elen = (ip6e->ip6e_len + 1) << 3;
+ if (off + elen > m->m_len)
+ goto loopend;
#else
- IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off,
- sizeof(struct ip6_ext));
- if (ip6e == NULL) {
+ ext = ip6_pullexthdr(m, off, nxt);
+ if (ext == NULL) {
ip6stat.ip6s_tooshort++;
return;
}
+ ip6e = mtod(ext, struct ip6_ext *);
if (nxt == IPPROTO_AH)
elen = (ip6e->ip6e_len + 2) << 2;
else
elen = (ip6e->ip6e_len + 1) << 3;
- IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off, elen);
- if (ip6e == NULL) {
+ if (elen != ext->m_len) {
+ m_freem(ext);
ip6stat.ip6s_tooshort++;
return;
}
#endif
- 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;
+ switch (nxt) {
+ case IPPROTO_DSTOPTS:
+ if ((in6p->in6p_flags & IN6P_DSTOPTS) == 0)
+ 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_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).
+ */
+#ifdef PULLDOWN_TEST
+ m_freem(ext);
+#endif
+ goto loopend;
+
}
/* proceed with the next header. */
off += elen;
nxt = ip6e->ip6e_nxt;
+ ip6e = NULL;
+#ifdef PULLDOWN_TEST
+ m_freem(ext);
+ ext = NULL;
+#endif
}
loopend:
+ ;
}
- if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) {
- /* to be done */
+
+}
+
+#ifdef PULLDOWN_TEST
+/*
+ * 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);
+ }
+#endif
+
+ 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) {
- /* to be done */
+ if (!n)
+ 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;
}
+#endif
/*
* Get pointer to the previous header followed by the header
@@ -1253,7 +1481,7 @@ ip6_get_prevhdr(m, off)
while (len < off) {
ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + len);
- switch(nxt) {
+ switch (nxt) {
case IPPROTO_FRAGMENT:
len += sizeof(struct ip6_frag);
break;
@@ -1382,6 +1610,55 @@ ip6_lasthdr(m, off, proto, nxtp)
}
}
+struct mbuf *
+ip6_addaux(m)
+ struct mbuf *m;
+{
+ struct mbuf *n;
+
+#ifdef DIAGNOSTIC
+ if (sizeof(struct ip6aux) > MHLEN)
+ panic("assumption failed on sizeof(ip6aux)");
+#endif
+ n = m_aux_find(m, AF_INET6, -1);
+ if (n) {
+ if (n->m_len < sizeof(struct ip6aux)) {
+ printf("conflicting use of ip6aux");
+ return NULL;
+ }
+ } else {
+ n = m_aux_add(m, AF_INET6, -1);
+ n->m_len = sizeof(struct ip6aux);
+ bzero(mtod(n, caddr_t), n->m_len);
+ }
+ return n;
+}
+
+struct mbuf *
+ip6_findaux(m)
+ struct mbuf *m;
+{
+ struct mbuf *n;
+
+ n = m_aux_find(m, AF_INET6, -1);
+ if (n && n->m_len < sizeof(struct ip6aux)) {
+ printf("conflicting use of ip6aux");
+ n = NULL;
+ }
+ return n;
+}
+
+void
+ip6_delaux(m)
+ struct mbuf *m;
+{
+ struct mbuf *n;
+
+ n = m_aux_find(m, AF_INET6, -1);
+ if (n)
+ m_aux_delete(m, n);
+}
+
/*
* System control for IP6
*/
OpenPOWER on IntegriCloud