summaryrefslogtreecommitdiffstats
path: root/sys/netinet6
diff options
context:
space:
mode:
authorshin <shin@FreeBSD.org>1999-11-22 02:45:11 +0000
committershin <shin@FreeBSD.org>1999-11-22 02:45:11 +0000
commitcad2014b2749528351ec5180e88a5929efebbfc4 (patch)
treea147aa319428e26625f19209916c257b71cfd2e1 /sys/netinet6
parent00ea4eac2008e0a2aaa1eda46cc090b7c1741a2d (diff)
downloadFreeBSD-src-cad2014b2749528351ec5180e88a5929efebbfc4.zip
FreeBSD-src-cad2014b2749528351ec5180e88a5929efebbfc4.tar.gz
KAME netinet6 basic part(no IPsec,no V6 Multicast Forwarding, no UDP/TCP
for IPv6 yet) With this patch, you can assigne IPv6 addr automatically, and can reply to IPv6 ping. Reviewed by: freebsd-arch, cvs-committers Obtained from: KAME project
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/dest6.c108
-rw-r--r--sys/netinet6/frag6.c573
-rw-r--r--sys/netinet6/icmp6.c1868
-rw-r--r--sys/netinet6/icmp6.h602
-rw-r--r--sys/netinet6/in6.c1879
-rw-r--r--sys/netinet6/in6.h35
-rw-r--r--sys/netinet6/in6_cksum.c301
-rw-r--r--sys/netinet6/in6_ifattach.c690
-rw-r--r--sys/netinet6/in6_ifattach.h50
-rw-r--r--sys/netinet6/in6_pcb.c1131
-rw-r--r--sys/netinet6/in6_pcb.h108
-rw-r--r--sys/netinet6/in6_prefix.c1106
-rw-r--r--sys/netinet6/in6_prefix.h88
-rw-r--r--sys/netinet6/in6_proto.c410
-rw-r--r--sys/netinet6/in6_rmx.c477
-rw-r--r--sys/netinet6/in6_var.h101
-rw-r--r--sys/netinet6/ip6.h245
-rw-r--r--sys/netinet6/ip6_forward.c391
-rw-r--r--sys/netinet6/ip6_fw.h202
-rw-r--r--sys/netinet6/ip6_input.c1016
-rw-r--r--sys/netinet6/ip6_output.c2176
-rw-r--r--sys/netinet6/ip6_var.h251
-rw-r--r--sys/netinet6/ip6protosw.h129
-rw-r--r--sys/netinet6/ipsec.h314
-rw-r--r--sys/netinet6/ipsec6.h77
-rw-r--r--sys/netinet6/mld6.c459
-rw-r--r--sys/netinet6/mld6_var.h52
-rw-r--r--sys/netinet6/nd6.c1531
-rw-r--r--sys/netinet6/nd6.h302
-rw-r--r--sys/netinet6/nd6_nbr.c1123
-rw-r--r--sys/netinet6/nd6_rtr.c1243
-rw-r--r--sys/netinet6/pim6.h68
-rw-r--r--sys/netinet6/pim6_var.h71
-rw-r--r--sys/netinet6/raw_ip6.c612
-rw-r--r--sys/netinet6/route6.c155
-rw-r--r--sys/netinet6/tcp6_var.h83
-rw-r--r--sys/netinet6/udp6_var.h80
37 files changed, 19991 insertions, 116 deletions
diff --git a/sys/netinet6/dest6.c b/sys/netinet6/dest6.c
new file mode 100644
index 0000000..403ed5d
--- /dev/null
+++ b/sys/netinet6/dest6.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+
+/*
+ * Destination options header processing.
+ */
+int
+dest6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ register struct mbuf *m = *mp;
+ int off = *offp, dstoptlen, optlen;
+ struct ip6_dest *dstopts;
+ u_int8_t *opt;
+
+ /* validation of the length of the header */
+ IP6_EXTHDR_CHECK(m, off, sizeof(*dstopts), IPPROTO_DONE);
+ dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off);
+ dstoptlen = (dstopts->ip6d_len + 1) << 3;
+
+ IP6_EXTHDR_CHECK(m, off, dstoptlen, IPPROTO_DONE);
+ dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off);
+ off += dstoptlen;
+ dstoptlen -= sizeof(struct ip6_dest);
+ opt = (u_int8_t *)dstopts + sizeof(struct ip6_dest);
+
+ /* search header for all options. */
+ for (optlen = 0; dstoptlen > 0; dstoptlen -= optlen, opt += optlen) {
+ switch(*opt) {
+ case IP6OPT_PAD1:
+ optlen = 1;
+ break;
+ case IP6OPT_PADN:
+ if (dstoptlen < IP6OPT_MINLEN) {
+ ip6stat.ip6s_toosmall++;
+ goto bad;
+ }
+ optlen = *(opt + 1) + 2;
+ break;
+ default: /* unknown option */
+ if (dstoptlen < IP6OPT_MINLEN) {
+ ip6stat.ip6s_toosmall++;
+ goto bad;
+ }
+ if ((optlen = ip6_unknown_opt(opt, m,
+ opt-mtod(m, u_int8_t *))) == -1)
+ return(IPPROTO_DONE);
+ optlen += 2;
+ break;
+ }
+ }
+
+ *offp = off;
+ return(dstopts->ip6d_nxt);
+
+ bad:
+ m_freem(m);
+ return(IPPROTO_DONE);
+}
diff --git a/sys/netinet6/frag6.c b/sys/netinet6/frag6.c
new file mode 100644
index 0000000..390ab13e
--- /dev/null
+++ b/sys/netinet6/frag6.c
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+
+#include <net/net_osdep.h>
+
+/*
+ * Define it to get a correct behavior on per-interface statistics.
+ * You will need to perform an extra routing table lookup, per fragment,
+ * to do it. This may, or may not be, a performance hit.
+ */
+#define IN6_IFSTAT_STRICT
+
+static void frag6_enq __P((struct ip6asfrag *, struct ip6asfrag *));
+static void frag6_deq __P((struct ip6asfrag *));
+static void frag6_insque __P((struct ip6q *, struct ip6q *));
+static void frag6_remque __P((struct ip6q *));
+static void frag6_freef __P((struct ip6q *));
+
+int frag6_doing_reass;
+u_int frag6_nfragpackets;
+struct ip6q ip6q; /* ip6 reassemble queue */
+
+#if !defined(M_FTABLE)
+MALLOC_DEFINE(M_FTABLE, "fragment", "fragment reassembly header");
+#endif
+
+/*
+ * Initialise reassembly queue and fragment identifier.
+ */
+void
+frag6_init()
+{
+ struct timeval tv;
+
+ /*
+ * in many cases, random() here does NOT return random number
+ * as initialization during bootstrap time occur in fixed order.
+ */
+ microtime(&tv);
+ ip6q.ip6q_next = ip6q.ip6q_prev = &ip6q;
+ ip6_id = random() ^ tv.tv_usec;
+}
+
+/*
+ * Fragment input
+ */
+int
+frag6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m = *mp, *t;
+ struct ip6_hdr *ip6;
+ struct ip6_frag *ip6f;
+ struct ip6q *q6;
+ struct ip6asfrag *af6, *ip6af;
+ int offset = *offp, nxt, i, next;
+ int first_frag = 0;
+ u_short fragoff, frgpartlen;
+ struct ifnet *dstifp;
+#ifdef IN6_IFSTAT_STRICT
+ static struct route_in6 ro;
+ struct sockaddr_in6 *dst;
+#endif
+
+ IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), IPPROTO_DONE);
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6f = (struct ip6_frag *)((caddr_t)ip6 + offset);
+
+ dstifp = NULL;
+#ifdef IN6_IFSTAT_STRICT
+ /* find the destination interface of the packet. */
+ dst = (struct sockaddr_in6 *)&ro.ro_dst;
+ if (ro.ro_rt
+ && ((ro.ro_rt->rt_flags & RTF_UP) == 0
+ || !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst))) {
+ RTFREE(ro.ro_rt);
+ ro.ro_rt = (struct rtentry *)0;
+ }
+ if (ro.ro_rt == NULL) {
+ bzero(dst, sizeof(*dst));
+ dst->sin6_family = AF_INET6;
+ dst->sin6_len = sizeof(struct sockaddr_in6);
+ dst->sin6_addr = ip6->ip6_dst;
+ }
+ rtcalloc((struct route *)&ro);
+ if (ro.ro_rt != NULL && ro.ro_rt->rt_ifa != NULL)
+ dstifp = ((struct in6_ifaddr *)ro.ro_rt->rt_ifa)->ia_ifp;
+#else
+ /* we are violating the spec, this is not the destination interface */
+ if ((m->m_flags & M_PKTHDR) != 0)
+ dstifp = m->m_pkthdr.rcvif;
+#endif
+
+ /* jumbo payload can't contain a fragment header */
+ if (ip6->ip6_plen == 0) {
+ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset);
+ in6_ifstat_inc(dstifp, ifs6_reass_fail);
+ return IPPROTO_DONE;
+ }
+
+ /*
+ * check whether fragment packet's fragment length is
+ * multiple of 8 octets.
+ * sizeof(struct ip6_frag) == 8
+ * sizeof(struct ip6_hdr) = 40
+ */
+ if ((ip6f->ip6f_offlg & IP6F_MORE_FRAG) &&
+ (((ntohs(ip6->ip6_plen) - offset) & 0x7) != 0)) {
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ (caddr_t)&ip6->ip6_plen - (caddr_t)ip6);
+ in6_ifstat_inc(dstifp, ifs6_reass_fail);
+ return IPPROTO_DONE;
+ }
+
+ ip6stat.ip6s_fragments++;
+ in6_ifstat_inc(dstifp, ifs6_reass_reqd);
+
+ /*
+ * Presence of header sizes in mbufs
+ * would confuse code below.
+ */
+
+ offset += sizeof(struct ip6_frag);
+ m->m_data += offset;
+ m->m_len -= offset;
+
+ for (q6 = ip6q.ip6q_next; q6 != &ip6q; q6 = q6->ip6q_next)
+ if (ip6f->ip6f_ident == q6->ip6q_ident &&
+ IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &q6->ip6q_src) &&
+ IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &q6->ip6q_dst))
+ break;
+
+ if (q6 == &ip6q) {
+ /*
+ * the first fragment to arrive, create a reassembly queue.
+ */
+ first_frag = 1;
+ frag6_nfragpackets++;
+
+ /*
+ * Enforce upper bound on number of fragmented packets
+ * for which we attempt reassembly;
+ * If maxfrag is 0, never accept fragments.
+ * If maxfrag is -1, accept all fragments without limitation.
+ */
+ if (frag6_nfragpackets >= (u_int)ip6_maxfragpackets) {
+ ip6stat.ip6s_fragoverflow++;
+ in6_ifstat_inc(dstifp, ifs6_reass_fail);
+ frag6_freef(ip6q.ip6q_prev);
+ }
+ q6 = (struct ip6q *)malloc(sizeof(struct ip6q), M_FTABLE,
+ M_DONTWAIT);
+ if (q6 == NULL)
+ goto dropfrag;
+
+ frag6_insque(q6, &ip6q);
+
+ q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6;
+ q6->ip6q_ident = ip6f->ip6f_ident;
+ q6->ip6q_arrive = 0; /* Is it used anywhere? */
+ q6->ip6q_ttl = IPV6_FRAGTTL;
+ q6->ip6q_src = ip6->ip6_src;
+ q6->ip6q_dst = ip6->ip6_dst;
+ q6->ip6q_unfrglen = -1; /* The 1st fragment has not arrived. */
+ }
+
+ /*
+ * If it's the 1st fragment, record the length of the
+ * unfragmentable part and the next header of the fragment header.
+ */
+ fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK);
+ if (fragoff == 0) {
+ q6->ip6q_unfrglen = offset - sizeof(struct ip6_hdr)
+ - sizeof(struct ip6_frag);
+ q6->ip6q_nxt = ip6f->ip6f_nxt;
+ }
+
+ /*
+ * Check that the reassembled packet would not exceed 65535 bytes
+ * in size.
+ * If it would exceed, discard the fragment and return an ICMP error.
+ */
+ frgpartlen = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - offset;
+ if (q6->ip6q_unfrglen >= 0) {
+ /* The 1st fragment has already arrived. */
+ if (q6->ip6q_unfrglen + fragoff + frgpartlen > IPV6_MAXPACKET) {
+ m->m_data -= offset;
+ m->m_len += offset;
+ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
+ offset - sizeof(struct ip6_frag) + 2);
+ return(IPPROTO_DONE);
+ }
+ }
+ else if (fragoff + frgpartlen > IPV6_MAXPACKET) {
+ m->m_data -= offset;
+ m->m_len += offset;
+ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
+ offset - sizeof(struct ip6_frag) + 2);
+ return(IPPROTO_DONE);
+ }
+ /*
+ * If it's the first fragment, do the above check for each
+ * fragment already stored in the reassembly queue.
+ */
+ if (fragoff == 0) {
+ struct ip6asfrag *af6dwn;
+
+ for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
+ af6 = af6dwn) {
+ af6dwn = af6->ip6af_down;
+
+ if (q6->ip6q_unfrglen + af6->ip6af_off + af6->ip6af_frglen >
+ IPV6_MAXPACKET) {
+ struct mbuf *merr = IP6_REASS_MBUF(af6);
+ struct ip6_hdr *ip6err;
+ int erroff = af6->ip6af_offset;
+
+ /* dequeue the fragment. */
+ frag6_deq(af6);
+
+ /* adjust pointer. */
+ merr->m_data -= af6->ip6af_offset;
+ merr->m_len += af6->ip6af_offset;
+ ip6err = mtod(merr, struct ip6_hdr *);
+
+ /*
+ * Restore source and destination addresses
+ * in the erroneous IPv6 header.
+ */
+ ip6err->ip6_src = q6->ip6q_src;
+ ip6err->ip6_dst = q6->ip6q_dst;
+
+ icmp6_error(merr, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ erroff - sizeof(struct ip6_frag) + 2);
+ }
+ }
+ }
+
+ /* Override the IPv6 header */
+ ip6af = (struct ip6asfrag *)ip6;
+ ip6af->ip6af_mff = ip6f->ip6f_offlg & IP6F_MORE_FRAG;
+ ip6af->ip6af_off = fragoff;
+ ip6af->ip6af_frglen = frgpartlen;
+ ip6af->ip6af_offset = offset;
+ IP6_REASS_MBUF(ip6af) = m;
+
+ if (first_frag) {
+ af6 = (struct ip6asfrag *)q6;
+ goto insert;
+ }
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
+ af6 = af6->ip6af_down)
+ if (af6->ip6af_off > ip6af->ip6af_off)
+ break;
+
+ /*
+ * If the incoming framgent overlaps some existing fragments in
+ * the reassembly queue, drop it, since it is dangerous to override
+ * existing fragments from a security point of view.
+ */
+ if (af6->ip6af_up != (struct ip6asfrag *)q6) {
+ i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen
+ - ip6af->ip6af_off;
+ if (i > 0) {
+ log(LOG_ERR, "%d bytes of a fragment from %s "
+ "overlaps the previous fragment\n",
+ i, ip6_sprintf(&q6->ip6q_src));
+ goto dropfrag;
+ }
+ }
+ if (af6 != (struct ip6asfrag *)q6) {
+ i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off;
+ if (i > 0) {
+ log(LOG_ERR, "%d bytes of a fragment from %s "
+ "overlaps the succeeding fragment",
+ i, ip6_sprintf(&q6->ip6q_src));
+ goto dropfrag;
+ }
+ }
+
+insert:
+
+ /*
+ * Stick new segment in its place;
+ * check for complete reassembly.
+ * Move to front of packet queue, as we are
+ * the most recently active fragmented packet.
+ */
+ frag6_enq(ip6af, af6->ip6af_up);
+ next = 0;
+ for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
+ af6 = af6->ip6af_down) {
+ if (af6->ip6af_off != next) {
+ frag6_doing_reass = 0;
+ return IPPROTO_DONE;
+ }
+ next += af6->ip6af_frglen;
+ }
+ if (af6->ip6af_up->ip6af_mff) {
+ frag6_doing_reass = 0;
+ return IPPROTO_DONE;
+ }
+
+ /*
+ * Reassembly is complete; concatenate fragments.
+ */
+
+ ip6af = q6->ip6q_down;
+ t = m = IP6_REASS_MBUF(ip6af);
+ af6 = ip6af->ip6af_down;
+ while (af6 != (struct ip6asfrag *)q6) {
+ while (t->m_next)
+ t = t->m_next;
+ t->m_next = IP6_REASS_MBUF(af6);
+ af6 = af6->ip6af_down;
+ }
+
+ /* adjust offset to point where the original next header starts */
+ offset = ip6af->ip6af_offset - sizeof(struct ip6_frag);
+ ip6 = (struct ip6_hdr *)ip6af;
+ ip6->ip6_plen = htons((u_short)next + offset - sizeof(struct ip6_hdr));
+ ip6->ip6_src = q6->ip6q_src;
+ ip6->ip6_dst = q6->ip6q_dst;
+ nxt = q6->ip6q_nxt;
+
+ /*
+ * Delete frag6 header with as a few cost as possible.
+ */
+
+ if (offset < m->m_len)
+ ovbcopy((caddr_t)ip6, (caddr_t)ip6 + sizeof(struct ip6_frag),
+ offset);
+ else {
+ ovbcopy(mtod(m, caddr_t), (caddr_t)ip6 + offset, m->m_len);
+ m->m_data -= sizeof(struct ip6_frag);
+ }
+ m->m_data -= offset;
+ m->m_len += offset;
+
+ /*
+ * Store NXT to the original.
+ */
+ {
+ char *prvnxtp = ip6_get_prevhdr(m, offset); /* XXX */
+ *prvnxtp = nxt;
+ }
+
+ frag6_remque(q6);
+ free(q6, M_FTABLE);
+ frag6_nfragpackets--;
+
+ if (m->m_flags & M_PKTHDR) { /* Isn't it always true? */
+ int plen = 0;
+ for (t = m; t; t = t->m_next)
+ plen += t->m_len;
+ m->m_pkthdr.len = plen;
+ }
+
+ ip6stat.ip6s_reassembled++;
+ in6_ifstat_inc(dstifp, ifs6_reass_ok);
+
+ /*
+ * Tell launch routine the next header
+ */
+
+ *mp = m;
+ *offp = offset;
+
+ frag6_doing_reass = 0;
+ return nxt;
+
+ dropfrag:
+ in6_ifstat_inc(dstifp, ifs6_reass_fail);
+ ip6stat.ip6s_fragdropped++;
+ m_freem(m);
+ return IPPROTO_DONE;
+}
+
+/*
+ * Free a fragment reassembly header and all
+ * associated datagrams.
+ */
+void
+frag6_freef(q6)
+ struct ip6q *q6;
+{
+ struct ip6asfrag *af6, *down6;
+
+ for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
+ af6 = down6) {
+ struct mbuf *m = IP6_REASS_MBUF(af6);
+
+ down6 = af6->ip6af_down;
+ frag6_deq(af6);
+
+ /*
+ * Return ICMP time exceeded error for the 1st fragment.
+ * Just free other fragments.
+ */
+ if (af6->ip6af_off == 0) {
+ struct ip6_hdr *ip6;
+
+ /* adjust pointer */
+ m->m_data -= af6->ip6af_offset;
+ m->m_len += af6->ip6af_offset;
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ /* restoure source and destination addresses */
+ ip6->ip6_src = q6->ip6q_src;
+ ip6->ip6_dst = q6->ip6q_dst;
+
+ icmp6_error(m, ICMP6_TIME_EXCEEDED,
+ ICMP6_TIME_EXCEED_REASSEMBLY, 0);
+ }
+ else
+ m_freem(m);
+ }
+ frag6_remque(q6);
+ free(q6, M_FTABLE);
+ frag6_nfragpackets--;
+}
+
+/*
+ * Put an ip fragment on a reassembly chain.
+ * Like insque, but pointers in middle of structure.
+ */
+void
+frag6_enq(af6, up6)
+ struct ip6asfrag *af6, *up6;
+{
+ af6->ip6af_up = up6;
+ af6->ip6af_down = up6->ip6af_down;
+ up6->ip6af_down->ip6af_up = af6;
+ up6->ip6af_down = af6;
+}
+
+/*
+ * To frag6_enq as remque is to insque.
+ */
+void
+frag6_deq(af6)
+ struct ip6asfrag *af6;
+{
+ af6->ip6af_up->ip6af_down = af6->ip6af_down;
+ af6->ip6af_down->ip6af_up = af6->ip6af_up;
+}
+
+void
+frag6_insque(new, old)
+ struct ip6q *new, *old;
+{
+ new->ip6q_prev = old;
+ new->ip6q_next = old->ip6q_next;
+ old->ip6q_next->ip6q_prev= new;
+ old->ip6q_next = new;
+}
+
+void
+frag6_remque(p6)
+ struct ip6q *p6;
+{
+ p6->ip6q_prev->ip6q_next = p6->ip6q_next;
+ p6->ip6q_next->ip6q_prev = p6->ip6q_prev;
+}
+
+/*
+ * IP timer processing;
+ * if a timer expires on a reassembly
+ * queue, discard it.
+ */
+void
+frag6_slowtimo()
+{
+ struct ip6q *q6;
+ int s = splnet();
+
+ frag6_doing_reass = 1;
+ q6 = ip6q.ip6q_next;
+ if (q6)
+ while (q6 != &ip6q) {
+ --q6->ip6q_ttl;
+ q6 = q6->ip6q_next;
+ if (q6->ip6q_prev->ip6q_ttl == 0) {
+ ip6stat.ip6s_fragtimeout++;
+ /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
+ frag6_freef(q6->ip6q_prev);
+ }
+ }
+ /*
+ * If we are over the maximum number of fragments
+ * (due to the limit being lowered), drain off
+ * enough to get down to the new limit.
+ */
+ while (frag6_nfragpackets > (u_int)ip6_maxfragpackets) {
+ ip6stat.ip6s_fragoverflow++;
+ /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
+ frag6_freef(ip6q.ip6q_prev);
+ }
+ frag6_doing_reass = 0;
+ splx(s);
+}
+
+/*
+ * Drain off all datagram fragments.
+ */
+void
+frag6_drain()
+{
+ if (frag6_doing_reass)
+ return;
+ while (ip6q.ip6q_next != &ip6q) {
+ ip6stat.ip6s_fragdropped++;
+ /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
+ frag6_freef(ip6q.ip6q_next);
+ }
+}
diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c
new file mode 100644
index 0000000..e06c6ef
--- /dev/null
+++ b/sys/netinet6/icmp6.c
@@ -0,0 +1,1868 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include "opt_inet.h"
+#include "opt_ipsec.h"
+#include "opt_key.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/domain.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+#include <netinet6/mld6_var.h>
+#include <netinet/in_pcb.h>
+#include <netinet6/nd6.h>
+#include <netinet6/in6_ifattach.h>
+#include <netinet6/ip6protosw.h>
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif /* INET6 */
+#include <netkey/key.h>
+#ifdef KEY_DEBUG
+#include <netkey/key_debug.h>
+#ifdef INET6
+#include <netkey/key_debug6.h>
+#endif /* INET6 */
+#else
+#define DPRINTF(lev,arg)
+#define DDO(lev, stmt)
+#define DP(x, y, z)
+#endif /* KEY_DEBUG */
+#endif /* IPSEC */
+
+/* #include "faith.h" */
+
+#include <net/net_osdep.h>
+
+extern struct domain inet6domain;
+extern struct ip6protosw inet6sw[];
+extern u_char ip6_protox[];
+
+struct icmp6stat icmp6stat;
+
+extern struct inpcbhead ripcb;
+extern u_int icmp6errratelim;
+
+static int icmp6_rip6_input __P((struct mbuf **, int));
+static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int));
+static const char *icmp6_redirect_diag __P((struct in6_addr *,
+ struct in6_addr *,
+ struct in6_addr *));
+static struct mbuf *ni6_input __P((struct mbuf *, int));
+static int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *,
+ struct ifnet **));
+static int ni6_store_addrs __P((struct icmp6_nodeinfo *,
+ struct icmp6_nodeinfo *,
+ struct ifnet *, int));
+
+#ifdef COMPAT_RFC1885
+static struct route_in6 icmp6_reflect_rt;
+#endif
+static struct timeval icmp6_nextsend = {0, 0};
+
+void
+icmp6_init()
+{
+ mld6_init();
+}
+
+/*
+ * Generate an error packet of type error in response to bad IP6 packet.
+ */
+void
+icmp6_error(m, type, code, param)
+ struct mbuf *m;
+ int type, code, param;
+{
+ struct ip6_hdr *oip6, *nip6;
+ struct icmp6_hdr *icmp6;
+ u_int prep;
+ int off;
+ u_char nxt;
+
+ icmp6stat.icp6s_error++;
+
+ if (m->m_flags & M_DECRYPTED)
+ goto freeit;
+
+ oip6 = mtod(m, struct ip6_hdr *);
+
+ /*
+ * Multicast destination check. For unrecognized option errors,
+ * this check has already done in ip6_unknown_opt(), so we can
+ * check only for other errors.
+ */
+ if ((m->m_flags & (M_BCAST|M_MCAST) ||
+ IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) &&
+ (type != ICMP6_PACKET_TOO_BIG &&
+ (type != ICMP6_PARAM_PROB ||
+ code != ICMP6_PARAMPROB_OPTION)))
+ goto freeit;
+
+ /* Source address check. XXX: the case of anycast source? */
+ if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) ||
+ IN6_IS_ADDR_MULTICAST(&oip6->ip6_src))
+ goto freeit;
+
+ /*
+ * If the erroneous packet is also an ICMP error, discard it.
+ */
+ IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), );
+ off = sizeof(struct ip6_hdr);
+ nxt = oip6->ip6_nxt;
+ while(1) { /* XXX: should avoid inf. loop explicitly? */
+ struct ip6_ext *ip6e;
+ struct icmp6_hdr *icp;
+
+ switch(nxt) {
+ case IPPROTO_IPV6:
+ case IPPROTO_IPV4:
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_ESP:
+ case IPPROTO_FRAGMENT:
+ /*
+ * ICMPv6 error must not be fragmented.
+ * XXX: but can we trust the sender?
+ */
+ default:
+ /* What if unknown header followed by ICMP error? */
+ goto generate;
+ case IPPROTO_ICMPV6:
+ IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), );
+ icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
+ if (icp->icmp6_type < ICMP6_ECHO_REQUEST
+ || icp->icmp6_type == ND_REDIRECT) {
+ /*
+ * ICMPv6 error
+ * Special case: for redirect (which is
+ * informational) we must not send icmp6 error.
+ */
+ icmp6stat.icp6s_canterror++;
+ goto freeit;
+ } else {
+ /* ICMPv6 informational */
+ goto generate;
+ }
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_AH:
+ IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct ip6_ext), );
+ ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
+ if (nxt == IPPROTO_AH)
+ off += (ip6e->ip6e_len + 2) << 2;
+ else
+ off += (ip6e->ip6e_len + 1) << 3;
+ nxt = ip6e->ip6e_nxt;
+ break;
+ }
+ }
+
+ freeit:
+ /*
+ * If we can't tell wheter or not we can generate ICMP6, free it.
+ */
+ m_freem(m);
+ return;
+
+ generate:
+ oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */
+
+ /* Finally, do rate limitation check. */
+ if (icmp6_ratelimit(&oip6->ip6_src, type, code)) {
+ icmp6stat.icp6s_toofreq++;
+ goto freeit;
+ }
+
+ /*
+ * OK, ICMP6 can be generated.
+ */
+
+ if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN)
+ m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len);
+
+ prep = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
+ M_PREPEND(m, prep, M_DONTWAIT);
+ if (m && m->m_len < prep)
+ m = m_pullup(m, prep);
+ if (m == NULL) {
+ printf("ENOBUFS in icmp6_error %d\n", __LINE__);
+ return;
+ }
+
+ nip6 = mtod(m, struct ip6_hdr *);
+ nip6->ip6_src = oip6->ip6_src;
+ nip6->ip6_dst = oip6->ip6_dst;
+
+ if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src))
+ oip6->ip6_src.s6_addr16[1] = 0;
+ if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst))
+ oip6->ip6_dst.s6_addr16[1] = 0;
+
+ icmp6 = (struct icmp6_hdr *)(nip6 + 1);
+ icmp6->icmp6_type = type;
+ icmp6->icmp6_code = code;
+ icmp6->icmp6_pptr = htonl((u_int32_t)param);
+
+ icmp6stat.icp6s_outhist[type]++;
+ icmp6_reflect(m, sizeof(struct ip6_hdr)); /*header order: IPv6 - ICMPv6*/
+}
+
+/*
+ * Process a received ICMP6 message.
+ */
+int
+icmp6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m = *mp, *n;
+ struct ip6_hdr *ip6, *nip6;
+ struct icmp6_hdr *icmp6, *nicmp6;
+ int off = *offp;
+ int icmp6len = m->m_pkthdr.len - *offp;
+ int code, sum, noff;
+ struct sockaddr_in6 icmp6src;
+
+ IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE);
+ /* m might change if M_LOOP. So, call mtod after this */
+
+ /*
+ * Locate icmp6 structure in mbuf, and check
+ * that not corrupted and of at least minimum length
+ */
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ if (icmp6len < sizeof(struct icmp6_hdr)) {
+ icmp6stat.icp6s_tooshort++;
+ goto freeit;
+ }
+
+ /*
+ * calculate the checksum
+ */
+
+ icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
+ code = icmp6->icmp6_code;
+
+ if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) {
+ log(LOG_ERR,
+ "ICMP6 checksum error(%d|%x) %s\n",
+ icmp6->icmp6_type,
+ sum,
+ ip6_sprintf(&ip6->ip6_src));
+ icmp6stat.icp6s_checksum++;
+ goto freeit;
+ }
+
+#if defined(NFAITH) && 0 < NFAITH
+ if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
+ /*
+ * Deliver very specific ICMP6 type only.
+ * This is important to deilver TOOBIG. Otherwise PMTUD
+ * will not work.
+ */
+ switch (icmp6->icmp6_type) {
+ case ICMP6_DST_UNREACH:
+ case ICMP6_PACKET_TOO_BIG:
+ case ICMP6_TIME_EXCEEDED:
+ break;
+ default:
+ goto freeit;
+ }
+ }
+#endif
+
+#ifdef IPSEC
+ /* drop it if it does not match the default policy */
+ if (ipsec6_in_reject(m, NULL)) {
+ ipsecstat.in_polvio++;
+ goto freeit;
+ }
+#endif
+
+ icmp6stat.icp6s_inhist[icmp6->icmp6_type]++;
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg);
+ if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK)
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);
+
+ switch (icmp6->icmp6_type) {
+
+ case ICMP6_DST_UNREACH:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach);
+ switch (code) {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ code = PRC_UNREACH_NET;
+ break;
+ case ICMP6_DST_UNREACH_ADMIN:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib);
+ case ICMP6_DST_UNREACH_ADDR:
+ code = PRC_UNREACH_HOST;
+ break;
+ case ICMP6_DST_UNREACH_NOTNEIGHBOR:
+ code = PRC_UNREACH_SRCFAIL;
+ break;
+ case ICMP6_DST_UNREACH_NOPORT:
+ code = PRC_UNREACH_PORT;
+ break;
+ default:
+ goto badcode;
+ }
+ goto deliver;
+ break;
+
+ case ICMP6_PACKET_TOO_BIG:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig);
+ if (code != 0)
+ goto badcode;
+ {
+ u_int mtu = ntohl(icmp6->icmp6_mtu);
+ struct rtentry *rt = NULL;
+ struct sockaddr_in6 sin6;
+
+ code = PRC_MSGSIZE;
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = PF_INET6;
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst;
+ rt = rtalloc1((struct sockaddr *)&sin6, 0,
+ RTF_CLONING | RTF_PRCLONING);
+ if (rt && (rt->rt_flags & RTF_HOST)
+ && !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
+ if (mtu < IPV6_MMTU) {
+ /* xxx */
+ rt->rt_rmx.rmx_locks |= RTV_MTU;
+ } else if (mtu < rt->rt_ifp->if_mtu &&
+ rt->rt_rmx.rmx_mtu > mtu) {
+ rt->rt_rmx.rmx_mtu = mtu;
+ }
+ }
+ if (rt)
+ RTFREE(rt);
+
+ goto deliver;
+ }
+ break;
+
+ case ICMP6_TIME_EXCEEDED:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed);
+ switch (code) {
+ case ICMP6_TIME_EXCEED_TRANSIT:
+ case ICMP6_TIME_EXCEED_REASSEMBLY:
+ code += PRC_TIMXCEED_INTRANS;
+ break;
+ default:
+ goto badcode;
+ }
+ goto deliver;
+ break;
+
+ case ICMP6_PARAM_PROB:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob);
+ switch (code) {
+ case ICMP6_PARAMPROB_NEXTHEADER:
+ code = PRC_UNREACH_PROTOCOL;
+ break;
+ case ICMP6_PARAMPROB_HEADER:
+ case ICMP6_PARAMPROB_OPTION:
+ code = PRC_PARAMPROB;
+ break;
+ default:
+ goto badcode;
+ }
+ goto deliver;
+ break;
+
+ case ICMP6_ECHO_REQUEST:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo);
+ if (code != 0)
+ goto badcode;
+ if ((n = m_copy(m, 0, M_COPYALL)) == NULL) {
+ /* Give up remote */
+ break;
+ }
+ if (n->m_flags & M_EXT) {
+ int gap, move;
+ struct mbuf *n0 = n;
+
+ /*
+ * Prepare an internal mbuf. m_pullup() doesn't
+ * always copy the length we specified.
+ */
+ MGETHDR(n, M_DONTWAIT, n0->m_type);
+ if (n == NULL) {
+ /* Give up remote */
+ m_freem(n0);
+ break;
+ }
+ M_COPY_PKTHDR(n, n0);
+ n0->m_flags &= ~M_PKTHDR;
+ n->m_next = n0;
+ /*
+ * Copy IPv6 and ICMPv6 only.
+ */
+ nip6 = mtod(n, struct ip6_hdr *);
+ bcopy(ip6, nip6, sizeof(struct ip6_hdr));
+ nicmp6 = (struct icmp6_hdr *)(nip6 + 1);
+ bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
+ /*
+ * Adjust mbuf. ip6_plen will be adjusted.
+ */
+ noff = sizeof(struct ip6_hdr);
+ n->m_len = noff + sizeof(struct icmp6_hdr);
+ move = off + sizeof(struct icmp6_hdr);
+ n0->m_len -= move;
+ n0->m_data += move;
+ gap = off - noff;
+ n->m_pkthdr.len -= gap;
+ } else {
+ nip6 = mtod(n, struct ip6_hdr *);
+ nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off);
+ noff = off;
+ }
+ nicmp6->icmp6_type = ICMP6_ECHO_REPLY;
+ nicmp6->icmp6_code = 0;
+ if (n) {
+ icmp6stat.icp6s_reflect++;
+ icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]++;
+ icmp6_reflect(n, noff);
+ }
+ break;
+
+ case ICMP6_ECHO_REPLY:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echoreply);
+ if (code != 0)
+ goto badcode;
+ break;
+
+ case MLD6_LISTENER_QUERY:
+ case MLD6_LISTENER_REPORT:
+ if (icmp6len < sizeof(struct mld6_hdr))
+ goto badlen;
+ if (icmp6->icmp6_type == MLD6_LISTENER_QUERY) /* XXX: ugly... */
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery);
+ else
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport);
+ IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
+ mld6_input(m, off);
+ /* m stays. */
+ break;
+
+ case MLD6_LISTENER_DONE:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone);
+ if (icmp6len < sizeof(struct mld6_hdr)) /* necessary? */
+ goto badlen;
+ break; /* nothing to be done in kernel */
+
+ case MLD6_MTRACE_RESP:
+ case MLD6_MTRACE:
+ /* XXX: these two are experimental. not officially defind. */
+ /* XXX: per-interface statistics? */
+ break; /* just pass it to the userland daemon */
+
+ case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */
+ {
+ enum { WRU, FQDN } mode;
+
+ if (code != 0)
+ goto badcode;
+ if (icmp6len == sizeof(struct icmp6_hdr) + 4)
+ mode = WRU;
+ else if (icmp6len >= sizeof(struct icmp6_hdr) + 8) /* XXX */
+ mode = FQDN;
+ else
+ goto badlen;
+
+#define hostnamelen strlen(hostname)
+ if (mode == FQDN) {
+ IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo),
+ IPPROTO_DONE);
+ n = ni6_input(m, off);
+ noff = sizeof(struct ip6_hdr);
+ } else {
+ u_char *p;
+
+ MGETHDR(n, M_DONTWAIT, m->m_type);
+ if (n == NULL) {
+ /* Give up remote */
+ break;
+ }
+ /*
+ * Copy IPv6 and ICMPv6 only.
+ */
+ nip6 = mtod(n, struct ip6_hdr *);
+ bcopy(ip6, nip6, sizeof(struct ip6_hdr));
+ nicmp6 = (struct icmp6_hdr *)(nip6 + 1);
+ bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
+ p = (u_char *)(nicmp6 + 1);
+ bzero(p, 4);
+ bcopy(hostname, p + 4, hostnamelen);
+ noff = sizeof(struct ip6_hdr);
+ M_COPY_PKTHDR(n, m); /* just for recvif */
+ n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
+ sizeof(struct icmp6_hdr) + 4 + hostnamelen;
+ nicmp6->icmp6_type = ICMP6_WRUREPLY;
+ nicmp6->icmp6_code = 0;
+ }
+#undef hostnamelen
+ if (n) {
+ icmp6stat.icp6s_reflect++;
+ icmp6stat.icp6s_outhist[ICMP6_WRUREPLY]++;
+ icmp6_reflect(n, noff);
+ }
+ break;
+ }
+
+ case ICMP6_WRUREPLY:
+ if (code != 0)
+ goto badcode;
+ break;
+
+ case ND_ROUTER_SOLICIT:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routersolicit);
+ if (code != 0)
+ goto badcode;
+ if (icmp6len < sizeof(struct nd_router_solicit))
+ goto badlen;
+ IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
+ nd6_rs_input(m, off, icmp6len);
+ /* m stays. */
+ break;
+
+ case ND_ROUTER_ADVERT:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert);
+ if (code != 0)
+ goto badcode;
+ if (icmp6len < sizeof(struct nd_router_advert))
+ goto badlen;
+ IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
+ nd6_ra_input(m, off, icmp6len);
+ /* m stays. */
+ break;
+
+ case ND_NEIGHBOR_SOLICIT:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit);
+ if (code != 0)
+ goto badcode;
+ if (icmp6len < sizeof(struct nd_neighbor_solicit))
+ goto badlen;
+ IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
+ nd6_ns_input(m, off, icmp6len);
+ /* m stays. */
+ break;
+
+ case ND_NEIGHBOR_ADVERT:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert);
+ if (code != 0)
+ goto badcode;
+ if (icmp6len < sizeof(struct nd_neighbor_advert))
+ goto badlen;
+ IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
+ nd6_na_input(m, off, icmp6len);
+ /* m stays. */
+ break;
+
+ case ND_REDIRECT:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect);
+ if (code != 0)
+ goto badcode;
+ if (icmp6len < sizeof(struct nd_redirect))
+ goto badlen;
+ icmp6_redirect_input(m, off);
+ /* m stays. */
+ break;
+
+ case ICMP6_ROUTER_RENUMBERING:
+ if (code != ICMP6_ROUTER_RENUMBERING_COMMAND &&
+ code != ICMP6_ROUTER_RENUMBERING_RESULT)
+ goto badcode;
+ if (icmp6len < sizeof(struct icmp6_router_renum))
+ goto badlen;
+ break;
+
+ default:
+ printf("icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n",
+ icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src),
+ ip6_sprintf(&ip6->ip6_dst),
+ m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0);
+ if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) {
+ /* ICMPv6 error: MUST deliver it by spec... */
+ code = PRC_NCMDS;
+ /* deliver */
+ } else {
+ /* ICMPv6 informational: MUST not deliver */
+ break;
+ }
+ deliver:
+ if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) {
+ icmp6stat.icp6s_tooshort++;
+ goto freeit;
+ }
+ IP6_EXTHDR_CHECK(m, off,
+ sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr),
+ IPPROTO_DONE);
+ icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
+ bzero(&icmp6src, sizeof(icmp6src));
+ icmp6src.sin6_len = sizeof(struct sockaddr_in6);
+ icmp6src.sin6_family = AF_INET6;
+ icmp6src.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst;
+
+ /* Detect the upper level protocol */
+ {
+ void (*ctlfunc) __P((int, struct sockaddr *, void *));
+ struct ip6_hdr *eip6 = (struct ip6_hdr *)(icmp6 + 1);
+ u_int8_t nxt = eip6->ip6_nxt;
+ int eoff = off + sizeof(struct icmp6_hdr) +
+ sizeof(struct ip6_hdr);
+ struct ip6ctlparam ip6cp;
+
+ while (1) { /* XXX: should avoid inf. loop explicitly? */
+ struct ip6_ext *eh;
+
+ switch(nxt) {
+ case IPPROTO_ESP:
+ case IPPROTO_NONE:
+ goto passit;
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_AH:
+ case IPPROTO_FRAGMENT:
+ IP6_EXTHDR_CHECK(m, 0, eoff +
+ sizeof(struct ip6_ext),
+ IPPROTO_DONE);
+ eh = (struct ip6_ext *)(mtod(m, caddr_t)
+ + eoff);
+ if (nxt == IPPROTO_AH)
+ eoff += (eh->ip6e_len + 2) << 2;
+ else if (nxt == IPPROTO_FRAGMENT)
+ eoff += sizeof(struct ip6_frag);
+ else
+ eoff += (eh->ip6e_len + 1) << 3;
+ nxt = eh->ip6e_nxt;
+ break;
+ default:
+ goto notify;
+ }
+ }
+ notify:
+ icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
+ ctlfunc = (void (*) __P((int, struct sockaddr *, void *)))
+ (inet6sw[ip6_protox[nxt]].pr_ctlinput);
+ if (ctlfunc) {
+ ip6cp.ip6c_m = m;
+ ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1);
+ ip6cp.ip6c_off = eoff;
+ (*ctlfunc)(code, (struct sockaddr *)&icmp6src, &ip6cp);
+ }
+ }
+ break;
+
+ badcode:
+ icmp6stat.icp6s_badcode++;
+ break;
+
+ badlen:
+ icmp6stat.icp6s_badlen++;
+ break;
+ }
+
+ passit:
+ icmp6_rip6_input(&m, *offp);
+ return IPPROTO_DONE;
+
+ freeit:
+ m_freem(m);
+ return IPPROTO_DONE;
+}
+
+/*
+ * Process a Node Information Query
+ */
+#define hostnamelen strlen(hostname)
+#ifndef offsetof /* XXX */
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
+#endif
+
+static struct mbuf *
+ni6_input(m, off)
+ struct mbuf *m;
+ int off;
+{
+ struct icmp6_nodeinfo *ni6 =
+ (struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off), *nni6;
+ struct mbuf *n = NULL;
+ u_int16_t qtype = ntohs(ni6->ni_qtype);
+ int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo);
+ struct ni_reply_fqdn *fqdn;
+ int addrs; /* for NI_QTYPE_NODEADDR */
+ struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */
+
+ switch(qtype) {
+ case NI_QTYPE_NOOP:
+ break; /* no reply data */
+ case NI_QTYPE_SUPTYPES:
+ goto bad; /* xxx: to be implemented */
+ break;
+ case NI_QTYPE_FQDN:
+ replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) +
+ hostnamelen;
+ break;
+ case NI_QTYPE_NODEADDR:
+ addrs = ni6_addrs(ni6, m, &ifp);
+ if ((replylen += addrs * sizeof(struct in6_addr)) > MCLBYTES)
+ replylen = MCLBYTES; /* XXX: we'll truncate later */
+
+ break;
+ default:
+ /*
+ * XXX: We must return a reply with the ICMP6 code
+ * `unknown Qtype' in this case. However we regard the case
+ * as an FQDN query for backward compatibility.
+ * Older versions set a random value to this field,
+ * so it rarely varies in the defined qtypes.
+ * But the mechanism is not reliable...
+ * maybe we should obsolete older versions.
+ */
+ qtype = NI_QTYPE_FQDN;
+ replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) +
+ hostnamelen;
+ break;
+ }
+
+ /* allocate a mbuf to reply. */
+ MGETHDR(n, M_DONTWAIT, m->m_type);
+ if (n == NULL)
+ return(NULL);
+ M_COPY_PKTHDR(n, m); /* just for recvif */
+ if (replylen > MHLEN) {
+ if (replylen > MCLBYTES)
+ /*
+ * XXX: should we try to allocate more? But MCLBYTES is
+ * probably much larger than IPV6_MMTU...
+ */
+ goto bad;
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ goto bad;
+ }
+ }
+ n->m_pkthdr.len = n->m_len = replylen;
+
+ /* copy mbuf header and IPv6 + Node Information base headers */
+ bcopy(mtod(m, caddr_t), mtod(n, caddr_t), sizeof(struct ip6_hdr));
+ nni6 = (struct icmp6_nodeinfo *)(mtod(n, struct ip6_hdr *) + 1);
+ bcopy(mtod(m, caddr_t) + off, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo));
+
+ /* qtype dependent procedure */
+ switch (qtype) {
+ case NI_QTYPE_NOOP:
+ nni6->ni_flags = 0;
+ break;
+ case NI_QTYPE_SUPTYPES:
+ goto bad; /* xxx: to be implemented */
+ break;
+ case NI_QTYPE_FQDN:
+ if (hostnamelen > 255) { /* XXX: rare case, but may happen */
+ printf("ni6_input: "
+ "hostname length(%d) is too large for reply\n",
+ hostnamelen);
+ goto bad;
+ }
+ fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) +
+ sizeof(struct ip6_hdr) +
+ sizeof(struct icmp6_nodeinfo));
+ nni6->ni_flags = 0; /* XXX: meaningless TTL */
+ fqdn->ni_fqdn_ttl = 0; /* ditto. */
+ fqdn->ni_fqdn_namelen = hostnamelen;
+ bcopy(hostname, &fqdn->ni_fqdn_name[0], hostnamelen);
+ break;
+ case NI_QTYPE_NODEADDR:
+ {
+ int lenlim, copied;
+
+ if (n->m_flags & M_EXT)
+ lenlim = MCLBYTES - sizeof(struct ip6_hdr) -
+ sizeof(struct icmp6_nodeinfo);
+ else
+ lenlim = MHLEN - sizeof(struct ip6_hdr) -
+ sizeof(struct icmp6_nodeinfo);
+ copied = ni6_store_addrs(ni6, nni6, ifp, lenlim);
+ /* XXX: reset mbuf length */
+ n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
+ sizeof(struct icmp6_nodeinfo) + copied;
+ break;
+ }
+ default:
+ break; /* XXX impossible! */
+ }
+
+ nni6->ni_type = ICMP6_NI_REPLY;
+ nni6->ni_code = ICMP6_NI_SUCESS;
+ return(n);
+
+ bad:
+ if (n)
+ m_freem(n);
+ return(NULL);
+}
+#undef hostnamelen
+
+/*
+ * calculate the number of addresses to be returned in the node info reply.
+ */
+static int
+ni6_addrs(ni6, m, ifpp)
+ struct icmp6_nodeinfo *ni6;
+ struct mbuf *m;
+ struct ifnet **ifpp;
+{
+ register struct ifnet *ifp;
+ register struct in6_ifaddr *ifa6;
+ register struct ifaddr *ifa;
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ int addrs = 0, addrsofif, iffound = 0;
+
+ for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
+ {
+ addrsofif = 0;
+ for (ifa = ifp->if_addrlist.tqh_first; ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ ifa6 = (struct in6_ifaddr *)ifa;
+
+ if (!(ni6->ni_flags & NI_NODEADDR_FLAG_ALL) &&
+ IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst,
+ &ifa6->ia_addr.sin6_addr))
+ iffound = 1;
+
+ if (ifa6->ia6_flags & IN6_IFF_ANYCAST)
+ continue; /* we need only unicast addresses */
+
+ if ((ni6->ni_flags & (NI_NODEADDR_FLAG_LINKLOCAL |
+ NI_NODEADDR_FLAG_SITELOCAL |
+ NI_NODEADDR_FLAG_GLOBAL)) == 0)
+ continue;
+
+ /* What do we have to do about ::1? */
+ switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
+ case IPV6_ADDR_SCOPE_LINKLOCAL:
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL)
+ addrsofif++;
+ break;
+ case IPV6_ADDR_SCOPE_SITELOCAL:
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL)
+ addrsofif++;
+ break;
+ case IPV6_ADDR_SCOPE_GLOBAL:
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL)
+ addrsofif++;
+ break;
+ default:
+ continue;
+ }
+ }
+ if (iffound) {
+ *ifpp = ifp;
+ return(addrsofif);
+ }
+
+ addrs += addrsofif;
+ }
+
+ return(addrs);
+}
+
+static int
+ni6_store_addrs(ni6, nni6, ifp0, resid)
+ struct icmp6_nodeinfo *ni6, *nni6;
+ struct ifnet *ifp0;
+ int resid;
+{
+ register struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet);
+ register struct in6_ifaddr *ifa6;
+ register struct ifaddr *ifa;
+ int docopy, copied = 0;
+ u_char *cp = (u_char *)(nni6 + 1);
+
+ if (ifp0 == NULL && !(ni6->ni_flags & NI_NODEADDR_FLAG_ALL))
+ return(0); /* needless to copy */
+
+ for (; ifp; ifp = TAILQ_NEXT(ifp, if_list))
+ {
+ for (ifa = ifp->if_addrlist.tqh_first; ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ docopy = 0;
+
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ ifa6 = (struct in6_ifaddr *)ifa;
+
+ if (ifa6->ia6_flags & IN6_IFF_ANYCAST) {
+ /* just experimental. not in the spec. */
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST)
+ docopy = 1;
+ else
+ continue;
+ } else { /* unicast address */
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST)
+ continue;
+ else
+ docopy = 1;
+ }
+
+ /* What do we have to do about ::1? */
+ switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
+ case IPV6_ADDR_SCOPE_LINKLOCAL:
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL)
+ docopy = 1;
+ break;
+ case IPV6_ADDR_SCOPE_SITELOCAL:
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL)
+ docopy = 1;
+ break;
+ case IPV6_ADDR_SCOPE_GLOBAL:
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL)
+ docopy = 1;
+ break;
+ default:
+ continue;
+ }
+
+ if (docopy) {
+ if (resid < sizeof(struct in6_addr)) {
+ /*
+ * We give up much more copy.
+ * Set the truncate flag and return.
+ */
+ nni6->ni_flags |=
+ NI_NODEADDR_FLAG_TRUNCATE;
+ return(copied);
+ }
+ bcopy(&ifa6->ia_addr.sin6_addr, cp,
+ sizeof(struct in6_addr));
+ /* XXX: KAME link-local hack; remove ifindex */
+ if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr))
+ ((struct in6_addr *)cp)->s6_addr16[1] = 0;
+ cp += sizeof(struct in6_addr);
+ resid -= sizeof(struct in6_addr);
+ copied += sizeof(struct in6_addr);
+ }
+ }
+ if (ifp0) /* we need search only on the specified IF */
+ break;
+ }
+
+ return(copied);
+}
+
+/*
+ * XXX almost dup'ed code with rip6_input.
+ */
+static int
+icmp6_rip6_input(mp, off)
+ struct mbuf **mp;
+ int off;
+{
+ struct mbuf *m = *mp;
+ register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ register struct in6pcb *in6p;
+ struct in6pcb *last = NULL;
+ struct sockaddr_in6 rip6src;
+ struct icmp6_hdr *icmp6;
+ struct mbuf *opts = NULL;
+
+ /* this is assumed to be safe. */
+ icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
+
+ bzero(&rip6src, sizeof(rip6src));
+ rip6src.sin6_len = sizeof(struct sockaddr_in6);
+ rip6src.sin6_family = AF_INET6;
+ rip6src.sin6_addr = ip6->ip6_src;
+ if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr))
+ rip6src.sin6_addr.s6_addr16[1] = 0;
+ if (m->m_pkthdr.rcvif) {
+ if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr))
+ rip6src.sin6_scope_id = m->m_pkthdr.rcvif->if_index;
+ else
+ rip6src.sin6_scope_id = 0;
+ } else
+ rip6src.sin6_scope_id = 0;
+
+ LIST_FOREACH(in6p, &ripcb, inp_list)
+ {
+ if ((in6p->inp_vflag & INP_IPV6) == NULL)
+ continue;
+ if (in6p->in6p_ip6_nxt != IPPROTO_ICMPV6)
+ continue;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
+ !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst))
+ continue;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) &&
+ !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
+ continue;
+ if (in6p->in6p_icmp6filt
+ && ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type,
+ in6p->in6p_icmp6filt))
+ continue;
+ if (last) {
+ struct mbuf *n;
+ if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
+ if (last->in6p_flags & IN6P_CONTROLOPTS)
+ ip6_savecontrol(last, &opts, ip6, n);
+ /* strip intermediate headers */
+ m_adj(n, off);
+ if (sbappendaddr(&last->in6p_socket->so_rcv,
+ (struct sockaddr *)&rip6src,
+ n, opts) == 0) {
+ /* should notify about lost packet */
+ m_freem(n);
+ if (opts)
+ m_freem(opts);
+ } else
+ sorwakeup(last->in6p_socket);
+ opts = NULL;
+ }
+ }
+ last = in6p;
+ }
+ if (last) {
+ if (last->in6p_flags & IN6P_CONTROLOPTS)
+ ip6_savecontrol(last, &opts, ip6, m);
+ /* strip intermediate headers */
+ m_adj(m, off);
+ if (sbappendaddr(&last->in6p_socket->so_rcv,
+ (struct sockaddr *)&rip6src, m, opts) == 0) {
+ m_freem(m);
+ if (opts)
+ m_freem(opts);
+ } else
+ sorwakeup(last->in6p_socket);
+ } else {
+ m_freem(m);
+ ip6stat.ip6s_delivered--;
+ }
+ return IPPROTO_DONE;
+}
+
+/*
+ * Reflect the ip6 packet back to the source.
+ * The caller MUST check if the destination is multicast or not.
+ * This function is usually called with a unicast destination which
+ * can be safely the source of the reply packet. But some exceptions
+ * exist(e.g. ECHOREPLY, PATCKET_TOOBIG, "10" in OPTION type).
+ * ``off'' points to the icmp6 header, counted from the top of the mbuf.
+ */
+void
+icmp6_reflect(m, off)
+ struct mbuf *m;
+ size_t off;
+{
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct icmp6_hdr *icmp6;
+ struct in6_ifaddr *ia;
+ struct in6_addr t, *src = 0;
+ int plen = m->m_pkthdr.len - sizeof(struct ip6_hdr);
+ int type, code;
+ struct ifnet *outif = NULL;
+#ifdef COMPAT_RFC1885
+ int mtu = IPV6_MMTU;
+ struct sockaddr_in6 *sin6 = &icmp6_reflect_rt.ro_dst;
+#endif
+
+ /*
+ * If there are extra headers between IPv6 and ICMPv6, strip
+ * off that header first.
+ */
+ if (off != sizeof(struct ip6_hdr)) {
+ size_t siz;
+
+ /* sanity checks */
+ if (off < sizeof(struct ip6_hdr)) {
+ printf("sanity fail: off=%x, sizeof(ip6)=%x in %s:%d\n",
+ (unsigned int)off,
+ (unsigned int)sizeof(struct ip6_hdr),
+ __FILE__, __LINE__);
+ goto bad;
+ }
+ siz = off - sizeof(struct ip6_hdr);
+ if (plen < siz) {
+ printf("sanity fail: siz=%x, payloadlen=%x in %s:%d\n",
+ (unsigned int)siz, plen, __FILE__, __LINE__);
+ goto bad;
+ }
+ IP6_EXTHDR_CHECK(m, 0, off, /*nothing*/);
+ IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), /*nothing*/);
+
+ ovbcopy((caddr_t)ip6,
+ (caddr_t)(mtod(m, u_char *) + siz),
+ sizeof(struct ip6_hdr));
+ m->m_data += siz;
+ m->m_len -= siz;
+ m->m_pkthdr.len -= siz;
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_nxt = IPPROTO_ICMPV6;
+ plen -= siz;
+ }
+
+ icmp6 = (struct icmp6_hdr *)(ip6 + 1);
+ type = icmp6->icmp6_type; /* keep type for statistics */
+ code = icmp6->icmp6_code; /* ditto. */
+
+ t = ip6->ip6_dst;
+ /*
+ * ip6_input() drops a packet if its src is multicast.
+ * So, the src is never multicast.
+ */
+ ip6->ip6_dst = ip6->ip6_src;
+
+ /* XXX hack for link-local addresses */
+ if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst))
+ ip6->ip6_dst.s6_addr16[1] =
+ htons(m->m_pkthdr.rcvif->if_index);
+ if (IN6_IS_ADDR_LINKLOCAL(&t))
+ t.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index);
+
+#ifdef COMPAT_RFC1885
+ /*
+ * xxx guess MTU
+ * RFC 1885 requires that echo reply should be truncated if it
+ * does not fit in with (return) path MTU, but the description was
+ * removed in the new spec.
+ */
+ if (icmp6_reflect_rt.ro_rt == 0 ||
+ ! (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ip6->ip6_dst))) {
+ if (icmp6_reflect_rt.ro_rt) {
+ RTFREE(icmp6_reflect_rt.ro_rt);
+ icmp6_reflect_rt.ro_rt = 0;
+ }
+ bzero(sin6, sizeof(*sin6));
+ sin6->sin6_family = PF_INET6;
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_addr = ip6->ip6_dst;
+
+ rtalloc_ign((struct route *)&icmp6_reflect_rt.ro_rt,
+ RTF_PRCLONING);
+ }
+
+ if (icmp6_reflect_rt.ro_rt == 0)
+ goto bad;
+
+ if ((icmp6_reflect_rt.ro_rt->rt_flags & RTF_HOST)
+ && mtu < icmp6_reflect_rt.ro_rt->rt_ifp->if_mtu)
+ mtu = icmp6_reflect_rt.ro_rt->rt_rmx.rmx_mtu;
+
+ if (mtu < m->m_pkthdr.len) {
+ plen -= (m->m_pkthdr.len - mtu);
+ m_adj(m, mtu - m->m_pkthdr.len);
+ }
+#endif
+ /*
+ * If the incoming packet was addressed directly to us(i.e. unicast),
+ * use dst as the src for the reply.
+ */
+ for (ia = in6_ifaddr; ia; ia = ia->ia_next)
+ if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) &&
+ (ia->ia6_flags & IN6_IFF_ANYCAST) == 0) {
+ src = &t;
+ break;
+ }
+ if (ia == NULL && IN6_IS_ADDR_LINKLOCAL(&t) && (m->m_flags & M_LOOP)) {
+ /*
+ * This is the case if the dst is our link-local address
+ * and the sender is also ourseleves.
+ */
+ src = &t;
+ }
+
+ if (src == 0)
+ /*
+ * We have not multicast routing yet. So this case matches
+ * to our multicast, our anycast or not to our unicast.
+ * Select a source address which has the same scope.
+ */
+ if ((ia = in6_ifawithscope(m->m_pkthdr.rcvif, &t)) != 0)
+ src = &IA6_SIN6(ia)->sin6_addr;
+
+ if (src == 0)
+ goto bad;
+
+ ip6->ip6_src = *src;
+
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc = IPV6_VERSION;
+ ip6->ip6_nxt = IPPROTO_ICMPV6;
+ if (m->m_pkthdr.rcvif) {
+ /* XXX: This may not be the outgoing interface */
+ ip6->ip6_hlim = nd_ifinfo[m->m_pkthdr.rcvif->if_index].chlim;
+ }
+
+ icmp6->icmp6_cksum = 0;
+ icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6,
+ sizeof(struct ip6_hdr), plen);
+
+ /*
+ * xxx option handling
+ */
+
+ m->m_flags &= ~(M_BCAST|M_MCAST);
+#ifdef IPSEC
+ m->m_pkthdr.rcvif = NULL;
+#endif /*IPSEC*/
+
+#ifdef COMPAT_RFC1885
+ ip6_output(m, NULL, &icmp6_reflect_rt, 0, NULL, &outif);
+#else
+ ip6_output(m, NULL, NULL, 0, NULL, &outif);
+#endif
+ if (outif)
+ icmp6_ifoutstat_inc(outif, type, code);
+
+ return;
+
+ bad:
+ m_freem(m);
+ return;
+}
+
+void
+icmp6_fasttimo()
+{
+ mld6_fasttimeo();
+}
+
+static const char *
+icmp6_redirect_diag(src6, dst6, tgt6)
+ struct in6_addr *src6;
+ struct in6_addr *dst6;
+ struct in6_addr *tgt6;
+{
+ static char buf[1024];
+ snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)",
+ ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6));
+ return buf;
+}
+
+void
+icmp6_redirect_input(m, off)
+ register struct mbuf *m;
+ int off;
+{
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct nd_redirect *nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off);
+ int icmp6len = ntohs(ip6->ip6_plen);
+ char *lladdr = NULL;
+ int lladdrlen = 0;
+ u_char *redirhdr = NULL;
+ int redirhdrlen = 0;
+ struct rtentry *rt = NULL;
+ int is_router;
+ int is_onlink;
+ struct in6_addr src6 = ip6->ip6_src;
+ struct in6_addr redtgt6 = nd_rd->nd_rd_target;
+ struct in6_addr reddst6 = nd_rd->nd_rd_dst;
+ union nd_opts ndopts;
+
+ if (!m || !ifp)
+ return;
+
+ /* XXX if we are router, we don't update route by icmp6 redirect */
+ if (ip6_forwarding)
+ return;
+ if (!icmp6_rediraccept)
+ return;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
+ redtgt6.s6_addr16[1] = htons(ifp->if_index);
+ if (IN6_IS_ADDR_LINKLOCAL(&reddst6))
+ reddst6.s6_addr16[1] = htons(ifp->if_index);
+
+ /* validation */
+ if (!IN6_IS_ADDR_LINKLOCAL(&src6)) {
+ log(LOG_ERR,
+ "ICMP6 redirect sent from %s rejected; "
+ "must be from linklocal\n", ip6_sprintf(&src6));
+ return;
+ }
+ if (ip6->ip6_hlim != 255) {
+ log(LOG_ERR,
+ "ICMP6 redirect sent from %s rejected; "
+ "hlim=%d (must be 255)\n",
+ ip6_sprintf(&src6), ip6->ip6_hlim);
+ return;
+ }
+ {
+ /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */
+ struct sockaddr_in6 sin6;
+ struct in6_addr *gw6;
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6));
+ rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
+ if (rt) {
+ gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr);
+ if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) {
+ log(LOG_ERR,
+ "ICMP6 redirect rejected; "
+ "not equal to gw-for-src=%s (must be same): "
+ "%s\n",
+ ip6_sprintf(gw6),
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
+ RTFREE(rt);
+ return;
+ }
+ } else {
+ log(LOG_ERR,
+ "ICMP6 redirect rejected; "
+ "no route found for redirect dst: %s\n",
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
+ return;
+ }
+ RTFREE(rt);
+ rt = NULL;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&reddst6)) {
+ log(LOG_ERR,
+ "ICMP6 redirect rejected; "
+ "redirect dst must be unicast: %s\n",
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
+ return;
+ }
+
+ is_router = is_onlink = 0;
+ if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
+ is_router = 1; /* router case */
+ if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0)
+ is_onlink = 1; /* on-link destination case */
+ if (!is_router && !is_onlink) {
+ log(LOG_ERR,
+ "ICMP6 redirect rejected; "
+ "neither router case nor onlink case: %s\n",
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
+ return;
+ }
+ /* validation passed */
+
+ icmp6len -= sizeof(*nd_rd);
+ nd6_option_init(nd_rd + 1, icmp6len, &ndopts);
+ if (nd6_options(&ndopts) < 0) {
+ log(LOG_INFO, "icmp6_redirect_input: "
+ "invalid ND option, rejected: %s\n",
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
+ return;
+ }
+
+ if (ndopts.nd_opts_tgt_lladdr) {
+ lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);
+ lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
+ }
+
+ if (ndopts.nd_opts_rh) {
+ redirhdrlen = ndopts.nd_opts_rh->nd_opt_rh_len;
+ redirhdr = (u_char *)(ndopts.nd_opts_rh + 1); /* xxx */
+ }
+
+ if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
+ log(LOG_INFO,
+ "icmp6_redirect_input: lladdrlen mismatch for %s "
+ "(if %d, icmp6 packet %d): %s\n",
+ ip6_sprintf(&redtgt6), ifp->if_addrlen, lladdrlen - 2,
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
+ }
+
+ /* RFC 2461 8.3 */
+ nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
+ is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
+
+ if (!is_onlink) { /* better router case. perform rtredirect. */
+ /* perform rtredirect */
+ struct sockaddr_in6 sdst;
+ struct sockaddr_in6 sgw;
+ struct sockaddr_in6 ssrc;
+
+ bzero(&sdst, sizeof(sdst));
+ bzero(&sgw, sizeof(sgw));
+ bzero(&ssrc, sizeof(ssrc));
+ sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6;
+ sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len =
+ sizeof(struct sockaddr_in6);
+ bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr));
+ bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
+ bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr));
+ rtredirect((struct sockaddr *)&sdst, (struct sockaddr *)&sgw,
+ (struct sockaddr *)NULL, RTF_GATEWAY | RTF_HOST,
+ (struct sockaddr *)&ssrc,
+ (struct rtentry **)NULL);
+ }
+ /* finally update cached route in each socket via pfctlinput */
+ {
+ struct sockaddr_in6 sdst;
+
+ bzero(&sdst, sizeof(sdst));
+ sdst.sin6_family = AF_INET6;
+ sdst.sin6_len = sizeof(struct sockaddr_in6);
+ bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
+ pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst);
+#ifdef IPSEC
+ key_sa_routechange((struct sockaddr *)&sdst);
+#endif
+ }
+}
+
+void
+icmp6_redirect_output(m0, rt)
+ struct mbuf *m0;
+ struct rtentry *rt;
+{
+ struct ifnet *ifp; /* my outgoing interface */
+ struct in6_addr *ifp_ll6;
+ struct in6_addr *router_ll6;
+ struct ip6_hdr *sip6; /* m0 as struct ip6_hdr */
+ struct mbuf *m = NULL; /* newly allocated one */
+ struct ip6_hdr *ip6; /* m as struct ip6_hdr */
+ struct nd_redirect *nd_rd;
+ size_t maxlen;
+ u_char *p;
+ struct ifnet *outif = NULL;
+
+ /* if we are not router, we don't send icmp6 redirect */
+ if (!ip6_forwarding || ip6_accept_rtadv)
+ goto fail;
+
+ /* sanity check */
+ if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp))
+ goto fail;
+
+ /*
+ * Address check:
+ * the source address must identify a neighbor, and
+ * the destination address must not be a multicast address
+ * [RFC 2461, sec 8.2]
+ */
+ sip6 = mtod(m0, struct ip6_hdr *);
+ if (nd6_is_addr_neighbor(&sip6->ip6_src, ifp) == 0)
+ goto fail;
+ if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst))
+ goto fail; /* what should we do here? */
+
+ /* rate limit */
+ if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0))
+ goto fail;
+
+ /*
+ * Since we are going to append up to 1280 bytes (= IPV6_MMTU),
+ * we almost always ask for an mbuf cluster for simplicity.
+ * (MHLEN < IPV6_MMTU is almost always true)
+ */
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (!m)
+ goto fail;
+ if (MHLEN < IPV6_MMTU)
+ MCLGET(m, M_DONTWAIT);
+ maxlen = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN;
+ maxlen = min(IPV6_MMTU, maxlen);
+ /* just for safety */
+ if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
+ goto fail;
+
+ {
+ /* get ip6 linklocal address for ifp(my outgoing interface). */
+ struct in6_ifaddr *ia = in6ifa_ifpforlinklocal(ifp);
+ if (ia == NULL)
+ goto fail;
+ ifp_ll6 = &ia->ia_addr.sin6_addr;
+ }
+
+ /* get ip6 linklocal address for the router. */
+ if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) {
+ struct sockaddr_in6 *sin6;
+ sin6 = (struct sockaddr_in6 *)rt->rt_gateway;
+ router_ll6 = &sin6->sin6_addr;
+ if (!IN6_IS_ADDR_LINKLOCAL(router_ll6))
+ router_ll6 = (struct in6_addr *)NULL;
+ } else
+ router_ll6 = (struct in6_addr *)NULL;
+
+ /* ip6 */
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc = IPV6_VERSION;
+ /* ip6->ip6_plen will be set later */
+ ip6->ip6_nxt = IPPROTO_ICMPV6;
+ ip6->ip6_hlim = 255;
+ /* ip6->ip6_src must be linklocal addr for my outgoing if. */
+ bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr));
+ bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr));
+
+ /* ND Redirect */
+ nd_rd = (struct nd_redirect *)(ip6 + 1);
+ nd_rd->nd_rd_type = ND_REDIRECT;
+ nd_rd->nd_rd_code = 0;
+ nd_rd->nd_rd_reserved = 0;
+ if (rt->rt_flags & RTF_GATEWAY) {
+ /*
+ * nd_rd->nd_rd_target must be a link-local address in
+ * better router cases.
+ */
+ if (!router_ll6)
+ goto fail;
+ bcopy(router_ll6, &nd_rd->nd_rd_target,
+ sizeof(nd_rd->nd_rd_target));
+ bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
+ sizeof(nd_rd->nd_rd_dst));
+ } else {
+ /* make sure redtgt == reddst */
+ bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target,
+ sizeof(nd_rd->nd_rd_target));
+ bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
+ sizeof(nd_rd->nd_rd_dst));
+ }
+
+ p = (u_char *)(nd_rd + 1);
+
+ if (!router_ll6)
+ goto nolladdropt;
+
+ {
+ /* target lladdr option */
+ struct rtentry *rt_router = NULL;
+ int len;
+ struct sockaddr_dl *sdl;
+ struct nd_opt_hdr *nd_opt;
+ char *lladdr;
+
+ rt_router = nd6_lookup(router_ll6, 0, ifp);
+ if (!rt_router)
+ goto nolladdropt;
+ if (!(rt_router->rt_flags & RTF_GATEWAY)
+ && (rt_router->rt_flags & RTF_LLINFO)
+ && (rt_router->rt_gateway->sa_family == AF_LINK)
+ && (sdl = (struct sockaddr_dl *)rt_router->rt_gateway)) {
+ nd_opt = (struct nd_opt_hdr *)p;
+ nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
+ len = 2 + ifp->if_addrlen;
+ len = (len + 7) & ~7; /*round by 8*/
+ nd_opt->nd_opt_len = len >> 3;
+ p += len;
+ lladdr = (char *)(nd_opt + 1);
+ bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen);
+ }
+ }
+nolladdropt:;
+
+ m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
+
+ /* just to be safe */
+ if (m0->m_flags & M_DECRYPTED)
+ goto noredhdropt;
+
+ {
+ /* redirected header option */
+ int len;
+ struct nd_opt_rd_hdr *nd_opt_rh;
+
+ /*
+ * compute the maximum size for icmp6 redirect header option.
+ * XXX room for auth header?
+ */
+ len = maxlen - (p - (u_char *)ip6);
+ len &= ~7;
+
+ /* This is just for simplicity. */
+ if (m0->m_pkthdr.len != m0->m_len) {
+ if (m0->m_next) {
+ m_freem(m0->m_next);
+ m0->m_next = NULL;
+ }
+ m0->m_pkthdr.len = m0->m_len;
+ }
+
+ /*
+ * Redirected header option spec (RFC2461 4.6.3) talks nothing
+ * about padding/truncate rule for the original IP packet.
+ * From the discussion on IPv6imp in Feb 1999, the consensus was:
+ * - "attach as much as possible" is the goal
+ * - pad if not aligned (original size can be guessed by original
+ * ip6 header)
+ * Following code adds the padding if it is simple enough,
+ * and truncates if not.
+ */
+ if (m0->m_next || m0->m_pkthdr.len != m0->m_len)
+ panic("assumption failed in %s:%d\n", __FILE__, __LINE__);
+
+ if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) {
+ /* not enough room, truncate */
+ m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh);
+ } else {
+ /* enough room, pad or truncate */
+ size_t extra;
+
+ extra = m0->m_pkthdr.len % 8;
+ if (extra) {
+ /* pad if easy enough, truncate if not */
+ if (8 - extra <= M_TRAILINGSPACE(m0)) {
+ /* pad */
+ m0->m_len += (8 - extra);
+ m0->m_pkthdr.len += (8 - extra);
+ } else {
+ /* truncate */
+ m0->m_pkthdr.len -= extra;
+ m0->m_len -= extra;
+ }
+ }
+ len = m0->m_pkthdr.len + sizeof(*nd_opt_rh);
+ m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh);
+ }
+
+ nd_opt_rh = (struct nd_opt_rd_hdr *)p;
+ bzero(nd_opt_rh, sizeof(*nd_opt_rh));
+ nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER;
+ nd_opt_rh->nd_opt_rh_len = len >> 3;
+ p += sizeof(*nd_opt_rh);
+ m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
+
+ /* connect m0 to m */
+ m->m_next = m0;
+ m->m_pkthdr.len = m->m_len + m0->m_len;
+ }
+noredhdropt:;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_src))
+ sip6->ip6_src.s6_addr16[1] = 0;
+ if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_dst))
+ sip6->ip6_dst.s6_addr16[1] = 0;
+ if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_target))
+ nd_rd->nd_rd_target.s6_addr16[1] = 0;
+ if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_dst))
+ nd_rd->nd_rd_dst.s6_addr16[1] = 0;
+
+ ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
+
+ nd_rd->nd_rd_cksum = 0;
+ nd_rd->nd_rd_cksum
+ = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen));
+
+ /* send the packet to outside... */
+#ifdef IPSEC
+ m->m_pkthdr.rcvif = NULL;
+#endif /*IPSEC*/
+ ip6_output(m, NULL, NULL, 0, NULL, &outif);
+ if (outif) {
+ icmp6_ifstat_inc(outif, ifs6_out_msg);
+ icmp6_ifstat_inc(outif, ifs6_out_redirect);
+ }
+ icmp6stat.icp6s_outhist[ND_REDIRECT]++;
+
+ return;
+
+fail:
+ if (m)
+ m_freem(m);
+ if (m0)
+ m_freem(m0);
+}
+
+/*
+ * ICMPv6 socket option processing.
+ */
+int
+icmp6_ctloutput(so, sopt)
+ struct socket *so;
+ struct sockopt *sopt;
+{
+ int error = 0;
+ int optlen;
+ register struct inpcb *inp = sotoinpcb(so);
+ int level, op, optname;
+
+ if (sopt) {
+ level = sopt->sopt_level;
+ op = sopt->sopt_dir;
+ optname = sopt->sopt_name;
+ optlen = sopt->sopt_valsize;
+ } else
+ level = op = optname = optlen = 0;
+ if (level != IPPROTO_ICMPV6) {
+ return EINVAL;
+ }
+
+ switch(op) {
+ case PRCO_SETOPT:
+ switch (optname) {
+ case ICMP6_FILTER:
+ {
+ struct icmp6_filter *p;
+
+ if (optlen != sizeof(*p)) {
+ error = EMSGSIZE;
+ break;
+ }
+ if (inp->in6p_icmp6filt == NULL) {
+ error = EINVAL;
+ break;
+ }
+ error = sooptcopyin(sopt, inp->in6p_icmp6filt, optlen,
+ optlen);
+ break;
+ }
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+
+ case PRCO_GETOPT:
+ switch (optname) {
+ case ICMP6_FILTER:
+ {
+ if (inp->in6p_icmp6filt == NULL) {
+ error = EINVAL;
+ break;
+ }
+ error = sooptcopyout(sopt, inp->in6p_icmp6filt,
+ sizeof(struct icmp6_filter));
+ break;
+ }
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+ }
+
+ return(error);
+}
+
+/*
+ * Perform rate limit check.
+ * Returns 0 if it is okay to send the icmp6 packet.
+ * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate
+ * limitation.
+ *
+ * XXX per-destination/type check necessary?
+ */
+static int
+icmp6_ratelimit(dst, type, code)
+ const struct in6_addr *dst; /* not used at this moment */
+ const int type; /* not used at this moment */
+ const int code; /* not used at this moment */
+{
+ struct timeval tp;
+ long sec_diff, usec_diff;
+
+ /* If we are not doing rate limitation, it is always okay to send */
+ if (!icmp6errratelim)
+ return 0;
+
+ microtime(&tp);
+ tp.tv_sec = time_second;
+ if (tp.tv_sec < icmp6_nextsend.tv_sec
+ || (tp.tv_sec == icmp6_nextsend.tv_sec
+ && tp.tv_usec < icmp6_nextsend.tv_usec)) {
+ /* The packet is subject to rate limit */
+ return 1;
+ }
+ sec_diff = icmp6errratelim / 1000000;
+ usec_diff = icmp6errratelim % 1000000;
+ icmp6_nextsend.tv_sec = tp.tv_sec + sec_diff;
+ if ((tp.tv_usec = tp.tv_usec + usec_diff) >= 1000000) {
+ icmp6_nextsend.tv_sec++;
+ icmp6_nextsend.tv_usec -= 1000000;
+ }
+
+ /* it is okay to send this */
+ return 0;
+}
diff --git a/sys/netinet6/icmp6.h b/sys/netinet6/icmp6.h
new file mode 100644
index 0000000..8fd63ff
--- /dev/null
+++ b/sys/netinet6/icmp6.h
@@ -0,0 +1,602 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _NETINET6_ICMPV6_H_
+#define _NETINET6_ICMPV6_H_
+
+#define ICMPV6_PLD_MAXLEN 1232 /* IPV6_MMTU - sizeof(struct ip6_hdr)
+ - sizeof(struct icmp6_hdr) */
+
+struct icmp6_hdr {
+ u_int8_t icmp6_type; /* type field */
+ u_int8_t icmp6_code; /* code field */
+ u_int16_t icmp6_cksum; /* checksum field */
+ union {
+ u_int32_t icmp6_un_data32[1]; /* type-specific field */
+ u_int16_t icmp6_un_data16[2]; /* type-specific field */
+ u_int8_t icmp6_un_data8[4]; /* type-specific field */
+ } icmp6_dataun;
+};
+
+#define icmp6_data32 icmp6_dataun.icmp6_un_data32
+#define icmp6_data16 icmp6_dataun.icmp6_un_data16
+#define icmp6_data8 icmp6_dataun.icmp6_un_data8
+#define icmp6_pptr icmp6_data32[0] /* parameter prob */
+#define icmp6_mtu icmp6_data32[0] /* packet too big */
+#define icmp6_id icmp6_data16[0] /* echo request/reply */
+#define icmp6_seq icmp6_data16[1] /* echo request/reply */
+#define icmp6_maxdelay icmp6_data16[0] /* mcast group membership */
+
+#define ICMP6_DST_UNREACH 1 /* dest unreachable, codes: */
+#define ICMP6_PACKET_TOO_BIG 2 /* packet too big */
+#define ICMP6_TIME_EXCEEDED 3 /* time exceeded, code: */
+#define ICMP6_PARAM_PROB 4 /* ip6 header bad */
+
+#define ICMP6_ECHO_REQUEST 128 /* echo service */
+#define ICMP6_ECHO_REPLY 129 /* echo reply */
+#define ICMP6_MEMBERSHIP_QUERY 130 /* group membership query */
+#define MLD6_LISTENER_QUERY 130 /* multicast listener query */
+#define ICMP6_MEMBERSHIP_REPORT 131 /* group membership report */
+#define MLD6_LISTENER_REPORT 131 /* multicast listener report */
+#define ICMP6_MEMBERSHIP_REDUCTION 132 /* group membership termination */
+#define MLD6_LISTENER_DONE 132 /* multicast listener done */
+
+#define ND_ROUTER_SOLICIT 133 /* router solicitation */
+#define ND_ROUTER_ADVERT 134 /* router advertisment */
+#define ND_NEIGHBOR_SOLICIT 135 /* neighbor solicitation */
+#define ND_NEIGHBOR_ADVERT 136 /* neighbor advertisment */
+#define ND_REDIRECT 137 /* redirect */
+
+#define ICMP6_ROUTER_RENUMBERING 138 /* router renumbering */
+
+#define ICMP6_WRUREQUEST 139 /* who are you request */
+#define ICMP6_WRUREPLY 140 /* who are you reply */
+#define ICMP6_FQDN_QUERY 139 /* FQDN query */
+#define ICMP6_FQDN_REPLY 140 /* FQDN reply */
+#define ICMP6_NI_QUERY 139 /* node information request */
+#define ICMP6_NI_REPLY 140 /* node information reply */
+
+/* The definitions below are experimental. TBA */
+#define MLD6_MTRACE_RESP 141 /* mtrace response(to sender) */
+#define MLD6_MTRACE 142 /* mtrace messages */
+
+#define ICMP6_MAXTYPE 142
+
+#define ICMP6_DST_UNREACH_NOROUTE 0 /* no route to destination */
+#define ICMP6_DST_UNREACH_ADMIN 1 /* administratively prohibited */
+#define ICMP6_DST_UNREACH_NOTNEIGHBOR 2 /* not a neighbor(obsolete) */
+#define ICMP6_DST_UNREACH_BEYONDSCOPE 2 /* beyond scope of source address */
+#define ICMP6_DST_UNREACH_ADDR 3 /* address unreachable */
+#define ICMP6_DST_UNREACH_NOPORT 4 /* port unreachable */
+
+#define ICMP6_TIME_EXCEED_TRANSIT 0 /* ttl==0 in transit */
+#define ICMP6_TIME_EXCEED_REASSEMBLY 1 /* ttl==0 in reass */
+
+#define ICMP6_PARAMPROB_HEADER 0 /* erroneous header field */
+#define ICMP6_PARAMPROB_NEXTHEADER 1 /* unrecognized next header */
+#define ICMP6_PARAMPROB_OPTION 2 /* unrecognized option */
+
+#define ICMP6_INFOMSG_MASK 0x80 /* all informational messages */
+
+#define ICMP6_NI_SUCESS 0 /* node information successful reply */
+#define ICMP6_NI_REFUSED 1 /* node information request is refused */
+#define ICMP6_NI_UNKNOWN 2 /* unknown Qtype */
+
+#define ICMP6_ROUTER_RENUMBERING_COMMAND 0 /* rr command */
+#define ICMP6_ROUTER_RENUMBERING_RESULT 1 /* rr result */
+#define ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET 255 /* rr seq num reset */
+
+/* Used in kernel only */
+#define ND_REDIRECT_ONLINK 0 /* redirect to an on-link node */
+#define ND_REDIRECT_ROUTER 1 /* redirect to a better router */
+
+/*
+ * Multicast Listener Discovery
+ */
+struct mld6_hdr {
+ struct icmp6_hdr mld6_hdr;
+ struct in6_addr mld6_addr; /* multicast address */
+};
+
+#define mld6_type mld6_hdr.icmp6_type
+#define mld6_code mld6_hdr.icmp6_code
+#define mld6_cksum mld6_hdr.icmp6_cksum
+#define mld6_maxdelay mld6_hdr.icmp6_data16[0]
+#define mld6_reserved mld6_hdr.icmp6_data16[1]
+
+/*
+ * Neighbor Discovery
+ */
+
+struct nd_router_solicit { /* router solicitation */
+ struct icmp6_hdr nd_rs_hdr;
+ /* could be followed by options */
+};
+
+#define nd_rs_type nd_rs_hdr.icmp6_type
+#define nd_rs_code nd_rs_hdr.icmp6_code
+#define nd_rs_cksum nd_rs_hdr.icmp6_cksum
+#define nd_rs_reserved nd_rs_hdr.icmp6_data32[0]
+
+struct nd_router_advert { /* router advertisement */
+ struct icmp6_hdr nd_ra_hdr;
+ u_int32_t nd_ra_reachable; /* reachable time */
+ u_int32_t nd_ra_retransmit; /* retransmit timer */
+ /* could be followed by options */
+};
+
+#define nd_ra_type nd_ra_hdr.icmp6_type
+#define nd_ra_code nd_ra_hdr.icmp6_code
+#define nd_ra_cksum nd_ra_hdr.icmp6_cksum
+#define nd_ra_curhoplimit nd_ra_hdr.icmp6_data8[0]
+#define nd_ra_flags_reserved nd_ra_hdr.icmp6_data8[1]
+#define ND_RA_FLAG_MANAGED 0x80
+#define ND_RA_FLAG_OTHER 0x40
+#define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1]
+
+struct nd_neighbor_solicit { /* neighbor solicitation */
+ struct icmp6_hdr nd_ns_hdr;
+ struct in6_addr nd_ns_target; /*target address */
+ /* could be followed by options */
+};
+
+#define nd_ns_type nd_ns_hdr.icmp6_type
+#define nd_ns_code nd_ns_hdr.icmp6_code
+#define nd_ns_cksum nd_ns_hdr.icmp6_cksum
+#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0]
+
+struct nd_neighbor_advert { /* neighbor advertisement */
+ struct icmp6_hdr nd_na_hdr;
+ struct in6_addr nd_na_target; /* target address */
+ /* could be followed by options */
+};
+
+#define nd_na_type nd_na_hdr.icmp6_type
+#define nd_na_code nd_na_hdr.icmp6_code
+#define nd_na_cksum nd_na_hdr.icmp6_cksum
+#define nd_na_flags_reserved nd_na_hdr.icmp6_data32[0]
+#if BYTE_ORDER == BIG_ENDIAN
+#define ND_NA_FLAG_ROUTER 0x80000000
+#define ND_NA_FLAG_SOLICITED 0x40000000
+#define ND_NA_FLAG_OVERRIDE 0x20000000
+#elif BYTE_ORDER == LITTLE_ENDIAN
+#define ND_NA_FLAG_ROUTER 0x80
+#define ND_NA_FLAG_SOLICITED 0x40
+#define ND_NA_FLAG_OVERRIDE 0x20
+#endif
+
+struct nd_redirect { /* redirect */
+ struct icmp6_hdr nd_rd_hdr;
+ struct in6_addr nd_rd_target; /* target address */
+ struct in6_addr nd_rd_dst; /* destination address */
+ /* could be followed by options */
+};
+
+#define nd_rd_type nd_rd_hdr.icmp6_type
+#define nd_rd_code nd_rd_hdr.icmp6_code
+#define nd_rd_cksum nd_rd_hdr.icmp6_cksum
+#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0]
+
+struct nd_opt_hdr { /* Neighbor discovery option header */
+ u_int8_t nd_opt_type;
+ u_int8_t nd_opt_len;
+ /* followed by option specific data*/
+};
+
+#define ND_OPT_SOURCE_LINKADDR 1
+#define ND_OPT_TARGET_LINKADDR 2
+#define ND_OPT_PREFIX_INFORMATION 3
+#define ND_OPT_REDIRECTED_HEADER 4
+#define ND_OPT_MTU 5
+
+struct nd_opt_prefix_info { /* prefix information */
+ u_int8_t nd_opt_pi_type;
+ u_int8_t nd_opt_pi_len;
+ u_int8_t nd_opt_pi_prefix_len;
+ u_int8_t nd_opt_pi_flags_reserved;
+ u_int32_t nd_opt_pi_valid_time;
+ u_int32_t nd_opt_pi_preferred_time;
+ u_int32_t nd_opt_pi_reserved2;
+ struct in6_addr nd_opt_pi_prefix;
+};
+
+#define ND_OPT_PI_FLAG_ONLINK 0x80
+#define ND_OPT_PI_FLAG_AUTO 0x40
+
+struct nd_opt_rd_hdr { /* redirected header */
+ u_int8_t nd_opt_rh_type;
+ u_int8_t nd_opt_rh_len;
+ u_int16_t nd_opt_rh_reserved1;
+ u_int32_t nd_opt_rh_reserved2;
+ /* followed by IP header and data */
+};
+
+struct nd_opt_mtu { /* MTU option */
+ u_int8_t nd_opt_mtu_type;
+ u_int8_t nd_opt_mtu_len;
+ u_int16_t nd_opt_mtu_reserved;
+ u_int32_t nd_opt_mtu_mtu;
+};
+
+/*
+ * icmp6 namelookup
+ */
+
+struct icmp6_namelookup {
+ struct icmp6_hdr icmp6_nl_hdr;
+ u_int64_t icmp6_nl_nonce;
+ u_int32_t icmp6_nl_ttl;
+ /* could be followed by options */
+};
+
+/*
+ * icmp6 node information
+ */
+struct icmp6_nodeinfo {
+ struct icmp6_hdr icmp6_ni_hdr;
+ u_int64_t icmp6_ni_nonce;
+ /* could be followed by reply data */
+};
+
+#define ni_type icmp6_ni_hdr.icmp6_type
+#define ni_code icmp6_ni_hdr.icmp6_code
+#define ni_cksum icmp6_ni_hdr.icmp6_cksum
+#define ni_qtype icmp6_ni_hdr.icmp6_data16[0]
+#define ni_flags icmp6_ni_hdr.icmp6_data16[1]
+
+
+#define NI_QTYPE_NOOP 0 /* NOOP */
+#define NI_QTYPE_SUPTYPES 1 /* Supported Qtypes */
+#define NI_QTYPE_FQDN 2 /* FQDN */
+#define NI_QTYPE_NODEADDR 3 /* Node Addresses. XXX: spec says 2, but it may be a typo... */
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define NI_SUPTYPE_FLAG_COMPRESS 0x1
+#define NI_FQDN_FLAG_VALIDTTL 0x1
+#define NI_NODEADDR_FLAG_LINKLOCAL 0x1
+#define NI_NODEADDR_FLAG_SITELOCAL 0x2
+#define NI_NODEADDR_FLAG_GLOBAL 0x4
+#define NI_NODEADDR_FLAG_ALL 0x8
+#define NI_NODEADDR_FLAG_TRUNCATE 0x10
+#define NI_NODEADDR_FLAG_ANYCAST 0x20 /* just experimental. not in spec */
+#elif BYTE_ORDER == LITTLE_ENDIAN
+#define NI_SUPTYPE_FLAG_COMPRESS 0x0100
+#define NI_FQDN_FLAG_VALIDTTL 0x0100
+#define NI_NODEADDR_FLAG_LINKLOCAL 0x0100
+#define NI_NODEADDR_FLAG_SITELOCAL 0x0200
+#define NI_NODEADDR_FLAG_GLOBAL 0x0400
+#define NI_NODEADDR_FLAG_ALL 0x0800
+#define NI_NODEADDR_FLAG_TRUNCATE 0x1000
+#define NI_NODEADDR_FLAG_ANYCAST 0x2000 /* just experimental. not in spec */
+#endif
+
+struct ni_reply_fqdn {
+ u_int32_t ni_fqdn_ttl; /* TTL */
+ u_int8_t ni_fqdn_namelen; /* length in octets of the FQDN */
+ u_int8_t ni_fqdn_name[3]; /* XXX: alignment */
+};
+
+/*
+ * Router Renumbering. as router-renum-08.txt
+ */
+struct icmp6_router_renum { /* router renumbering header */
+ struct icmp6_hdr rr_hdr;
+ u_int8_t rr_segnum;
+ u_int8_t rr_flags;
+ u_int16_t rr_maxdelay;
+ u_int32_t rr_reserved;
+};
+#define ICMP6_RR_FLAGS_SEGNUM 0x80
+#define ICMP6_RR_FLAGS_TEST 0x40
+#define ICMP6_RR_FLAGS_REQRESULT 0x20
+#define ICMP6_RR_FLAGS_FORCEAPPLY 0x10
+#define ICMP6_RR_FLAGS_SPECSITE 0x08
+#define ICMP6_RR_FLAGS_PREVDONE 0x04
+
+#define rr_type rr_hdr.icmp6_type
+#define rr_code rr_hdr.icmp6_code
+#define rr_cksum rr_hdr.icmp6_cksum
+#define rr_seqnum rr_hdr.icmp6_data32[0]
+
+struct rr_pco_match { /* match prefix part */
+ u_int8_t rpm_code;
+ u_int8_t rpm_len;
+ u_int8_t rpm_ordinal;
+ u_int8_t rpm_matchlen;
+ u_int8_t rpm_minlen;
+ u_int8_t rpm_maxlen;
+ u_int16_t rpm_reserved;
+ struct in6_addr rpm_prefix;
+};
+
+#define RPM_PCO_ADD 1
+#define RPM_PCO_CHANGE 2
+#define RPM_PCO_SETGLOBAL 3
+
+struct rr_pco_use { /* use prefix part */
+ u_int8_t rpu_uselen;
+ u_int8_t rpu_keeplen;
+ u_int8_t rpu_ramask;
+ u_int8_t rpu_raflags;
+ u_int32_t rpu_vltime;
+ u_int32_t rpu_pltime;
+ u_int32_t rpu_flags;
+ struct in6_addr rpu_prefix;
+};
+#define ICMP6_RR_PCOUSE_RAFLAGS_ONLINK 0x20
+#define ICMP6_RR_PCOUSE_RAFLAGS_AUTO 0x10
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME 0x80000000
+#define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME 0x40000000
+#elif BYTE_ORDER == LITTLE_ENDIAN
+#define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME 0x80
+#define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME 0x40
+#endif
+
+struct rr_result { /* router renumbering result message */
+ u_int16_t rrr_flags;
+ u_int8_t rrr_ordinal;
+ u_int8_t rrr_matchedlen;
+ u_int32_t rrr_ifid;
+ struct in6_addr rrr_prefix;
+};
+#if BYTE_ORDER == BIG_ENDIAN
+#define ICMP6_RR_RESULT_FLAGS_OOB 0x0002
+#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x0001
+#elif BYTE_ORDER == LITTLE_ENDIAN
+#define ICMP6_RR_RESULT_FLAGS_OOB 0x02
+#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x01
+#endif
+
+/*
+ * icmp6 filter structures.
+ */
+
+struct icmp6_filter {
+ u_int32_t icmp6_filter[8];
+};
+
+#ifdef _KERNEL
+#define ICMP6_FILTER_SETPASSALL(filterp) \
+ { \
+ int i; u_char *p; \
+ p = (u_char *)filterp; \
+ for (i = 0; i < sizeof(struct icmp6_filter); i++) \
+ p[i] = 0xff; \
+ }
+#define ICMP6_FILTER_SETBLOCKALL(filterp) \
+ bzero(filterp, sizeof(struct icmp6_filter))
+#else /* _KERNEL */
+#define ICMP6_FILTER_SETPASSALL(filterp) \
+ memset(filterp, 0xff, sizeof(struct icmp6_filter))
+#define ICMP6_FILTER_SETBLOCKALL(filterp) \
+ memset(filterp, 0x00, sizeof(struct icmp6_filter))
+#endif /* _KERNEL */
+
+#define ICMP6_FILTER_SETPASS(type, filterp) \
+ (((filterp)->icmp6_filter[(type) >> 5]) |= (1 << ((type) & 31)))
+#define ICMP6_FILTER_SETBLOCK(type, filterp) \
+ (((filterp)->icmp6_filter[(type) >> 5]) &= ~(1 << ((type) & 31)))
+#define ICMP6_FILTER_WILLPASS(type, filterp) \
+ ((((filterp)->icmp6_filter[(type) >> 5]) & (1 << ((type) & 31))) != 0)
+#define ICMP6_FILTER_WILLBLOCK(type, filterp) \
+ ((((filterp)->icmp6_filter[(type) >> 5]) & (1 << ((type) & 31))) == 0)
+
+/*
+ * Variables related to this implementation
+ * of the internet control message protocol version 6.
+ */
+struct icmp6stat {
+/* statistics related to icmp6 packets generated */
+ u_long icp6s_error; /* # of calls to icmp6_error */
+ u_long icp6s_canterror; /* no error 'cuz old was icmp */
+ u_long icp6s_toofreq; /* no error 'cuz rate limitation */
+ u_long icp6s_outhist[256];
+/* statistics related to input messages proccesed */
+ u_long icp6s_badcode; /* icmp6_code out of range */
+ u_long icp6s_tooshort; /* packet < sizeof(struct icmp6_hdr) */
+ u_long icp6s_checksum; /* bad checksum */
+ u_long icp6s_badlen; /* calculated bound mismatch */
+ u_long icp6s_reflect; /* number of responses */
+ u_long icp6s_inhist[256];
+};
+
+/*
+ * Names for ICMP sysctl objects
+ */
+#define ICMPV6CTL_STATS 1
+#define ICMPV6CTL_REDIRACCEPT 2 /* accept/process redirects */
+#define ICMPV6CTL_REDIRTIMEOUT 3 /* redirect cache time */
+#define ICMPV6CTL_ERRRATELIMIT 5 /* ICMPv6 error rate limitation */
+#define ICMPV6CTL_ND6_PRUNE 6
+#define ICMPV6CTL_ND6_DELAY 8
+#define ICMPV6CTL_ND6_UMAXTRIES 9
+#define ICMPV6CTL_ND6_MMAXTRIES 10
+#define ICMPV6CTL_ND6_USELOOPBACK 11
+#define ICMPV6CTL_ND6_PROXYALL 12
+#define ICMPV6CTL_MAXID 13
+
+#define ICMPV6CTL_NAMES { \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "rediraccept", CTLTYPE_INT }, \
+ { "redirtimeout", CTLTYPE_INT }, \
+ { 0, 0 }, \
+ { "errratelimit", CTLTYPE_INT }, \
+ { "nd6_prune", CTLTYPE_INT }, \
+ { 0, 0 }, \
+ { "nd6_delay", CTLTYPE_INT }, \
+ { "nd6_umaxtries", CTLTYPE_INT }, \
+ { "nd6_mmaxtries", CTLTYPE_INT }, \
+ { "nd6_useloopback", CTLTYPE_INT }, \
+ { "nd6_proxyall", CTLTYPE_INT }, \
+}
+
+#define ICMPV6CTL_VARS { \
+ 0, \
+ 0, \
+ &icmp6_rediraccept, \
+ &icmp6_redirtimeout, \
+ 0, \
+ 0, \
+ &icmp6errratelim, \
+ &nd6_prune, \
+ 0, \
+ &nd6_delay, \
+ &nd6_umaxtries, \
+ &nd6_mmaxtries, \
+ &nd6_useloopback, \
+ &nd6_proxyall, \
+}
+
+#define RTF_PROBEMTU RTF_PROTO1
+
+#ifdef _KERNEL
+# ifdef __STDC__
+struct rtentry;
+struct rttimer;
+struct in6_multi;
+# endif
+void icmp6_init __P((void));
+void icmp6_paramerror __P((struct mbuf *, int));
+void icmp6_error __P((struct mbuf *, int, int, int));
+int icmp6_input __P((struct mbuf **, int *, int));
+void icmp6_fasttimo __P((void));
+void icmp6_reflect __P((struct mbuf *, size_t));
+void icmp6_prepare __P((struct mbuf *));
+void icmp6_redirect_input __P((struct mbuf *, int));
+void icmp6_redirect_output __P((struct mbuf *, struct rtentry *));
+
+/* XXX: is this the right place for these macros? */
+#define icmp6_ifstat_inc(ifp, tag) \
+do { \
+ if ((ifp) && (ifp)->if_index <= if_index \
+ && (ifp)->if_index < icmp6_ifstatmax \
+ && icmp6_ifstat && icmp6_ifstat[(ifp)->if_index]) { \
+ icmp6_ifstat[(ifp)->if_index]->tag++; \
+ } \
+} while (0)
+
+#define icmp6_ifoutstat_inc(ifp, type, code) \
+do { \
+ icmp6_ifstat_inc(ifp, ifs6_out_msg); \
+ if (type < ICMP6_INFOMSG_MASK) \
+ icmp6_ifstat_inc(ifp, ifs6_out_error); \
+ switch(type) { \
+ case ICMP6_DST_UNREACH: \
+ icmp6_ifstat_inc(ifp, ifs6_out_dstunreach); \
+ if (code == ICMP6_DST_UNREACH_ADMIN) \
+ icmp6_ifstat_inc(ifp, ifs6_out_adminprohib); \
+ break; \
+ case ICMP6_PACKET_TOO_BIG: \
+ icmp6_ifstat_inc(ifp, ifs6_out_pkttoobig); \
+ break; \
+ case ICMP6_TIME_EXCEEDED: \
+ icmp6_ifstat_inc(ifp, ifs6_out_timeexceed); \
+ break; \
+ case ICMP6_PARAM_PROB: \
+ icmp6_ifstat_inc(ifp, ifs6_out_paramprob); \
+ break; \
+ case ICMP6_ECHO_REQUEST: \
+ icmp6_ifstat_inc(ifp, ifs6_out_echo); \
+ break; \
+ case ICMP6_ECHO_REPLY: \
+ icmp6_ifstat_inc(ifp, ifs6_out_echoreply); \
+ break; \
+ case MLD6_LISTENER_QUERY: \
+ icmp6_ifstat_inc(ifp, ifs6_out_mldquery); \
+ break; \
+ case MLD6_LISTENER_REPORT: \
+ icmp6_ifstat_inc(ifp, ifs6_out_mldreport); \
+ break; \
+ case MLD6_LISTENER_DONE: \
+ icmp6_ifstat_inc(ifp, ifs6_out_mlddone); \
+ break; \
+ case ND_ROUTER_SOLICIT: \
+ icmp6_ifstat_inc(ifp, ifs6_out_routersolicit); \
+ break; \
+ case ND_ROUTER_ADVERT: \
+ icmp6_ifstat_inc(ifp, ifs6_out_routeradvert); \
+ break; \
+ case ND_NEIGHBOR_SOLICIT: \
+ icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); \
+ break; \
+ case ND_NEIGHBOR_ADVERT: \
+ icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert); \
+ break; \
+ case ND_REDIRECT: \
+ icmp6_ifstat_inc(ifp, ifs6_out_redirect); \
+ break; \
+ } \
+} while (0)
+
+extern int icmp6_rediraccept; /* accept/process redirects */
+extern int icmp6_redirtimeout; /* cache time for redirect routes */
+#endif /* _KERNEL */
+
+#endif /* not _NETINET6_ICMPV6_H_ */
+
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
new file mode 100644
index 0000000..291b672
--- /dev/null
+++ b/sys/netinet6/in6.c
@@ -0,0 +1,1879 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in.c 8.2 (Berkeley) 11/15/93
+ */
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sockio.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+/* #include "gif.h" */
+#if NGIF > 0
+#include <net/if_gif.h>
+#endif
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+
+#include <netinet6/nd6.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/mld6_var.h>
+#include <netinet6/in6_ifattach.h>
+
+#include <net/net_osdep.h>
+
+MALLOC_DEFINE(M_IPMADDR, "in6_multi", "internet multicast address");
+
+/*
+ * Definitions of some costant IP6 addresses.
+ */
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+const struct in6_addr in6addr_nodelocal_allnodes =
+ IN6ADDR_NODELOCAL_ALLNODES_INIT;
+const struct in6_addr in6addr_linklocal_allnodes =
+ IN6ADDR_LINKLOCAL_ALLNODES_INIT;
+const struct in6_addr in6addr_linklocal_allrouters =
+ IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
+
+const struct in6_addr in6mask0 = IN6MASK0;
+const struct in6_addr in6mask32 = IN6MASK32;
+const struct in6_addr in6mask64 = IN6MASK64;
+const struct in6_addr in6mask96 = IN6MASK96;
+const struct in6_addr in6mask128 = IN6MASK128;
+
+static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t,
+ struct ifnet *, struct proc *));
+struct in6_multihead in6_multihead; /* XXX BSS initialization */
+
+/*
+ * Determine whether an IP6 address is in a reserved set of addresses
+ * that may not be forwarded, or whether datagrams to that destination
+ * may be forwarded.
+ */
+int
+in6_canforward(src, dst)
+ struct in6_addr *src, *dst;
+{
+ if (IN6_IS_ADDR_LINKLOCAL(src) ||
+ IN6_IS_ADDR_LINKLOCAL(dst) ||
+ IN6_IS_ADDR_MULTICAST(dst))
+ return(0);
+ return(1);
+}
+
+/*
+ * Check if the loopback entry will be automatically generated.
+ * if 0 returned, will not be automatically generated.
+ * if 1 returned, will be automatically generated.
+ */
+static int
+in6_is_ifloop_auto(struct ifaddr *ifa)
+{
+#define SIN6(s) ((struct sockaddr_in6 *)s)
+ /*
+ * If RTF_CLONING is unset, or (IFF_LOOPBACK | IFF_POINTOPOINT),
+ * or netmask is all0 or all1, then cloning will not happen,
+ * then we can't rely on its loopback entry generation.
+ */
+ if ((ifa->ifa_flags & RTF_CLONING) == 0 ||
+ (ifa->ifa_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) ||
+ (SIN6(ifa->ifa_netmask)->sin6_len == sizeof(struct sockaddr_in6)
+ &&
+ IN6_ARE_ADDR_EQUAL(&SIN6(ifa->ifa_netmask)->sin6_addr,
+ &in6mask128)) ||
+ ((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_len == 0)
+ return 0;
+ else
+ return 1;
+#undef SIN6
+}
+
+/*
+ * Subroutine for in6_ifaddloop() and in6_ifremloop().
+ * This routine does actual work.
+ */
+static void
+in6_ifloop_request(int cmd, struct ifaddr *ifa)
+{
+ struct sockaddr_in6 lo_sa;
+ struct sockaddr_in6 all1_sa;
+ struct rtentry *nrt = NULL;
+
+ bzero(&lo_sa, sizeof(lo_sa));
+ bzero(&all1_sa, sizeof(all1_sa));
+ lo_sa.sin6_family = AF_INET6;
+ lo_sa.sin6_len = sizeof(struct sockaddr_in6);
+ all1_sa = lo_sa;
+ lo_sa.sin6_addr = in6addr_loopback;
+ all1_sa.sin6_addr = in6mask128;
+
+ /* So we add or remove static loopback entry, here. */
+ rtrequest(cmd, ifa->ifa_addr,
+ (struct sockaddr *)&lo_sa,
+ (struct sockaddr *)&all1_sa,
+ RTF_UP|RTF_HOST, &nrt);
+
+ /*
+ * Make sure rt_ifa be equal to IFA, the second argument of the
+ * function.
+ * We need this because when we refer rt_ifa->ia6_flags in ip6_input,
+ * we assume that the rt_ifa points to the address instead of the
+ * loopback address.
+ */
+ if (cmd == RTM_ADD && nrt && ifa != nrt->rt_ifa) {
+ nrt->rt_ifa->ifa_refcnt--;
+ ifa->ifa_refcnt++;
+ nrt->rt_ifa = ifa;
+ }
+ if (nrt)
+ nrt->rt_refcnt--;
+}
+
+/*
+ * Add ownaddr as loopback rtentry, if necessary(ex. on p2p link).
+ * Because, KAME needs loopback rtentry for ownaddr check in
+ * ip6_input().
+ */
+static void
+in6_ifaddloop(struct ifaddr *ifa)
+{
+ if (!in6_is_ifloop_auto(ifa)) {
+ struct rtentry *rt;
+
+ /* If there is no loopback entry, allocate one. */
+ rt = rtalloc1(ifa->ifa_addr, 0, 0);
+ if (rt == 0 || (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0)
+ in6_ifloop_request(RTM_ADD, ifa);
+ if (rt)
+ rt->rt_refcnt--;
+ }
+}
+
+/*
+ * Remove loopback rtentry of ownaddr generated by in6_ifaddloop(),
+ * if it exists.
+ */
+static void
+in6_ifremloop(struct ifaddr *ifa)
+{
+ if (!in6_is_ifloop_auto(ifa)) {
+ struct in6_ifaddr *ia;
+ int ia_count = 0;
+
+ /* If only one ifa for the loopback entry, delete it. */
+ for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
+ if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa),
+ &ia->ia_addr.sin6_addr)) {
+ ia_count++;
+ if (ia_count > 1)
+ break;
+ }
+ }
+ if (ia_count == 1)
+ in6_ifloop_request(RTM_DELETE, ifa);
+ }
+}
+
+/*
+ * Subroutine for in6_ifaddproxy() and in6_ifremproxy().
+ * This routine does actual work.
+ * call in6_addmulti() when cmd == 1.
+ * call in6_delmulti() when cmd == 2.
+ */
+static int
+in6_ifproxy_request(int cmd, struct in6_ifaddr *ia)
+{
+ int error = 0;
+
+ /*
+ * If we have an IPv6 dstaddr on adding p2p interface,
+ * join dstaddr's solicited multicast on necessary interface.
+ */
+ if ((ia->ia_ifp->if_flags & IFF_POINTOPOINT) &&
+ ia->ia_dstaddr.sin6_family == AF_INET6 &&
+ !IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) {
+ struct in6_ifaddr *ia_lan;
+
+ /*
+ * TODO: Join only on some specified interfaces by some
+ * configuration.
+ * Unsolicited Neighbor Advertisements will be also necessary.
+ *
+ * Now, join on interfaces which meets following.
+ * -IFF_BROADCAST and IFF_MULTICAST
+ * (NBMA is out of scope)
+ * -the prefix value is same as p2p dstaddr
+ */
+ for (ia_lan = in6_ifaddr; ia_lan; ia_lan = ia_lan->ia_next) {
+ struct in6_addr llsol;
+
+ if ((ia_lan->ia_ifp->if_flags &
+ (IFF_BROADCAST|IFF_MULTICAST)) !=
+ (IFF_BROADCAST|IFF_MULTICAST))
+ continue;
+ if (!IN6_ARE_MASKED_ADDR_EQUAL(IA6_IN6(ia),
+ IA6_IN6(ia_lan),
+ IA6_MASKIN6(ia_lan)))
+ continue;
+ if (ia_lan->ia_ifp == ia->ia_ifp)
+ continue;
+
+ /* init llsol */
+ bzero(&llsol, sizeof(struct in6_addr));
+ llsol.s6_addr16[0] = htons(0xff02);
+ llsol.s6_addr16[1] = htons(ia_lan->ia_ifp->if_index);
+ llsol.s6_addr32[1] = 0;
+ llsol.s6_addr32[2] = htonl(1);
+ llsol.s6_addr32[3] =
+ ia->ia_dstaddr.sin6_addr.s6_addr32[3];
+ llsol.s6_addr8[12] = 0xff;
+
+ if (cmd == 1)
+ (void)in6_addmulti(&llsol,
+ ia_lan->ia_ifp,
+ &error);
+ else if (cmd == 2) {
+ struct in6_multi *in6m;
+
+ IN6_LOOKUP_MULTI(llsol,
+ ia_lan->ia_ifp,
+ in6m);
+ if (in6m)
+ in6_delmulti(in6m);
+ }
+ }
+ }
+ return error;
+}
+
+static int
+in6_ifaddproxy(struct in6_ifaddr *ia)
+{
+ return(in6_ifproxy_request(1, ia));
+}
+
+static void
+in6_ifremproxy(struct in6_ifaddr *ia)
+{
+ in6_ifproxy_request(2, ia);
+}
+
+int
+in6_ifindex2scopeid(idx)
+ int idx;
+{
+ struct ifnet *ifp;
+ struct ifaddr *ifa;
+ struct sockaddr_in6 *sin6;
+
+ if (idx < 0 || if_index < idx)
+ return -1;
+ ifp = ifindex2ifnet[idx];
+
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+ if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))
+ return sin6->sin6_scope_id & 0xffff;
+ }
+
+ return -1;
+}
+
+int
+in6_mask2len(mask)
+ struct in6_addr *mask;
+{
+ int x, y;
+
+ for (x = 0; x < sizeof(*mask); x++) {
+ if (mask->s6_addr8[x] != 0xff)
+ break;
+ }
+ y = 0;
+ if (x < sizeof(*mask)) {
+ for (y = 0; y < 8; y++) {
+ if ((mask->s6_addr8[x] & (0x80 >> y)) == 0)
+ break;
+ }
+ }
+ return x * 8 + y;
+}
+
+void
+in6_len2mask(mask, len)
+ struct in6_addr *mask;
+ int len;
+{
+ int i;
+
+ bzero(mask, sizeof(*mask));
+ for (i = 0; i < len / 8; i++)
+ mask->s6_addr8[i] = 0xff;
+ if (len % 8)
+ mask->s6_addr8[i] = (0xff00 >> (len % 8)) & 0xff;
+}
+
+int in6_interfaces; /* number of external internet interfaces */
+
+#define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa))
+#define ia62ifa(ia6) ((struct ifaddr *)(ia6))
+
+int
+in6_control(so, cmd, data, ifp, p)
+ struct socket *so;
+ u_long cmd;
+ caddr_t data;
+ struct ifnet *ifp;
+ struct proc *p;
+{
+ struct in6_ifreq *ifr = (struct in6_ifreq *)data;
+ struct in6_ifaddr *ia, *oia;
+ struct in6_aliasreq *ifra = (struct in6_aliasreq *)data;
+ struct sockaddr_in6 oldaddr, net;
+ int error = 0, hostIsNew, prefixIsNew;
+ int privileged;
+
+ privileged = 0;
+ if (p && !suser(p))
+ privileged++;
+
+ /*
+ * xxx should prevent processes for link-local addresses?
+ */
+#if NGIF > 0
+ if (ifp && ifp->if_type == IFT_GIF) {
+ switch (cmd) {
+ case SIOCSIFPHYADDR_IN6:
+ if (!privileged)
+ return(EPERM);
+ /*fall through*/
+ case SIOCGIFPSRCADDR_IN6:
+ case SIOCGIFPDSTADDR_IN6:
+ return gif_ioctl(ifp, cmd, data);
+ }
+ }
+#endif
+
+ if (ifp == 0)
+ return(EOPNOTSUPP);
+
+ switch (cmd) {
+ case SIOCSNDFLUSH_IN6:
+ case SIOCSPFXFLUSH_IN6:
+ case SIOCSRTRFLUSH_IN6:
+ if (!privileged)
+ return(EPERM);
+ /*fall through*/
+ case SIOCGIFINFO_IN6:
+ case SIOCGDRLST_IN6:
+ case SIOCGPRLST_IN6:
+ case SIOCGNBRINFO_IN6:
+ return(nd6_ioctl(cmd, data, ifp));
+ }
+
+ switch (cmd) {
+ case SIOCSIFPREFIX_IN6:
+ case SIOCDIFPREFIX_IN6:
+ case SIOCAIFPREFIX_IN6:
+ case SIOCCIFPREFIX_IN6:
+ case SIOCSGIFPREFIX_IN6:
+ if (!privileged)
+ return(EPERM);
+ /*fall through*/
+ case SIOCGIFPREFIX_IN6:
+ return(in6_prefix_ioctl(so, cmd, data, ifp));
+ }
+
+ switch (cmd) {
+ case SIOCALIFADDR:
+ case SIOCDLIFADDR:
+ if (!privileged)
+ return(EPERM);
+ /*fall through*/
+ case SIOCGLIFADDR:
+ return in6_lifaddr_ioctl(so, cmd, data, ifp, p);
+ }
+
+ /*
+ * Find address for this interface, if it exists.
+ */
+ {
+
+ struct sockaddr_in6 *sa6 =
+ (struct sockaddr_in6 *)&ifra->ifra_addr;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
+ if (sa6->sin6_addr.s6_addr16[1] == 0) {
+ /* interface ID is not embedded by the user */
+ sa6->sin6_addr.s6_addr16[1] =
+ htons(ifp->if_index);
+ } else
+ if (sa6->sin6_addr.s6_addr16[1] !=
+ htons(ifp->if_index))
+ return(EINVAL); /* ifid is contradict */
+ if (sa6->sin6_scope_id) {
+ if (sa6->sin6_scope_id !=
+ (u_int32_t)ifp->if_index)
+ return(EINVAL);
+ sa6->sin6_scope_id = 0; /* XXX: good way? */
+ }
+ }
+ }
+ ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr);
+
+ switch (cmd) {
+
+ case SIOCDIFADDR_IN6:
+ if (ia == 0)
+ return(EADDRNOTAVAIL);
+ /* FALLTHROUGH */
+ case SIOCAIFADDR_IN6:
+ case SIOCSIFADDR_IN6:
+ case SIOCSIFNETMASK_IN6:
+ case SIOCSIFDSTADDR_IN6:
+ if (!privileged)
+ return(EPERM);
+ if (ia == 0) {
+ ia = (struct in6_ifaddr *)
+ malloc(sizeof(*ia), M_IFADDR, M_WAITOK);
+ if (ia == NULL)
+ return (ENOBUFS);
+ bzero((caddr_t)ia, sizeof(*ia));
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ ia->ia_ifa.ifa_dstaddr
+ = (struct sockaddr *)&ia->ia_dstaddr;
+ ia->ia_ifa.ifa_netmask
+ = (struct sockaddr *)&ia->ia_prefixmask;
+
+ ia->ia_ifp = ifp;
+ if ((oia = in6_ifaddr) != NULL) {
+ for ( ; oia->ia_next; oia = oia->ia_next)
+ continue;
+ oia->ia_next = ia;
+ } else
+ in6_ifaddr = ia;
+ TAILQ_INSERT_TAIL(&ifp->if_addrlist,
+ (struct ifaddr *)ia, ifa_list);
+ if ((ifp->if_flags & IFF_LOOPBACK) == 0)
+ in6_interfaces++; /*XXX*/
+ }
+
+ if (cmd == SIOCAIFADDR_IN6) {
+ /* sanity for overflow - beware unsigned */
+ struct in6_addrlifetime *lt;
+ lt = &ifra->ifra_lifetime;
+ if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME
+ && lt->ia6t_vltime + time_second < time_second) {
+ return EINVAL;
+ }
+ if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME
+ && lt->ia6t_pltime + time_second < time_second) {
+ return EINVAL;
+ }
+ }
+ break;
+
+ case SIOCGIFADDR_IN6:
+ /* This interface is basically deprecated. use SIOCGIFCONF. */
+ /* fall through */
+ case SIOCGIFAFLAG_IN6:
+ case SIOCGIFNETMASK_IN6:
+ case SIOCGIFDSTADDR_IN6:
+ case SIOCGIFALIFETIME_IN6:
+ /* must think again about its semantics */
+ if (ia == 0)
+ return(EADDRNOTAVAIL);
+ break;
+ case SIOCSIFALIFETIME_IN6:
+ {
+ struct in6_addrlifetime *lt;
+
+ if (!privileged)
+ return(EPERM);
+ if (ia == 0)
+ return(EADDRNOTAVAIL);
+ /* sanity for overflow - beware unsigned */
+ lt = &ifr->ifr_ifru.ifru_lifetime;
+ if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME
+ && lt->ia6t_vltime + time_second < time_second) {
+ return EINVAL;
+ }
+ if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME
+ && lt->ia6t_pltime + time_second < time_second) {
+ return EINVAL;
+ }
+ break;
+ }
+ }
+
+ switch (cmd) {
+
+ case SIOCGIFADDR_IN6:
+ ifr->ifr_addr = ia->ia_addr;
+ break;
+
+ case SIOCGIFDSTADDR_IN6:
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ return(EINVAL);
+ ifr->ifr_dstaddr = ia->ia_dstaddr;
+ break;
+
+ case SIOCGIFNETMASK_IN6:
+ ifr->ifr_addr = ia->ia_prefixmask;
+ break;
+
+ case SIOCGIFAFLAG_IN6:
+ ifr->ifr_ifru.ifru_flags6 = ia->ia6_flags;
+ break;
+
+ case SIOCGIFSTAT_IN6:
+ if (ifp == NULL)
+ return EINVAL;
+ if (in6_ifstat == NULL || ifp->if_index >= in6_ifstatmax
+ || in6_ifstat[ifp->if_index] == NULL) {
+ /* return EAFNOSUPPORT? */
+ bzero(&ifr->ifr_ifru.ifru_stat,
+ sizeof(ifr->ifr_ifru.ifru_stat));
+ } else
+ ifr->ifr_ifru.ifru_stat = *in6_ifstat[ifp->if_index];
+ break;
+
+ case SIOCGIFSTAT_ICMP6:
+ if (ifp == NULL)
+ return EINVAL;
+ if (icmp6_ifstat == NULL || ifp->if_index >= icmp6_ifstatmax ||
+ icmp6_ifstat[ifp->if_index] == NULL) {
+ /* return EAFNOSUPPORT? */
+ bzero(&ifr->ifr_ifru.ifru_stat,
+ sizeof(ifr->ifr_ifru.ifru_icmp6stat));
+ } else
+ ifr->ifr_ifru.ifru_icmp6stat =
+ *icmp6_ifstat[ifp->if_index];
+ break;
+
+ case SIOCSIFDSTADDR_IN6:
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ return(EINVAL);
+ oldaddr = ia->ia_dstaddr;
+ ia->ia_dstaddr = ifr->ifr_dstaddr;
+
+ /* link-local index check */
+ if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) {
+ if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) {
+ /* interface ID is not embedded by the user */
+ ia->ia_dstaddr.sin6_addr.s6_addr16[1]
+ = htons(ifp->if_index);
+ } else
+ if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] !=
+ htons(ifp->if_index)) {
+ ia->ia_dstaddr = oldaddr;
+ return(EINVAL); /* ifid is contradict */
+ }
+ }
+
+ if (ifp->if_ioctl && (error = (ifp->if_ioctl)
+ (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) {
+ ia->ia_dstaddr = oldaddr;
+ return(error);
+ }
+ if (ia->ia_flags & IFA_ROUTE) {
+ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr;
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+ ia->ia_ifa.ifa_dstaddr =
+ (struct sockaddr *)&ia->ia_dstaddr;
+ rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
+ }
+ break;
+
+ case SIOCGIFALIFETIME_IN6:
+ ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime;
+ break;
+
+ case SIOCSIFALIFETIME_IN6:
+ ia->ia6_lifetime = ifr->ifr_ifru.ifru_lifetime;
+ /* for sanity */
+ if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
+ ia->ia6_lifetime.ia6t_expire =
+ time_second + ia->ia6_lifetime.ia6t_vltime;
+ } else
+ ia->ia6_lifetime.ia6t_expire = 0;
+ if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
+ ia->ia6_lifetime.ia6t_preferred =
+ time_second + ia->ia6_lifetime.ia6t_pltime;
+ } else
+ ia->ia6_lifetime.ia6t_preferred = 0;
+ break;
+
+ case SIOCSIFADDR_IN6:
+ return(in6_ifinit(ifp, ia, &ifr->ifr_addr, 1));
+
+ case SIOCSIFNETMASK_IN6:
+ ia->ia_prefixmask = ifr->ifr_addr;
+ bzero(&net, sizeof(net));
+ net.sin6_len = sizeof(struct sockaddr_in6);
+ net.sin6_family = AF_INET6;
+ net.sin6_port = htons(0);
+ net.sin6_flowinfo = htonl(0);
+ net.sin6_addr.s6_addr32[0]
+ = ia->ia_addr.sin6_addr.s6_addr32[0] &
+ ia->ia_prefixmask.sin6_addr.s6_addr32[0];
+ net.sin6_addr.s6_addr32[1]
+ = ia->ia_addr.sin6_addr.s6_addr32[1] &
+ ia->ia_prefixmask.sin6_addr.s6_addr32[1];
+ net.sin6_addr.s6_addr32[2]
+ = ia->ia_addr.sin6_addr.s6_addr32[2] &
+ ia->ia_prefixmask.sin6_addr.s6_addr32[2];
+ net.sin6_addr.s6_addr32[3]
+ = ia->ia_addr.sin6_addr.s6_addr32[3] &
+ ia->ia_prefixmask.sin6_addr.s6_addr32[3];
+ ia->ia_net = net;
+ break;
+
+ case SIOCAIFADDR_IN6:
+ prefixIsNew = 0;
+ hostIsNew = 1;
+
+ if (ifra->ifra_addr.sin6_len == 0) {
+ ifra->ifra_addr = ia->ia_addr;
+ hostIsNew = 0;
+ } else if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr,
+ &ia->ia_addr.sin6_addr))
+ hostIsNew = 0;
+
+ if (ifra->ifra_prefixmask.sin6_len) {
+ in6_ifscrub(ifp, ia);
+ ia->ia_prefixmask = ifra->ifra_prefixmask;
+ prefixIsNew = 1;
+ }
+ if ((ifp->if_flags & IFF_POINTOPOINT) &&
+ (ifra->ifra_dstaddr.sin6_family == AF_INET6)) {
+ in6_ifscrub(ifp, ia);
+ ia->ia_dstaddr = ifra->ifra_dstaddr;
+ /* link-local index check: should be a separate function? */
+ if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) {
+ if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) {
+ /*
+ * interface ID is not embedded by
+ * the user
+ */
+ ia->ia_dstaddr.sin6_addr.s6_addr16[1]
+ = htons(ifp->if_index);
+ } else
+ if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] !=
+ htons(ifp->if_index)) {
+ ia->ia_dstaddr = oldaddr;
+ return(EINVAL); /* ifid is contradict */
+ }
+ }
+ prefixIsNew = 1; /* We lie; but effect's the same */
+ }
+ if (ifra->ifra_addr.sin6_family == AF_INET6 &&
+ (hostIsNew || prefixIsNew))
+ error = in6_ifinit(ifp, ia, &ifra->ifra_addr, 0);
+ if (ifra->ifra_addr.sin6_family == AF_INET6
+ && hostIsNew && (ifp->if_flags & IFF_MULTICAST)) {
+ int error_local = 0;
+
+ /*
+ * join solicited multicast addr for new host id
+ */
+ struct in6_addr llsol;
+ bzero(&llsol, sizeof(struct in6_addr));
+ llsol.s6_addr16[0] = htons(0xff02);
+ llsol.s6_addr16[1] = htons(ifp->if_index);
+ llsol.s6_addr32[1] = 0;
+ llsol.s6_addr32[2] = htonl(1);
+ llsol.s6_addr32[3] =
+ ifra->ifra_addr.sin6_addr.s6_addr32[3];
+ llsol.s6_addr8[12] = 0xff;
+ (void)in6_addmulti(&llsol, ifp, &error_local);
+ if (error == 0)
+ error = error_local;
+ }
+ /* Join dstaddr's solicited multicast if necessary. */
+ if (nd6_proxyall && hostIsNew) {
+ int error_local;
+
+ error_local = in6_ifaddproxy(ia);
+ if (error == 0)
+ error = error_local;
+ }
+
+ ia->ia6_flags = ifra->ifra_flags;
+ ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/
+
+ ia->ia6_lifetime = ifra->ifra_lifetime;
+ /* for sanity */
+ if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
+ ia->ia6_lifetime.ia6t_expire =
+ time_second + ia->ia6_lifetime.ia6t_vltime;
+ } else
+ ia->ia6_lifetime.ia6t_expire = 0;
+ if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
+ ia->ia6_lifetime.ia6t_preferred =
+ time_second + ia->ia6_lifetime.ia6t_pltime;
+ } else
+ ia->ia6_lifetime.ia6t_preferred = 0;
+
+ /*
+ * Perform DAD, if needed.
+ * XXX It may be of use, if we can administratively
+ * disable DAD.
+ */
+ switch (ifp->if_type) {
+ case IFT_ARCNET:
+ case IFT_ETHER:
+ case IFT_FDDI:
+ ia->ia6_flags |= IN6_IFF_TENTATIVE;
+ nd6_dad_start((struct ifaddr *)ia, NULL);
+ break;
+#ifdef IFT_DUMMY
+ case IFT_DUMMY:
+#endif
+ case IFT_FAITH:
+ case IFT_GIF:
+ case IFT_LOOP:
+ default:
+ break;
+ }
+
+ if (hostIsNew) {
+ int iilen;
+ int error_local = 0;
+
+ iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) -
+ in6_mask2len(&ia->ia_prefixmask.sin6_addr);
+ error_local = in6_prefix_add_ifid(iilen, ia);
+ if (error == 0)
+ error = error_local;
+ }
+
+ return(error);
+
+ case SIOCDIFADDR_IN6:
+ in6_ifscrub(ifp, ia);
+
+ if (ifp->if_flags & IFF_MULTICAST) {
+ /*
+ * delete solicited multicast addr for deleting host id
+ */
+ struct in6_multi *in6m;
+ struct in6_addr llsol;
+ bzero(&llsol, sizeof(struct in6_addr));
+ llsol.s6_addr16[0] = htons(0xff02);
+ llsol.s6_addr16[1] = htons(ifp->if_index);
+ llsol.s6_addr32[1] = 0;
+ llsol.s6_addr32[2] = htonl(1);
+ llsol.s6_addr32[3] =
+ ia->ia_addr.sin6_addr.s6_addr32[3];
+ llsol.s6_addr8[12] = 0xff;
+
+ IN6_LOOKUP_MULTI(llsol, ifp, in6m);
+ if (in6m)
+ in6_delmulti(in6m);
+ }
+ /* Leave dstaddr's solicited multicast if necessary. */
+ if (nd6_proxyall)
+ in6_ifremproxy(ia);
+
+ TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+ oia = ia;
+ if (oia == (ia = in6_ifaddr))
+ in6_ifaddr = ia->ia_next;
+ else {
+ while (ia->ia_next && (ia->ia_next != oia))
+ ia = ia->ia_next;
+ if (ia->ia_next)
+ ia->ia_next = oia->ia_next;
+ else
+ printf("Didn't unlink in6_ifaddr from list\n");
+ }
+ {
+ int iilen;
+
+ iilen = (sizeof(oia->ia_prefixmask.sin6_addr) << 3) -
+ in6_mask2len(&oia->ia_prefixmask.sin6_addr);
+ in6_prefix_remove_ifid(iilen, oia);
+ }
+ IFAFREE((&oia->ia_ifa));
+ break;
+
+ default:
+ if (ifp == 0 || ifp->if_ioctl == 0)
+ return(EOPNOTSUPP);
+ return((*ifp->if_ioctl)(ifp, cmd, data));
+ }
+ return(0);
+}
+
+/*
+ * SIOC[GAD]LIFADDR.
+ * SIOCGLIFADDR: get first address. (???)
+ * SIOCGLIFADDR with IFLR_PREFIX:
+ * get first address that matches the specified prefix.
+ * SIOCALIFADDR: add the specified address.
+ * SIOCALIFADDR with IFLR_PREFIX:
+ * add the specified prefix, filling hostid part from
+ * the first link-local address. prefixlen must be <= 64.
+ * SIOCDLIFADDR: delete the specified address.
+ * SIOCDLIFADDR with IFLR_PREFIX:
+ * delete the first address that matches the specified prefix.
+ * return values:
+ * EINVAL on invalid parameters
+ * EADDRNOTAVAIL on prefix match failed/specified address not found
+ * other values may be returned from in6_ioctl()
+ *
+ * NOTE: SIOCALIFADDR(with IFLR_PREFIX set) allows prefixlen less than 64.
+ * this is to accomodate address naming scheme other than RFC2374,
+ * in the future.
+ * RFC2373 defines interface id to be 64bit, but it allows non-RFC2374
+ * address encoding scheme. (see figure on page 8)
+ */
+static int
+in6_lifaddr_ioctl(so, cmd, data, ifp, p)
+ struct socket *so;
+ u_long cmd;
+ caddr_t data;
+ struct ifnet *ifp;
+ struct proc *p;
+{
+ struct if_laddrreq *iflr = (struct if_laddrreq *)data;
+ struct ifaddr *ifa;
+
+ /* sanity checks */
+ if (!data || !ifp) {
+ panic("invalid argument to in6_lifaddr_ioctl");
+ /*NOTRECHED*/
+ }
+
+ switch (cmd) {
+ case SIOCGLIFADDR:
+ /* address must be specified on GET with IFLR_PREFIX */
+ if ((iflr->flags & IFLR_PREFIX) == 0)
+ break;
+ /*FALLTHROUGH*/
+ case SIOCALIFADDR:
+ case SIOCDLIFADDR:
+ /* address must be specified on ADD and DELETE */
+ if (iflr->addr.__ss_family != AF_INET6)
+ return EINVAL;
+ if (iflr->addr.__ss_len != sizeof(struct sockaddr_in6))
+ return EINVAL;
+ /* XXX need improvement */
+ if (iflr->dstaddr.__ss_family
+ && iflr->dstaddr.__ss_family != AF_INET6)
+ return EINVAL;
+ if (iflr->dstaddr.__ss_family
+ && iflr->dstaddr.__ss_len != sizeof(struct sockaddr_in6))
+ return EINVAL;
+ break;
+ default: /*shouldn't happen*/
+ return EOPNOTSUPP;
+ }
+ if (sizeof(struct in6_addr) * 8 < iflr->prefixlen)
+ return EINVAL;
+
+ switch (cmd) {
+ case SIOCALIFADDR:
+ {
+ struct in6_aliasreq ifra;
+ struct in6_addr *hostid = NULL;
+ int prefixlen;
+
+ if ((iflr->flags & IFLR_PREFIX) != 0) {
+ struct sockaddr_in6 *sin6;
+
+ /*
+ * hostid is to fill in the hostid part of the
+ * address. hostid points to the first link-local
+ * address attached to the interface.
+ */
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
+ if (!ifa)
+ return EADDRNOTAVAIL;
+ hostid = IFA_IN6(ifa);
+
+ /* prefixlen must be <= 64. */
+ if (64 < iflr->prefixlen)
+ return EINVAL;
+ prefixlen = iflr->prefixlen;
+
+ /* hostid part must be zero. */
+ sin6 = (struct sockaddr_in6 *)&iflr->addr;
+ if (sin6->sin6_addr.s6_addr32[2] != 0
+ || sin6->sin6_addr.s6_addr32[3] != 0) {
+ return EINVAL;
+ }
+ } else
+ prefixlen = iflr->prefixlen;
+
+ /* copy args to in6_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */
+ bzero(&ifra, sizeof(ifra));
+ bcopy(iflr->iflr_name, ifra.ifra_name,
+ sizeof(ifra.ifra_name));
+
+ bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.__ss_len);
+ if (hostid) {
+ /* fill in hostid part */
+ ifra.ifra_addr.sin6_addr.s6_addr32[2] =
+ hostid->s6_addr32[2];
+ ifra.ifra_addr.sin6_addr.s6_addr32[3] =
+ hostid->s6_addr32[3];
+ }
+
+ if (iflr->dstaddr.__ss_family) { /*XXX*/
+ bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr,
+ iflr->dstaddr.__ss_len);
+ if (hostid) {
+ ifra.ifra_dstaddr.sin6_addr.s6_addr32[2] =
+ hostid->s6_addr32[2];
+ ifra.ifra_dstaddr.sin6_addr.s6_addr32[3] =
+ hostid->s6_addr32[3];
+ }
+ }
+
+ ifra.ifra_prefixmask.sin6_family = AF_INET6;
+ ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ in6_len2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen);
+
+ ifra.ifra_flags = iflr->flags & ~IFLR_PREFIX;
+ return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp, p);
+ }
+ case SIOCGLIFADDR:
+ case SIOCDLIFADDR:
+ {
+ struct in6_ifaddr *ia;
+ struct in6_addr mask, candidate, match;
+ struct sockaddr_in6 *sin6;
+ int cmp;
+
+ bzero(&mask, sizeof(mask));
+ if (iflr->flags & IFLR_PREFIX) {
+ /* lookup a prefix rather than address. */
+ in6_len2mask(&mask, iflr->prefixlen);
+
+ sin6 = (struct sockaddr_in6 *)&iflr->addr;
+ bcopy(&sin6->sin6_addr, &match, sizeof(match));
+ match.s6_addr32[0] &= mask.s6_addr32[0];
+ match.s6_addr32[1] &= mask.s6_addr32[1];
+ match.s6_addr32[2] &= mask.s6_addr32[2];
+ match.s6_addr32[3] &= mask.s6_addr32[3];
+
+ /* if you set extra bits, that's wrong */
+ if (bcmp(&match, &sin6->sin6_addr, sizeof(match)))
+ return EINVAL;
+
+ cmp = 1;
+ } else {
+ if (cmd == SIOCGLIFADDR) {
+ /* on getting an address, take the 1st match */
+ cmp = 0; /*XXX*/
+ } else {
+ /* on deleting an address, do exact match */
+ in6_len2mask(&mask, 128);
+ sin6 = (struct sockaddr_in6 *)&iflr->addr;
+ bcopy(&sin6->sin6_addr, &match, sizeof(match));
+
+ cmp = 1;
+ }
+ }
+
+ for (ifa = ifp->if_addrlist.tqh_first;
+ ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (!cmp)
+ break;
+ bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate));
+ candidate.s6_addr32[0] &= mask.s6_addr32[0];
+ candidate.s6_addr32[1] &= mask.s6_addr32[1];
+ candidate.s6_addr32[2] &= mask.s6_addr32[2];
+ candidate.s6_addr32[3] &= mask.s6_addr32[3];
+ if (IN6_ARE_ADDR_EQUAL(&candidate, &match))
+ break;
+ }
+ if (!ifa)
+ return EADDRNOTAVAIL;
+ ia = ifa2ia6(ifa);
+
+ if (cmd == SIOCGLIFADDR) {
+ /* fill in the if_laddrreq structure */
+ bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len);
+
+ if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
+ bcopy(&ia->ia_dstaddr, &iflr->dstaddr,
+ ia->ia_dstaddr.sin6_len);
+ } else
+ bzero(&iflr->dstaddr, sizeof(iflr->dstaddr));
+
+ iflr->prefixlen =
+ in6_mask2len(&ia->ia_prefixmask.sin6_addr);
+
+ iflr->flags = ia->ia6_flags; /*XXX*/
+
+ return 0;
+ } else {
+ struct in6_aliasreq ifra;
+
+ /* fill in6_aliasreq and do ioctl(SIOCDIFADDR_IN6) */
+ bzero(&ifra, sizeof(ifra));
+ bcopy(iflr->iflr_name, ifra.ifra_name,
+ sizeof(ifra.ifra_name));
+
+ bcopy(&ia->ia_addr, &ifra.ifra_addr,
+ ia->ia_addr.sin6_len);
+ if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
+ bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr,
+ ia->ia_dstaddr.sin6_len);
+ }
+ bcopy(&ia->ia_prefixmask, &ifra.ifra_dstaddr,
+ ia->ia_prefixmask.sin6_len);
+
+ ifra.ifra_flags = ia->ia6_flags;
+ return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra,
+ ifp, p);
+ }
+ }
+ }
+
+ return EOPNOTSUPP; /*just for safety*/
+}
+
+/*
+ * Delete any existing route for an interface.
+ */
+void
+in6_ifscrub(ifp, ia)
+ register struct ifnet *ifp;
+ register struct in6_ifaddr *ia;
+{
+ if ((ia->ia_flags & IFA_ROUTE) == 0)
+ return;
+ if (ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+ else
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
+ ia->ia_flags &= ~IFA_ROUTE;
+
+ /* Remove ownaddr's loopback rtentry, if it exists. */
+ in6_ifremloop(&(ia->ia_ifa));
+}
+
+/*
+ * Initialize an interface's intetnet6 address
+ * and routing table entry.
+ */
+int
+in6_ifinit(ifp, ia, sin6, scrub)
+ struct ifnet *ifp;
+ struct in6_ifaddr *ia;
+ struct sockaddr_in6 *sin6;
+ int scrub;
+{
+ struct sockaddr_in6 oldaddr;
+ int error, flags = RTF_UP;
+ int s = splimp();
+
+ oldaddr = ia->ia_addr;
+ ia->ia_addr = *sin6;
+ /*
+ * Give the interface a chance to initialize
+ * if this is its first address,
+ * and to validate the address if necessary.
+ */
+ if (ifp->if_ioctl &&
+ (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) {
+ splx(s);
+ ia->ia_addr = oldaddr;
+ return(error);
+ }
+
+ switch (ifp->if_type) {
+ case IFT_ARCNET:
+ case IFT_ETHER:
+ case IFT_FDDI:
+ ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
+ ia->ia_ifa.ifa_flags |= RTF_CLONING;
+ break;
+ case IFT_PPP:
+ ia->ia_ifa.ifa_rtrequest = nd6_p2p_rtrequest;
+ ia->ia_ifa.ifa_flags |= RTF_CLONING;
+ break;
+ }
+
+ splx(s);
+ if (scrub) {
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
+ in6_ifscrub(ifp, ia);
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ }
+ /* xxx
+ * in_socktrim
+ */
+ /*
+ * Add route for the network.
+ */
+ ia->ia_ifa.ifa_metric = ifp->if_metric;
+ if (ifp->if_flags & IFF_LOOPBACK) {
+ ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr;
+ flags |= RTF_HOST;
+ } else if (ifp->if_flags & IFF_POINTOPOINT) {
+ if (ia->ia_dstaddr.sin6_family != AF_INET6)
+ return(0);
+ flags |= RTF_HOST;
+ }
+ if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0)
+ ia->ia_flags |= IFA_ROUTE;
+
+ /* Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). */
+ in6_ifaddloop(&(ia->ia_ifa));
+
+ return(error);
+}
+
+/*
+ * Add an address to the list of IP6 multicast addresses for a
+ * given interface.
+ */
+struct in6_multi *
+in6_addmulti(maddr6, ifp, errorp)
+ register struct in6_addr *maddr6;
+ register struct ifnet *ifp;
+ int *errorp;
+{
+ struct in6_multi *in6m;
+ struct sockaddr_in6 sin6;
+ struct ifmultiaddr *ifma;
+ int s = splnet();
+
+ *errorp = 0;
+
+ /*
+ * Call generic routine to add membership or increment
+ * refcount. It wants addresses in the form of a sockaddr,
+ * so we build one here (being careful to zero the unused bytes).
+ */
+ bzero(&sin6, sizeof sin6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof sin6;
+ sin6.sin6_addr = *maddr6;
+ *errorp = if_addmulti(ifp, (struct sockaddr *)&sin6, &ifma);
+ if (*errorp) {
+ splx(s);
+ return 0;
+ }
+
+ /*
+ * If ifma->ifma_protospec is null, then if_addmulti() created
+ * a new record. Otherwise, we are done.
+ */
+ if (ifma->ifma_protospec != 0)
+ return ifma->ifma_protospec;
+
+ /* XXX - if_addmulti uses M_WAITOK. Can this really be called
+ at interrupt time? If so, need to fix if_addmulti. XXX */
+ in6m = (struct in6_multi *)malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT);
+ if (in6m == NULL) {
+ splx(s);
+ return (NULL);
+ }
+
+ bzero(in6m, sizeof *in6m);
+ in6m->in6m_addr = *maddr6;
+ in6m->in6m_ifp = ifp;
+ in6m->in6m_ifma = ifma;
+ ifma->ifma_protospec = in6m;
+ LIST_INSERT_HEAD(&in6_multihead, in6m, in6m_entry);
+
+ /*
+ * Let MLD6 know that we have joined a new IP6 multicast
+ * group.
+ */
+ mld6_start_listening(in6m);
+ splx(s);
+ return(in6m);
+}
+
+/*
+ * Delete a multicast address record.
+ */
+void
+in6_delmulti(in6m)
+ struct in6_multi *in6m;
+{
+ struct ifmultiaddr *ifma = in6m->in6m_ifma;
+ int s = splnet();
+
+ if (ifma->ifma_refcount == 1) {
+ /*
+ * No remaining claims to this record; let MLD6 know
+ * that we are leaving the multicast group.
+ */
+ mld6_stop_listening(in6m);
+ ifma->ifma_protospec = 0;
+ LIST_REMOVE(in6m, in6m_entry);
+ free(in6m, M_IPMADDR);
+ }
+ /* XXX - should be separate API for when we have an ifma? */
+ if_delmulti(ifma->ifma_ifp, ifma->ifma_addr);
+ splx(s);
+}
+
+/*
+ * Find an IPv6 interface link-local address specific to an interface.
+ */
+struct in6_ifaddr *
+in6ifa_ifpforlinklocal(ifp)
+ struct ifnet *ifp;
+{
+ register struct ifaddr *ifa;
+
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr == NULL)
+ continue; /* just for safety */
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa)))
+ break;
+ }
+
+ return((struct in6_ifaddr *)ifa);
+}
+
+
+/*
+ * find the internet address corresponding to a given interface and address.
+ */
+struct in6_ifaddr *
+in6ifa_ifpwithaddr(ifp, addr)
+ struct ifnet *ifp;
+ struct in6_addr *addr;
+{
+ register struct ifaddr *ifa;
+
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr == NULL)
+ continue; /* just for safety */
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa)))
+ break;
+ }
+
+ return((struct in6_ifaddr *)ifa);
+}
+
+/*
+ * Convert IP6 address to printable (loggable) representation.
+ */
+static char digits[] = "0123456789abcdef";
+static int ip6round = 0;
+char *
+ip6_sprintf(addr)
+register struct in6_addr *addr;
+{
+ static char ip6buf[8][48];
+ register int i;
+ register char *cp;
+ register u_short *a = (u_short *)addr;
+ register u_char *d;
+ int dcolon = 0;
+
+ ip6round = (ip6round + 1) & 7;
+ cp = ip6buf[ip6round];
+
+ for (i = 0; i < 8; i++) {
+ if (dcolon == 1) {
+ if (*a == 0) {
+ if (i == 7)
+ *cp++ = ':';
+ a++;
+ continue;
+ } else
+ dcolon = 2;
+ }
+ if (*a == 0) {
+ if (dcolon == 0 && *(a + 1) == 0) {
+ if (i == 0)
+ *cp++ = ':';
+ *cp++ = ':';
+ dcolon = 1;
+ } else {
+ *cp++ = '0';
+ *cp++ = ':';
+ }
+ a++;
+ continue;
+ }
+ d = (u_char *)a;
+ *cp++ = digits[*d >> 4];
+ *cp++ = digits[*d++ & 0xf];
+ *cp++ = digits[*d >> 4];
+ *cp++ = digits[*d & 0xf];
+ *cp++ = ':';
+ a++;
+ }
+ *--cp = 0;
+ return(ip6buf[ip6round]);
+}
+
+int
+in6_localaddr(in6)
+ struct in6_addr *in6;
+{
+ struct in6_ifaddr *ia;
+
+ if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6))
+ return 1;
+
+ for (ia = in6_ifaddr; ia; ia = ia->ia_next)
+ if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr,
+ &ia->ia_prefixmask.sin6_addr))
+ return 1;
+
+ return (0);
+}
+
+/*
+ * Get a scope of the address. Node-local, link-local, site-local or global.
+ */
+int
+in6_addrscope (addr)
+struct in6_addr *addr;
+{
+ int scope;
+
+ if (addr->s6_addr8[0] == 0xfe) {
+ scope = addr->s6_addr8[1] & 0xc0;
+
+ switch (scope) {
+ case 0x80:
+ return IPV6_ADDR_SCOPE_LINKLOCAL;
+ break;
+ case 0xc0:
+ return IPV6_ADDR_SCOPE_SITELOCAL;
+ break;
+ default:
+ return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
+ break;
+ }
+ }
+
+
+ if (addr->s6_addr8[0] == 0xff) {
+ scope = addr->s6_addr8[1] & 0x0f;
+
+ /*
+ * due to other scope such as reserved,
+ * return scope doesn't work.
+ */
+ switch (scope) {
+ case IPV6_ADDR_SCOPE_NODELOCAL:
+ return IPV6_ADDR_SCOPE_NODELOCAL;
+ break;
+ case IPV6_ADDR_SCOPE_LINKLOCAL:
+ return IPV6_ADDR_SCOPE_LINKLOCAL;
+ break;
+ case IPV6_ADDR_SCOPE_SITELOCAL:
+ return IPV6_ADDR_SCOPE_SITELOCAL;
+ break;
+ default:
+ return IPV6_ADDR_SCOPE_GLOBAL;
+ break;
+ }
+ }
+
+ if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) {
+ if (addr->s6_addr8[15] == 1) /* loopback */
+ return IPV6_ADDR_SCOPE_NODELOCAL;
+ if (addr->s6_addr8[15] == 0) /* unspecified */
+ return IPV6_ADDR_SCOPE_LINKLOCAL;
+ }
+
+ return IPV6_ADDR_SCOPE_GLOBAL;
+}
+
+/*
+ * return length of part which dst and src are equal
+ * hard coding...
+ */
+
+int
+in6_matchlen(src, dst)
+struct in6_addr *src, *dst;
+{
+ int match = 0;
+ u_char *s = (u_char *)src, *d = (u_char *)dst;
+ u_char *lim = s + 16, r;
+
+ while (s < lim)
+ if ((r = (*d++ ^ *s++)) != 0) {
+ while (r < 128) {
+ match++;
+ r <<= 1;
+ }
+ break;
+ } else
+ match += 8;
+ return match;
+}
+
+int
+in6_are_prefix_equal(p1, p2, len)
+ struct in6_addr *p1, *p2;
+ int len;
+{
+ int bytelen, bitlen;
+
+ /* sanity check */
+ if (0 > len || len > 128) {
+ log(LOG_ERR, "in6_are_prefix_equal: invalid prefix length(%d)\n",
+ len);
+ return(0);
+ }
+
+ bytelen = len / 8;
+ bitlen = len % 8;
+
+ if (bcmp(&p1->s6_addr, &p2->s6_addr, bytelen))
+ return(0);
+ if (p1->s6_addr[bytelen] >> (8 - bitlen) !=
+ p2->s6_addr[bytelen] >> (8 - bitlen))
+ return(0);
+
+ return(1);
+}
+
+void
+in6_prefixlen2mask(maskp, len)
+ struct in6_addr *maskp;
+ int len;
+{
+ u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
+ int bytelen, bitlen, i;
+
+ /* sanity check */
+ if (0 > len || len > 128) {
+ log(LOG_ERR, "in6_prefixlen2mask: invalid prefix length(%d)\n",
+ len);
+ return;
+ }
+
+ bzero(maskp, sizeof(*maskp));
+ bytelen = len / 8;
+ bitlen = len % 8;
+ for (i = 0; i < bytelen; i++)
+ maskp->s6_addr[i] = 0xff;
+ if (bitlen)
+ maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
+}
+
+/*
+ * return the best address out of the same scope
+ */
+
+struct in6_ifaddr *
+in6_ifawithscope(ifp, dst)
+ register struct ifnet *ifp;
+ register struct in6_addr *dst;
+{
+ int dst_scope = in6_addrscope(dst), blen = -1, tlen;
+ struct ifaddr *ifa;
+ struct in6_ifaddr *besta = NULL, *ia;
+ struct in6_ifaddr *dep[2]; /*last-resort: deprecated*/
+
+ dep[0] = dep[1] = NULL;
+
+ /*
+ * We first look for addresses in the same scope.
+ * If there is one, return it.
+ * If two or more, return one which matches the dst longest.
+ * If none, return one of global addresses assigned other ifs.
+ */
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
+ continue; /* XXX: is there any case to allow anycast? */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY)
+ continue; /* don't use this interface */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED)
+ continue;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
+ if (ip6_use_deprecated)
+ dep[0] = (struct in6_ifaddr *)ifa;
+ continue;
+ }
+
+ if (dst_scope == in6_addrscope(IFA_IN6(ifa))) {
+ /*
+ * call in6_matchlen() as few as possible
+ */
+ if (besta) {
+ if (blen == -1)
+ blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst);
+ tlen = in6_matchlen(IFA_IN6(ifa), dst);
+ if (tlen > blen) {
+ blen = tlen;
+ besta = (struct in6_ifaddr *)ifa;
+ }
+ } else
+ besta = (struct in6_ifaddr *)ifa;
+ }
+ }
+ if (besta)
+ return besta;
+
+ for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
+ if (IPV6_ADDR_SCOPE_GLOBAL !=
+ in6_addrscope(&(ia->ia_addr.sin6_addr)))
+ continue;
+ /* XXX: is there any case to allow anycast? */
+ if ((ia->ia6_flags & IN6_IFF_ANYCAST) != 0)
+ continue;
+ if ((ia->ia6_flags & IN6_IFF_NOTREADY) != 0)
+ continue;
+ if ((ia->ia6_flags & IN6_IFF_DETACHED) != 0)
+ continue;
+ if ((ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) {
+ if (ip6_use_deprecated)
+ dep[1] = (struct in6_ifaddr *)ifa;
+ continue;
+ }
+ return ia;
+ }
+
+ /* use the last-resort values, that are, deprecated addresses */
+ if (dep[0])
+ return dep[0];
+ if (dep[1])
+ return dep[1];
+
+ return NULL;
+}
+
+/*
+ * return the best address out of the same scope. if no address was
+ * found, return the first valid address from designated IF.
+ */
+
+struct in6_ifaddr *
+in6_ifawithifp(ifp, dst)
+ register struct ifnet *ifp;
+ register struct in6_addr *dst;
+{
+ int dst_scope = in6_addrscope(dst), blen = -1, tlen;
+ struct ifaddr *ifa;
+ struct in6_ifaddr *besta = 0;
+ struct in6_ifaddr *dep[2]; /*last-resort: deprecated*/
+
+ dep[0] = dep[1] = NULL;
+
+ /*
+ * We first look for addresses in the same scope.
+ * If there is one, return it.
+ * If two or more, return one which matches the dst longest.
+ * If none, return one of global addresses assigned other ifs.
+ */
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
+ continue; /* XXX: is there any case to allow anycast? */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY)
+ continue; /* don't use this interface */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED)
+ continue;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
+ if (ip6_use_deprecated)
+ dep[0] = (struct in6_ifaddr *)ifa;
+ continue;
+ }
+
+ if (dst_scope == in6_addrscope(IFA_IN6(ifa))) {
+ /*
+ * call in6_matchlen() as few as possible
+ */
+ if (besta) {
+ if (blen == -1)
+ blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst);
+ tlen = in6_matchlen(IFA_IN6(ifa), dst);
+ if (tlen > blen) {
+ blen = tlen;
+ besta = (struct in6_ifaddr *)ifa;
+ }
+ } else
+ besta = (struct in6_ifaddr *)ifa;
+ }
+ }
+ if (besta)
+ return(besta);
+
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
+ continue; /* XXX: is there any case to allow anycast? */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY)
+ continue; /* don't use this interface */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED)
+ continue;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
+ if (ip6_use_deprecated)
+ dep[1] = (struct in6_ifaddr *)ifa;
+ continue;
+ }
+
+ return (struct in6_ifaddr *)ifa;
+ }
+
+ /* use the last-resort values, that are, deprecated addresses */
+ if (dep[0])
+ return dep[0];
+ if (dep[1])
+ return dep[1];
+
+ return NULL;
+}
+
+/*
+ * perform DAD when interface becomes IFF_UP.
+ */
+void
+in6_if_up(ifp)
+ struct ifnet *ifp;
+{
+ struct ifaddr *ifa;
+ struct in6_ifaddr *ia;
+ struct sockaddr_dl *sdl;
+ int type;
+ struct ether_addr ea;
+ int off;
+ int dad_delay; /* delay ticks before DAD output */
+
+ bzero(&ea, sizeof(ea));
+ sdl = NULL;
+
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family == AF_INET6
+ && IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr)) {
+ goto dad;
+ }
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ break;
+ }
+
+ switch (ifp->if_type) {
+ case IFT_SLIP:
+ case IFT_PPP:
+#ifdef IFT_DUMMY
+ case IFT_DUMMY:
+#endif
+ case IFT_GIF:
+ case IFT_FAITH:
+ type = IN6_IFT_P2P;
+ in6_ifattach(ifp, type, 0, 1);
+ break;
+ case IFT_ETHER:
+ case IFT_FDDI:
+ case IFT_ATM:
+ type = IN6_IFT_802;
+ if (sdl == NULL)
+ break;
+ off = sdl->sdl_nlen;
+ if (bcmp(&sdl->sdl_data[off], &ea, sizeof(ea)) != 0)
+ in6_ifattach(ifp, type, LLADDR(sdl), 0);
+ break;
+ case IFT_ARCNET:
+ type = IN6_IFT_ARCNET;
+ if (sdl == NULL)
+ break;
+ off = sdl->sdl_nlen;
+ if (sdl->sdl_data[off] != 0) /* XXX ?: */
+ in6_ifattach(ifp, type, LLADDR(sdl), 0);
+ break;
+ default:
+ break;
+ }
+
+dad:
+ dad_delay = 0;
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ ia = (struct in6_ifaddr *)ifa;
+ if (ia->ia6_flags & IN6_IFF_TENTATIVE)
+ nd6_dad_start(ifa, &dad_delay);
+ }
+}
+
+/*
+ * Calculate max IPv6 MTU through all the interfaces and store it
+ * to in6_maxmtu.
+ */
+void
+in6_setmaxmtu()
+{
+ unsigned long maxmtu = 0;
+ struct ifnet *ifp;
+
+ for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
+ {
+ if ((ifp->if_flags & IFF_LOOPBACK) == 0 &&
+ nd_ifinfo[ifp->if_index].linkmtu > maxmtu)
+ maxmtu = nd_ifinfo[ifp->if_index].linkmtu;
+ }
+ if (maxmtu) /* update only when maxmtu is positive */
+ in6_maxmtu = maxmtu;
+}
+
+/*
+ * Convert sockaddr_in6 to sockaddr_in. Original sockaddr_in6 must be
+ * v4 mapped addr or v4 compat addr
+ */
+void
+in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
+{
+ bzero(sin, sizeof(*sin));
+ sin->sin_len = sizeof(struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_port = sin6->sin6_port;
+ sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3];
+}
+
+/* Convert sockaddr_in to sockaddr_in6 in v4 mapped addr format. */
+void
+in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
+{
+ bzero(sin6, sizeof(*sin6));
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = sin->sin_port;
+ sin6->sin6_addr.s6_addr32[0] = 0;
+ sin6->sin6_addr.s6_addr32[1] = 0;
+ sin6->sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP;
+ sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr;
+}
+
+/* Convert sockaddr_in6 into sockaddr_in. */
+void
+in6_sin6_2_sin_in_sock(struct sockaddr *nam)
+{
+ struct sockaddr_in *sin_p;
+ struct sockaddr_in6 sin6;
+
+ /*
+ * Save original sockaddr_in6 addr and convert it
+ * to sockaddr_in.
+ */
+ sin6 = *(struct sockaddr_in6 *)nam;
+ sin_p = (struct sockaddr_in *)nam;
+ in6_sin6_2_sin(sin_p, &sin6);
+}
+
+/* Convert sockaddr_in into sockaddr_in6 in v4 mapped addr format. */
+void
+in6_sin_2_v4mapsin6_in_sock(struct sockaddr **nam)
+{
+ struct sockaddr_in *sin_p;
+ struct sockaddr_in6 *sin6_p;
+
+ MALLOC(sin6_p, struct sockaddr_in6 *, sizeof *sin6_p, M_SONAME,
+ M_WAITOK);
+ sin_p = (struct sockaddr_in *)*nam;
+ in6_sin_2_v4mapsin6(sin_p, sin6_p);
+ FREE(*nam, M_SONAME);
+ *nam = (struct sockaddr *)sin6_p;
+}
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
index 97b0c7e..a934e9b 100644
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -1,7 +1,7 @@
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -13,7 +13,7 @@
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -516,22 +516,10 @@ struct in6_pktinfo {
#define IPV6CTL_GIF_HLIM 19 /* default HLIM for gif encap packet */
#define IPV6CTL_USE_DEPRECATED 21 /* use deprecated addr (RFC2462 5.5.4) */
#define IPV6CTL_RR_PRUNE 22 /* walk timer for router renumbering */
-#ifdef MAPPED_ADDR_ENABLED
#define IPV6CTL_MAPPED_ADDR 23
-#endif /* MAPPED_ADDR_ENABLED */
/* New entries should be added here from current IPV6CTL_MAXID value. */
#define IPV6CTL_MAXID 24
-#ifdef MAPPED_ADDR_ENABLED
-#define IPV6CTL_NAMES_MAPPED_ADDR "mapped_addr"
-#define IPV6CTL_TYPE_MAPPED_ADDR CTLTYPE_INT
-#define IPV6CTL_VARS_MAPPED_ADDR &ip6_mapped_addr_on
-#else /* MAPPED_ADDR_ENABLED */
-#define IPV6CTL_NAMES_MAPPED_ADDR 0
-#define IPV6CTL_TYPE_MAPPED_ADDR 0
-#define IPV6CTL_VARS_MAPPED_ADDR 0
-#endif /* MAPPED_ADDR_ENABLED */
-
#define IPV6CTL_NAMES { \
{ 0, 0 }, \
{ "forwarding", CTLTYPE_INT }, \
@@ -556,7 +544,7 @@ struct in6_pktinfo {
{ 0, 0 }, \
{ "use_deprecated", CTLTYPE_INT }, \
{ "rr_prune", CTLTYPE_INT }, \
- { IPV6CTL_NAMES_MAPPED_ADDR, IPV6CTL_TYPE_MAPPED_ADDR }, \
+ { "mapped_addr", CTLTYPE_INT }, \
}
#define IPV6CTL_VARS { \
@@ -583,12 +571,23 @@ struct in6_pktinfo {
0, \
&ip6_use_deprecated, \
&ip6_rr_prune, \
- IPV6CTL_VARS_MAPPED_ADDR, \
+ &ip6_mapped_addr_on, \
}
#endif /* !_XOPEN_SOURCE */
+/*
+ * Redefinition of mbuf flags
+ */
+#define M_ANYCAST6 M_PROTO1
+#define M_AUTHIPHDR M_PROTO2
+#define M_DECRYPTED M_PROTO3
+#define M_LOOP M_PROTO4
+#define M_AUTHIPDGM M_PROTO5
+
#ifdef _KERNEL
-struct cmsghdr;
+struct cmsghdr;
+struct mbuf;
+struct ifnet;
int in6_canforward __P((struct in6_addr *, struct in6_addr *));
int in6_cksum __P((struct mbuf *, u_int8_t, int, int));
@@ -597,7 +596,6 @@ int in6_addrscope __P((struct in6_addr *));
struct in6_ifaddr *in6_ifawithscope __P((struct ifnet *, struct in6_addr *));
struct in6_ifaddr *in6_ifawithifp __P((struct ifnet *, struct in6_addr *));
extern void in6_if_up __P((struct ifnet *));
-#ifdef MAPPED_ADDR_ENABLED
struct sockaddr;
void in6_sin6_2_sin __P((struct sockaddr_in *sin,
@@ -606,7 +604,6 @@ void in6_sin_2_v4mapsin6 __P((struct sockaddr_in *sin,
struct sockaddr_in6 *sin6));
void in6_sin6_2_sin_in_sock __P((struct sockaddr *nam));
void in6_sin_2_v4mapsin6_in_sock __P((struct sockaddr **nam));
-#endif /* MAPPED_ADDR_ENABLED */
#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
#define sin6tosa(sin6) ((struct sockaddr *)(sin6))
diff --git a/sys/netinet6/in6_cksum.c b/sys/netinet6/in6_cksum.c
new file mode 100644
index 0000000..28af01f
--- /dev/null
+++ b/sys/netinet6/in6_cksum.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1988, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+#include <netinet/in.h>
+#include <netinet6/ip6.h>
+
+#include <net/net_osdep.h>
+
+/*
+ * Checksum routine for Internet Protocol family headers (Portable Version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
+
+static union {
+ u_int16_t phs[4];
+ struct {
+ u_int32_t ph_len;
+ u_int8_t ph_zero[3];
+ u_int8_t ph_nxt;
+ } ph;
+} uph;
+
+/*
+ * m MUST contain a continuous IP6 header.
+ * off is a offset where TCP/UDP/ICMP6 header starts.
+ * len is a total length of a transport segment.
+ * (e.g. TCP header + TCP payload)
+ */
+
+int
+in6_cksum(m, nxt, off, len)
+ register struct mbuf *m;
+ u_int8_t nxt;
+ register int off, len;
+{
+ register u_int16_t *w;
+ register int sum = 0;
+ register int mlen = 0;
+ int byte_swapped = 0;
+ struct ip6_hdr *ip6;
+
+ union {
+ u_int8_t c[2];
+ u_int16_t s;
+ } s_util;
+ union {
+ u_int16_t s[2];
+ u_int32_t l;
+ } l_util;
+
+ /* sanity check */
+ if (m->m_pkthdr.len < off + len) {
+ panic("in6_cksum: mbuf len (%d) < off+len (%d+%d)\n",
+ m->m_pkthdr.len, off, len);
+ }
+
+ /*
+ * First create IP6 pseudo header and calculate a summary.
+ */
+ ip6 = mtod(m, struct ip6_hdr *);
+ w = (u_int16_t *)&ip6->ip6_src;
+ uph.ph.ph_len = htonl(len);
+ uph.ph.ph_nxt = nxt;
+
+ /* IPv6 source address */
+ sum += w[0];
+ if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
+ sum += w[1];
+ sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
+ sum += w[6]; sum += w[7];
+ /* IPv6 destination address */
+ sum += w[8];
+ if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
+ sum += w[9];
+ sum += w[10]; sum += w[11]; sum += w[12]; sum += w[13];
+ sum += w[14]; sum += w[15];
+ /* Payload length and upper layer identifier */
+ sum += uph.phs[0]; sum += uph.phs[1];
+ sum += uph.phs[2]; sum += uph.phs[3];
+
+ /*
+ * Secondly calculate a summary of the first mbuf excluding offset.
+ */
+ while (m != NULL && off > 0) {
+ if (m->m_len <= off)
+ off -= m->m_len;
+ else
+ break;
+ m = m->m_next;
+ }
+ w = (u_int16_t *)(mtod(m, u_char *) + off);
+ mlen = m->m_len - off;
+ if (len < mlen)
+ mlen = len;
+ len -= mlen;
+ /*
+ * Force to even boundary.
+ */
+ if ((1 & (long) w) && (mlen > 0)) {
+ REDUCE;
+ sum <<= 8;
+ s_util.c[0] = *(u_char *)w;
+ w = (u_int16_t *)((char *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ /*
+ * Unroll the loop to make overhead from
+ * branches &c small.
+ */
+ while ((mlen -= 32) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+ sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
+ sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
+ w += 16;
+ }
+ mlen += 32;
+ while ((mlen -= 8) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ w += 4;
+ }
+ mlen += 8;
+ if (mlen == 0 && byte_swapped == 0)
+ goto next;
+ REDUCE;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++;
+ }
+ if (byte_swapped) {
+ REDUCE;
+ sum <<= 8;
+ byte_swapped = 0;
+ if (mlen == -1) {
+ s_util.c[1] = *(char *)w;
+ sum += s_util.s;
+ mlen = 0;
+ } else
+ mlen = -1;
+ } else if (mlen == -1)
+ s_util.c[0] = *(char *)w;
+ next:
+ m = m->m_next;
+
+ /*
+ * Lastly calculate a summary of the rest of mbufs.
+ */
+
+ for (;m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+ w = mtod(m, u_int16_t *);
+ if (mlen == -1) {
+ /*
+ * The first byte of this mbuf is the continuation
+ * of a word spanning between this mbuf and the
+ * last mbuf.
+ *
+ * s_util.c[0] is already saved when scanning previous
+ * mbuf.
+ */
+ s_util.c[1] = *(char *)w;
+ sum += s_util.s;
+ w = (u_int16_t *)((char *)w + 1);
+ mlen = m->m_len - 1;
+ len--;
+ } else
+ mlen = m->m_len;
+ if (len < mlen)
+ mlen = len;
+ len -= mlen;
+ /*
+ * Force to even boundary.
+ */
+ if ((1 & (long) w) && (mlen > 0)) {
+ REDUCE;
+ sum <<= 8;
+ s_util.c[0] = *(u_char *)w;
+ w = (u_int16_t *)((char *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ /*
+ * Unroll the loop to make overhead from
+ * branches &c small.
+ */
+ while ((mlen -= 32) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+ sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
+ sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
+ w += 16;
+ }
+ mlen += 32;
+ while ((mlen -= 8) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ w += 4;
+ }
+ mlen += 8;
+ if (mlen == 0 && byte_swapped == 0)
+ continue;
+ REDUCE;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++;
+ }
+ if (byte_swapped) {
+ REDUCE;
+ sum <<= 8;
+ byte_swapped = 0;
+ if (mlen == -1) {
+ s_util.c[1] = *(char *)w;
+ sum += s_util.s;
+ mlen = 0;
+ } else
+ mlen = -1;
+ } else if (mlen == -1)
+ s_util.c[0] = *(char *)w;
+ }
+ if (len)
+ panic("in6_cksum: out of data\n");
+ if (mlen == -1) {
+ /* The last mbuf has odd # of bytes. Follow the
+ standard (the odd byte may be shifted left by 8 bits
+ or not as determined by endian-ness of the machine) */
+ s_util.c[1] = 0;
+ sum += s_util.s;
+ }
+ REDUCE;
+ return (~sum & 0xffff);
+}
diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
new file mode 100644
index 0000000..85ca75f
--- /dev/null
+++ b/sys/netinet6/in6_ifattach.c
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/kernel.h>
+#include <sys/md5.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+
+#include <netinet6/in6.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_ifattach.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+
+#include <net/net_osdep.h>
+
+static struct in6_addr llsol;
+
+struct in6_ifstat **in6_ifstat = NULL;
+struct icmp6_ifstat **icmp6_ifstat = NULL;
+size_t in6_ifstatmax = 0;
+size_t icmp6_ifstatmax = 0;
+unsigned long in6_maxmtu = 0;
+
+int found_first_ifid = 0;
+#define IFID_LEN 8
+static char first_ifid[IFID_LEN];
+
+static int laddr_to_eui64 __P((u_int8_t *, u_int8_t *, size_t));
+static int gen_rand_eui64 __P((u_int8_t *));
+
+static int
+laddr_to_eui64(dst, src, len)
+ u_int8_t *dst;
+ u_int8_t *src;
+ size_t len;
+{
+ static u_int8_t zero[8];
+
+ bzero(zero, sizeof(zero));
+
+ switch (len) {
+ case 6:
+ if (bcmp(zero, src, 6) == 0)
+ return EINVAL;
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = 0xff;
+ dst[4] = 0xfe;
+ dst[5] = src[3];
+ dst[6] = src[4];
+ dst[7] = src[5];
+ break;
+ case 8:
+ if (bcmp(zero, src, 8) == 0)
+ return EINVAL;
+ bcopy(src, dst, len);
+ break;
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Generate a last-resort interface identifier, when the machine has no
+ * IEEE802/EUI64 address sources.
+ * The address should be random, and should not change across reboot.
+ */
+static int
+gen_rand_eui64(dst)
+ u_int8_t *dst;
+{
+ MD5_CTX ctxt;
+ u_int8_t digest[16];
+ int hostnamelen = strlen(hostname);
+
+ /* generate 8bytes of pseudo-random value. */
+ bzero(&ctxt, sizeof(ctxt));
+ MD5Init(&ctxt);
+ MD5Update(&ctxt, hostname, hostnamelen);
+ MD5Final(digest, &ctxt);
+
+ /* assumes sizeof(digest) > sizeof(first_ifid) */
+ bcopy(digest, dst, 8);
+
+ /* make sure to set "u" bit to local, and "g" bit to individual. */
+ dst[0] &= 0xfe;
+ dst[0] |= 0x02; /* EUI64 "local" */
+
+ return 0;
+}
+
+/*
+ * Find first ifid on list of interfaces.
+ * This is assumed that ifp0's interface token (for example, IEEE802 MAC)
+ * is globally unique. We may need to have a flag parameter in the future.
+ */
+int
+in6_ifattach_getifid(ifp0)
+ struct ifnet *ifp0;
+{
+ struct ifnet *ifp;
+ struct ifaddr *ifa;
+ u_int8_t *addr = NULL;
+ int addrlen = 0;
+ struct sockaddr_dl *sdl;
+
+ if (found_first_ifid)
+ return 0;
+
+ for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next)
+ {
+ if (ifp0 != NULL && ifp0 != ifp)
+ continue;
+ for (ifa = ifp->if_addrlist.tqh_first;
+ ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ if (sdl == NULL)
+ continue;
+ if (sdl->sdl_alen == 0)
+ continue;
+ switch (ifp->if_type) {
+ case IFT_ETHER:
+ case IFT_FDDI:
+ case IFT_ATM:
+ /* IEEE802/EUI64 cases - what others? */
+ addr = LLADDR(sdl);
+ addrlen = sdl->sdl_alen;
+ /*
+ * to copy ifid from IEEE802/EUI64 interface,
+ * u bit of the source needs to be 0.
+ */
+ if ((addr[0] & 0x02) != 0)
+ break;
+ goto found;
+ case IFT_ARCNET:
+ /*
+ * ARCnet interface token cannot be used as
+ * globally unique identifier due to its
+ * small bitwidth.
+ */
+ break;
+ default:
+ break;
+ }
+ }
+ }
+#ifdef DEBUG
+ printf("in6_ifattach_getifid: failed to get EUI64");
+#endif
+ return EADDRNOTAVAIL;
+
+found:
+ if (laddr_to_eui64(first_ifid, addr, addrlen) == 0)
+ found_first_ifid = 1;
+
+ if (found_first_ifid) {
+ printf("%s: supplying EUI64: "
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ if_name(ifp),
+ first_ifid[0] & 0xff, first_ifid[1] & 0xff,
+ first_ifid[2] & 0xff, first_ifid[3] & 0xff,
+ first_ifid[4] & 0xff, first_ifid[5] & 0xff,
+ first_ifid[6] & 0xff, first_ifid[7] & 0xff);
+
+ /* invert u bit to convert EUI64 to RFC2373 interface ID. */
+ first_ifid[0] ^= 0x02;
+
+ return 0;
+ } else {
+#ifdef DEBUG
+ printf("in6_ifattach_getifid: failed to get EUI64");
+#endif
+ return EADDRNOTAVAIL;
+ }
+}
+
+/*
+ * add link-local address to *pseudo* p2p interfaces.
+ * get called when the first MAC address is made available in in6_ifattach().
+ *
+ * XXX I start considering this loop as a bad idea. (itojun)
+ */
+void
+in6_ifattach_p2p()
+{
+ struct ifnet *ifp;
+
+ /* prevent infinite loop. just in case. */
+ if (found_first_ifid == 0)
+ return;
+
+ for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next)
+ {
+ switch (ifp->if_type) {
+ case IFT_GIF:
+ /* pseudo interfaces - safe to initialize here */
+ in6_ifattach(ifp, IN6_IFT_P2P, 0, 0);
+ break;
+#ifdef IFT_DUMMY
+ case IFT_DUMMY:
+#endif
+ case IFT_FAITH:
+ /* this mistakingly becomes IFF_UP */
+ break;
+ case IFT_SLIP:
+ /* IPv6 is not supported */
+ break;
+ case IFT_PPP:
+ /* this is not a pseudo interface, skip it */
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void
+in6_ifattach(ifp, type, laddr, noloop)
+ struct ifnet *ifp;
+ u_int type;
+ caddr_t laddr;
+ /* size_t laddrlen; */
+ int noloop;
+{
+ static size_t if_indexlim = 8;
+ struct sockaddr_in6 mltaddr;
+ struct sockaddr_in6 mltmask;
+ struct sockaddr_in6 gate;
+ struct sockaddr_in6 mask;
+
+ struct in6_ifaddr *ia, *ib, *oia;
+ struct ifaddr *ifa;
+ int rtflag = 0;
+
+ if (type == IN6_IFT_P2P && found_first_ifid == 0) {
+ printf("%s: no ifid available for IPv6 link-local address\n",
+ if_name(ifp));
+ /* last resort */
+ if (gen_rand_eui64(first_ifid) == 0) {
+ printf("%s: using random value as EUI64: "
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ if_name(ifp),
+ first_ifid[0] & 0xff, first_ifid[1] & 0xff,
+ first_ifid[2] & 0xff, first_ifid[3] & 0xff,
+ first_ifid[4] & 0xff, first_ifid[5] & 0xff,
+ first_ifid[6] & 0xff, first_ifid[7] & 0xff);
+ /*
+ * invert u bit to convert EUI64 to RFC2373 interface
+ * ID.
+ */
+ first_ifid[0] ^= 0x02;
+
+ found_first_ifid = 1;
+ }
+ }
+
+ if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+ printf("%s: not multicast capable, IPv6 not enabled\n",
+ if_name(ifp));
+ return;
+ }
+
+ /*
+ * We have some arrays that should be indexed by if_index.
+ * since if_index will grow dynamically, they should grow too.
+ * struct in6_ifstat **in6_ifstat
+ * struct icmp6_ifstat **icmp6_ifstat
+ */
+ if (in6_ifstat == NULL || icmp6_ifstat == NULL
+ || if_index >= if_indexlim) {
+ size_t n;
+ caddr_t q;
+ size_t olim;
+
+ olim = if_indexlim;
+ while (if_index >= if_indexlim)
+ if_indexlim <<= 1;
+
+ /* grow in6_ifstat */
+ n = if_indexlim * sizeof(struct in6_ifstat *);
+ q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
+ bzero(q, n);
+ if (in6_ifstat) {
+ bcopy((caddr_t)in6_ifstat, q,
+ olim * sizeof(struct in6_ifstat *));
+ free((caddr_t)in6_ifstat, M_IFADDR);
+ }
+ in6_ifstat = (struct in6_ifstat **)q;
+ in6_ifstatmax = if_indexlim;
+
+ /* grow icmp6_ifstat */
+ n = if_indexlim * sizeof(struct icmp6_ifstat *);
+ q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
+ bzero(q, n);
+ if (icmp6_ifstat) {
+ bcopy((caddr_t)icmp6_ifstat, q,
+ olim * sizeof(struct icmp6_ifstat *));
+ free((caddr_t)icmp6_ifstat, M_IFADDR);
+ }
+ icmp6_ifstat = (struct icmp6_ifstat **)q;
+ icmp6_ifstatmax = if_indexlim;
+ }
+
+ /*
+ * To prevent to assign link-local address to PnP network
+ * cards multiple times.
+ * This is lengthy for P2P and LOOP but works.
+ */
+ ifa = TAILQ_FIRST(&ifp->if_addrlist);
+ if (ifa != NULL) {
+ for ( ; ifa; ifa = TAILQ_NEXT(ifa, ifa_list)) {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr))
+ return;
+ }
+ } else {
+ TAILQ_INIT(&ifp->if_addrlist);
+ }
+
+ /*
+ * link-local address
+ */
+ ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK);
+ bzero((caddr_t)ia, sizeof(*ia));
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
+ ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
+ ia->ia_ifp = ifp;
+ TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+ /*
+ * Also link into the IPv6 address chain beginning with in6_ifaddr.
+ * kazu opposed it, but itojun & jinmei wanted.
+ */
+ if ((oia = in6_ifaddr) != NULL) {
+ for (; oia->ia_next; oia = oia->ia_next)
+ continue;
+ oia->ia_next = ia;
+ } else
+ in6_ifaddr = ia;
+
+ ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ ia->ia_prefixmask.sin6_family = AF_INET6;
+ ia->ia_prefixmask.sin6_addr = in6mask64;
+
+ bzero(&ia->ia_addr, sizeof(struct sockaddr_in6));
+ ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
+ ia->ia_addr.sin6_family = AF_INET6;
+ ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
+ ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ ia->ia_addr.sin6_addr.s6_addr32[1] = 0;
+
+ switch (type) {
+ case IN6_IFT_LOOP:
+ ia->ia_addr.sin6_addr.s6_addr32[2] = 0;
+ ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1);
+ break;
+ case IN6_IFT_802:
+ ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
+ ia->ia_ifa.ifa_flags |= RTF_CLONING;
+ rtflag = RTF_CLONING;
+ /* fall through */
+ case IN6_IFT_P2P802:
+ if (laddr == NULL)
+ break;
+ /* XXX use laddrlen */
+ if (laddr_to_eui64(&ia->ia_addr.sin6_addr.s6_addr8[8],
+ laddr, 6) != 0) {
+ break;
+ }
+ /* invert u bit to convert EUI64 to RFC2373 interface ID. */
+ ia->ia_addr.sin6_addr.s6_addr8[8] ^= 0x02;
+ if (found_first_ifid == 0) {
+ if (in6_ifattach_getifid(ifp) == 0)
+ in6_ifattach_p2p();
+ }
+ break;
+ case IN6_IFT_P2P:
+ bcopy((caddr_t)first_ifid,
+ (caddr_t)&ia->ia_addr.sin6_addr.s6_addr8[8],
+ IFID_LEN);
+ break;
+ case IN6_IFT_ARCNET:
+ ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
+ ia->ia_ifa.ifa_flags |= RTF_CLONING;
+ rtflag = RTF_CLONING;
+ if (laddr == NULL)
+ break;
+
+ /* make non-global IF id out of link-level address */
+ bzero(&ia->ia_addr.sin6_addr.s6_addr8[8], 7);
+ ia->ia_addr.sin6_addr.s6_addr8[15] = *laddr;
+ }
+
+ ia->ia_ifa.ifa_metric = ifp->if_metric;
+
+ if (ifp->if_ioctl != NULL) {
+ int s;
+ int error;
+
+ /*
+ * give the interface a chance to initialize, in case this
+ * is the first address to be added.
+ */
+ s = splimp();
+ error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia);
+ splx(s);
+
+ if (error) {
+ switch (error) {
+ case EAFNOSUPPORT:
+ printf("%s: IPv6 not supported\n",
+ if_name(ifp));
+ break;
+ default:
+ printf("%s: SIOCSIFADDR error %d\n",
+ if_name(ifp), error);
+ break;
+ }
+
+ /* undo changes */
+ TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+ if (oia)
+ oia->ia_next = ia->ia_next;
+ else
+ in6_ifaddr = ia->ia_next;
+ free(ia, M_IFADDR);
+ return;
+ }
+ }
+
+ /* add route to the interface. */
+ rtrequest(RTM_ADD,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&ia->ia_prefixmask,
+ RTF_UP|rtflag,
+ (struct rtentry **)0);
+ ia->ia_flags |= IFA_ROUTE;
+
+ if (type == IN6_IFT_P2P || type == IN6_IFT_P2P802) {
+ /*
+ * route local address to loopback
+ */
+ bzero(&gate, sizeof(gate));
+ gate.sin6_len = sizeof(struct sockaddr_in6);
+ gate.sin6_family = AF_INET6;
+ gate.sin6_addr = in6addr_loopback;
+ bzero(&mask, sizeof(mask));
+ mask.sin6_len = sizeof(struct sockaddr_in6);
+ mask.sin6_family = AF_INET6;
+ mask.sin6_addr = in6mask64;
+ rtrequest(RTM_ADD,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&gate,
+ (struct sockaddr *)&mask,
+ RTF_UP|RTF_HOST,
+ (struct rtentry **)0);
+ }
+
+ /*
+ * loopback address
+ */
+ ib = (struct in6_ifaddr *)NULL;
+ if (type == IN6_IFT_LOOP) {
+ ib = (struct in6_ifaddr *)
+ malloc(sizeof(*ib), M_IFADDR, M_WAITOK);
+ bzero((caddr_t)ib, sizeof(*ib));
+ ib->ia_ifa.ifa_addr = (struct sockaddr *)&ib->ia_addr;
+ ib->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ib->ia_dstaddr;
+ ib->ia_ifa.ifa_netmask = (struct sockaddr *)&ib->ia_prefixmask;
+ ib->ia_ifp = ifp;
+
+ ia->ia_next = ib;
+ TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ib,
+ ifa_list);
+
+ ib->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ ib->ia_prefixmask.sin6_family = AF_INET6;
+ ib->ia_prefixmask.sin6_addr = in6mask128;
+ ib->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
+ ib->ia_addr.sin6_family = AF_INET6;
+ ib->ia_addr.sin6_addr = in6addr_loopback;
+ ib->ia_ifa.ifa_metric = ifp->if_metric;
+
+ rtrequest(RTM_ADD,
+ (struct sockaddr *)&ib->ia_addr,
+ (struct sockaddr *)&ib->ia_addr,
+ (struct sockaddr *)&ib->ia_prefixmask,
+ RTF_UP|RTF_HOST,
+ (struct rtentry **)0);
+
+ ib->ia_flags |= IFA_ROUTE;
+ }
+
+ /*
+ * join multicast
+ */
+ if (ifp->if_flags & IFF_MULTICAST) {
+ int error; /* not used */
+
+ bzero(&mltmask, sizeof(mltmask));
+ mltmask.sin6_len = sizeof(struct sockaddr_in6);
+ mltmask.sin6_family = AF_INET6;
+ mltmask.sin6_addr = in6mask32;
+
+ /*
+ * join link-local all-nodes address
+ */
+ bzero(&mltaddr, sizeof(mltaddr));
+ mltaddr.sin6_len = sizeof(struct sockaddr_in6);
+ mltaddr.sin6_family = AF_INET6;
+ mltaddr.sin6_addr = in6addr_linklocal_allnodes;
+ mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ rtrequest(RTM_ADD,
+ (struct sockaddr *)&mltaddr,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&mltmask,
+ RTF_UP|RTF_CLONING, /* xxx */
+ (struct rtentry **)0);
+ (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
+
+ if (type == IN6_IFT_LOOP) {
+ /*
+ * join node-local all-nodes address
+ */
+ mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
+ rtrequest(RTM_ADD,
+ (struct sockaddr *)&mltaddr,
+ (struct sockaddr *)&ib->ia_addr,
+ (struct sockaddr *)&mltmask,
+ RTF_UP,
+ (struct rtentry **)0);
+ (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
+ } else {
+ /*
+ * join solicited multicast address
+ */
+ bzero(&llsol, sizeof(llsol));
+ llsol.s6_addr16[0] = htons(0xff02);
+ llsol.s6_addr16[1] = htons(ifp->if_index);
+ llsol.s6_addr32[1] = 0;
+ llsol.s6_addr32[2] = htonl(1);
+ llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3];
+ llsol.s6_addr8[12] = 0xff;
+ (void)in6_addmulti(&llsol, ifp, &error);
+ }
+ }
+
+ /* update dynamically. */
+ if (in6_maxmtu < ifp->if_mtu)
+ in6_maxmtu = ifp->if_mtu;
+
+ if (in6_ifstat[ifp->if_index] == NULL) {
+ in6_ifstat[ifp->if_index] = (struct in6_ifstat *)
+ malloc(sizeof(struct in6_ifstat), M_IFADDR, M_WAITOK);
+ bzero(in6_ifstat[ifp->if_index], sizeof(struct in6_ifstat));
+ }
+ if (icmp6_ifstat[ifp->if_index] == NULL) {
+ icmp6_ifstat[ifp->if_index] = (struct icmp6_ifstat *)
+ malloc(sizeof(struct icmp6_ifstat), M_IFADDR, M_WAITOK);
+ bzero(icmp6_ifstat[ifp->if_index], sizeof(struct icmp6_ifstat));
+ }
+
+ /* initialize NDP variables */
+ nd6_ifattach(ifp);
+
+ /* mark the address TENTATIVE, if needed. */
+ switch (ifp->if_type) {
+ case IFT_ARCNET:
+ case IFT_ETHER:
+ case IFT_FDDI:
+ ia->ia6_flags |= IN6_IFF_TENTATIVE;
+ /* nd6_dad_start() will be called in in6_if_up */
+ break;
+#ifdef IFT_DUMMY
+ case IFT_DUMMY:
+#endif
+ case IFT_GIF: /*XXX*/
+ case IFT_LOOP:
+ case IFT_FAITH:
+ default:
+ break;
+ }
+
+ return;
+}
+
+void
+in6_ifdetach(ifp)
+ struct ifnet *ifp;
+{
+ struct in6_ifaddr *ia, *oia;
+ struct ifaddr *ifa;
+ struct rtentry *rt;
+ short rtflags;
+
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6
+ || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) {
+ continue;
+ }
+
+ ia = (struct in6_ifaddr *)ifa;
+
+ /* remove from the routing table */
+ if ((ia->ia_flags & IFA_ROUTE)
+ && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0UL))) {
+ rtflags = rt->rt_flags;
+ rtfree(rt);
+ rtrequest(RTM_DELETE,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&ia->ia_prefixmask,
+ rtflags, (struct rtentry **)0);
+ }
+
+ /* remove from the linked list */
+ TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+
+ /* also remove from the IPv6 address chain(itojun&jinmei) */
+ oia = ia;
+ if (oia == (ia = in6_ifaddr))
+ in6_ifaddr = ia->ia_next;
+ else {
+ while (ia->ia_next && (ia->ia_next != oia))
+ ia = ia->ia_next;
+ if (ia->ia_next)
+ ia->ia_next = oia->ia_next;
+#ifdef DEBUG
+ else
+ printf("%s: didn't unlink in6ifaddr from "
+ "list\n", if_name(ifp));
+#endif
+ }
+
+ free(ia, M_IFADDR);
+ }
+}
diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h
new file mode 100644
index 0000000..03dbac7
--- /dev/null
+++ b/sys/netinet6/in6_ifattach.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETINET6_IN6_IFATTACH_H_
+#define _NETINET6_IN6_IFATTACH_H_
+
+#ifdef _KERNEL
+extern int found_first_ifid;
+
+int in6_ifattach_getifid __P((struct ifnet *));
+void in6_ifattach_p2p __P((void));
+void in6_ifattach __P((struct ifnet *, u_int, caddr_t, int));
+void in6_ifdetach __P((struct ifnet *));
+#endif /* _KERNEL */
+
+#define IN6_IFT_LOOP 1
+#define IN6_IFT_P2P 2
+#define IN6_IFT_802 3
+#define IN6_IFT_P2P802 4
+#define IN6_IFT_ARCNET 5
+
+#endif /* _NETINET6_IN6_IFATTACH_H_ */
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
new file mode 100644
index 0000000..884410a
--- /dev/null
+++ b/sys/netinet6/in6_pcb.c
@@ -0,0 +1,1131 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94
+ * $FreeBSD$
+ */
+
+#include "opt_inet.h"
+#include "opt_ipsec.h"
+#include "opt_key.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/jail.h>
+
+#include <vm/vm_zone.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet/in_pcb.h>
+#include <netinet6/in6_pcb.h>
+
+/* #include "faith.h" */
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif /* INET6 */
+#include <netkey/key.h>
+#ifdef KEY_DEBUG
+#include <netkey/key_debug.h>
+#ifdef INET6
+#include <netkey/key_debug6.h>
+#endif /* INET6 */
+#else
+#define DPRINTF(lev,arg)
+#define DDO(lev, stmt)
+#define DP(x, y, z)
+#endif /* KEY_DEBUG */
+#endif /* IPSEC */
+
+struct in6_addr zeroin6_addr;
+
+int
+in6_pcbbind(inp, nam, p)
+ register struct inpcb *inp;
+ struct sockaddr *nam;
+ struct proc *p;
+{
+ struct socket *so = inp->inp_socket;
+ unsigned short *lastport;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL;
+ struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+ u_short lport = 0;
+ int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
+ int error;
+
+ if (!in6_ifaddr) /* XXX broken! */
+ return (EADDRNOTAVAIL);
+ if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))
+ return(EINVAL);
+ if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
+ wild = 1;
+ if (nam) {
+ sin6 = (struct sockaddr_in6 *)nam;
+ if (nam->sa_len != sizeof(*sin6))
+ return(EINVAL);
+ /*
+ * family check.
+ */
+ if (nam->sa_family != AF_INET6)
+ return(EAFNOSUPPORT);
+
+ /*
+ * If the scope of the destination is link-local, embed the
+ * interface index in the address.
+ */
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) {
+ /* XXX boundary check is assumed to be already done. */
+ /* XXX sin6_scope_id is weaker than advanced-api. */
+ struct in6_pktinfo *pi;
+ if (inp->in6p_outputopts &&
+ (pi = inp->in6p_outputopts->ip6po_pktinfo) &&
+ pi->ipi6_ifindex) {
+ sin6->sin6_addr.s6_addr16[1]
+ = htons(pi->ipi6_ifindex);
+ } else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)
+ && inp->in6p_moptions
+ && inp->in6p_moptions->im6o_multicast_ifp) {
+ sin6->sin6_addr.s6_addr16[1] =
+ htons(inp->in6p_moptions->im6o_multicast_ifp->if_index);
+ } else if (sin6->sin6_scope_id) {
+ /* boundary check */
+ if (sin6->sin6_scope_id < 0
+ || if_index < sin6->sin6_scope_id) {
+ return ENXIO; /* XXX EINVAL? */
+ }
+ sin6->sin6_addr.s6_addr16[1]
+ = htons(sin6->sin6_scope_id & 0xffff);/*XXX*/
+ /* this must be cleared for ifa_ifwithaddr() */
+ sin6->sin6_scope_id = 0;
+ }
+ }
+
+ lport = sin6->sin6_port;
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
+ /*
+ * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
+ * allow compepte duplication of binding if
+ * SO_REUSEPORT is set, or if SO_REUSEADDR is set
+ * and a multicast address is bound on both
+ * new and duplicated sockets.
+ */
+ if (so->so_options & SO_REUSEADDR)
+ reuseport = SO_REUSEADDR|SO_REUSEPORT;
+ } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+ struct ifaddr *ia = NULL;
+
+ sin6->sin6_port = 0; /* yech... */
+ if ((ia = ifa_ifwithaddr((struct sockaddr *)sin6)) == 0)
+ return(EADDRNOTAVAIL);
+
+ /*
+ * XXX: bind to an anycast address might accidentally
+ * cause sending a packet with anycast source address.
+ */
+ if (ia &&
+ ((struct in6_ifaddr *)ia)->ia6_flags &
+ (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|
+ IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) {
+ return(EADDRNOTAVAIL);
+ }
+ }
+ if (lport) {
+ struct inpcb *t;
+
+ /* GROSS */
+ if (ntohs(lport) < IPV6PORT_RESERVED && p &&
+ suser_xxx(0, p, PRISON_ROOT))
+ return(EACCES);
+ if (so->so_cred->cr_uid != 0 &&
+ !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
+ t = in6_pcblookup_local(inp->inp_pcbinfo,
+ &sin6->sin6_addr, lport,
+ INPLOOKUP_WILDCARD);
+ if (t &&
+ (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ||
+ !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) ||
+ (t->inp_socket->so_options &
+ SO_REUSEPORT) == 0) &&
+ (so->so_cred->cr_uid !=
+ t->inp_socket->so_cred->cr_uid))
+ return (EADDRINUSE);
+ }
+ t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr,
+ lport, wild);
+ if (t && (reuseport & t->inp_socket->so_options) == 0)
+ return(EADDRINUSE);
+ }
+ inp->in6p_laddr = sin6->sin6_addr;
+ }
+ if (lport == 0) {
+ ushort first, last;
+ int count;
+
+ inp->inp_flags |= INP_ANONPORT;
+
+ if (inp->inp_flags & INP_HIGHPORT) {
+ first = ipport_hifirstauto; /* sysctl */
+ last = ipport_hilastauto;
+ lastport = &pcbinfo->lasthi;
+ } else if (inp->inp_flags & INP_LOWPORT) {
+ if (p && (error = suser_xxx(0, p, PRISON_ROOT)))
+ return error;
+ first = ipport_lowfirstauto; /* 1023 */
+ last = ipport_lowlastauto; /* 600 */
+ lastport = &pcbinfo->lastlow;
+ } else {
+ first = ipport_firstauto; /* sysctl */
+ last = ipport_lastauto;
+ lastport = &pcbinfo->lastport;
+ }
+ /*
+ * Simple check to ensure all ports are not used up causing
+ * a deadlock here.
+ *
+ * We split the two cases (up and down) so that the direction
+ * is not being tested on each round of the loop.
+ */
+ if (first > last) {
+ /*
+ * counting down
+ */
+ count = first - last;
+
+ do {
+ if (count-- < 0) { /* completely used? */
+ /*
+ * Undo any address bind that may have
+ * occurred above.
+ */
+ inp->in6p_laddr = in6addr_any;
+ return (EAGAIN);
+ }
+ --*lastport;
+ if (*lastport > first || *lastport < last)
+ *lastport = first;
+ lport = htons(*lastport);
+ } while (in6_pcblookup_local(pcbinfo,
+ &inp->in6p_laddr, lport, wild));
+ } else {
+ /*
+ * counting up
+ */
+ count = last - first;
+
+ do {
+ if (count-- < 0) { /* completely used? */
+ /*
+ * Undo any address bind that may have
+ * occurred above.
+ */
+ inp->in6p_laddr = in6addr_any;
+ return (EAGAIN);
+ }
+ ++*lastport;
+ if (*lastport < first || *lastport > last)
+ *lastport = first;
+ lport = htons(*lastport);
+ } while (in6_pcblookup_local(pcbinfo,
+ &inp->in6p_laddr, lport, wild));
+ }
+ }
+ inp->inp_lport = lport;
+ if (in_pcbinshash(inp) != 0) {
+ inp->in6p_laddr = in6addr_any;
+ inp->inp_lport = 0;
+ return (EAGAIN);
+ }
+ inp->in6p_flowinfo = sin6 ? sin6->sin6_flowinfo : 0; /*XXX*/
+ return(0);
+}
+
+/*
+ * Transform old in6_pcbconnect() into an inner subroutine for new
+ * in6_pcbconnect(): Do some validity-checking on the remote
+ * address (in mbuf 'nam') and then determine local host address
+ * (i.e., which interface) to use to access that remote host.
+ *
+ * This preserves definition of in6_pcbconnect(), while supporting a
+ * slightly different version for T/TCP. (This is more than
+ * a bit of a kludge, but cleaning up the internal interfaces would
+ * have forced minor changes in every protocol).
+ */
+
+int
+in6_pcbladdr(inp, nam, plocal_addr6)
+ register struct inpcb *inp;
+ struct sockaddr *nam;
+ struct in6_addr **plocal_addr6;
+{
+ register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
+ struct in6_pktinfo *pi;
+ struct ifnet *ifp = NULL;
+ int error = 0;
+
+ if (nam->sa_len != sizeof (*sin6))
+ return (EINVAL);
+ if (sin6->sin6_family != AF_INET6)
+ return (EAFNOSUPPORT);
+ if (sin6->sin6_port == 0)
+ return (EADDRNOTAVAIL);
+
+ /*
+ * If the scope of the destination is link-local, embed the interface
+ * index in the address.
+ */
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) {
+ /* XXX boundary check is assumed to be already done. */
+ /* XXX sin6_scope_id is weaker than advanced-api. */
+ if (inp->in6p_outputopts &&
+ (pi = inp->in6p_outputopts->ip6po_pktinfo) &&
+ pi->ipi6_ifindex) {
+ sin6->sin6_addr.s6_addr16[1] = htons(pi->ipi6_ifindex);
+ ifp = ifindex2ifnet[pi->ipi6_ifindex];
+ } else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) &&
+ inp->in6p_moptions &&
+ inp->in6p_moptions->im6o_multicast_ifp) {
+ sin6->sin6_addr.s6_addr16[1] =
+ htons(inp->in6p_moptions->im6o_multicast_ifp->if_index);
+ ifp = ifindex2ifnet[inp->in6p_moptions->im6o_multicast_ifp->if_index];
+ } else if (sin6->sin6_scope_id) {
+ /* boundary check */
+ if (sin6->sin6_scope_id < 0
+ || if_index < sin6->sin6_scope_id) {
+ return ENXIO; /* XXX EINVAL? */
+ }
+ sin6->sin6_addr.s6_addr16[1]
+ = htons(sin6->sin6_scope_id & 0xffff);/*XXX*/
+ ifp = ifindex2ifnet[sin6->sin6_scope_id];
+ }
+ }
+
+ if (in6_ifaddr) {
+ /*
+ * If the destination address is UNSPECIFIED addr,
+ * use the loopback addr, e.g ::1.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
+ sin6->sin6_addr = in6addr_loopback;
+ }
+ {
+ /*
+ * XXX: in6_selectsrc might replace the bound local address
+ * with the address specified by setsockopt(IPV6_PKTINFO).
+ * Is it the intended behavior?
+ */
+ *plocal_addr6 = in6_selectsrc(sin6, inp->in6p_outputopts,
+ inp->in6p_moptions,
+ &inp->in6p_route,
+ &inp->in6p_laddr, &error);
+ if (*plocal_addr6 == 0) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ return(error);
+ }
+ /*
+ * Don't do pcblookup call here; return interface in
+ * plocal_addr6
+ * and exit to caller, that will do the lookup.
+ */
+ }
+
+ if (inp->in6p_route.ro_rt)
+ ifp = inp->in6p_route.ro_rt->rt_ifp;
+
+ inp->in6p_ip6_hlim = (u_int8_t)in6_selecthlim(inp, ifp);
+
+ return(0);
+}
+
+/*
+ * Outer subroutine:
+ * Connect from a socket to a specified address.
+ * Both address and port must be specified in argument sin.
+ * If don't have a local address for this socket yet,
+ * then pick one.
+ */
+int
+in6_pcbconnect(inp, nam, p)
+ register struct inpcb *inp;
+ struct sockaddr *nam;
+ struct proc *p;
+{
+ struct in6_addr *addr6;
+ register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
+ int error;
+
+ /*
+ * Call inner routine, to assign local interface address.
+ */
+ if ((error = in6_pcbladdr(inp, nam, &addr6)) != 0)
+ return(error);
+
+ if (in6_pcblookup_hash(inp->inp_pcbinfo, &sin6->sin6_addr,
+ sin6->sin6_port,
+ IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)
+ ? addr6 : &inp->in6p_laddr,
+ inp->inp_lport, 0, NULL) != NULL) {
+ return (EADDRINUSE);
+ }
+ if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) {
+ if (inp->inp_lport == 0) {
+ error = in6_pcbbind(inp, (struct sockaddr *)0, p);
+ if (error)
+ return (error);
+ }
+ inp->in6p_laddr = *addr6;
+ }
+ inp->in6p_faddr = sin6->sin6_addr;
+ inp->inp_fport = sin6->sin6_port;
+ /*
+ * xxx kazu flowlabel is necessary for connect?
+ * but if this line is missing, the garbage value remains.
+ */
+ inp->in6p_flowinfo = sin6->sin6_flowinfo;
+
+ in_pcbrehash(inp);
+ return (0);
+}
+
+/*
+ * Return an IPv6 address, which is the most appropriate for given
+ * destination and user specified options.
+ * If necessary, this function lookups the routing table and return
+ * an entry to the caller for later use.
+ */
+struct in6_addr *
+in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
+ struct sockaddr_in6 *dstsock;
+ struct ip6_pktopts *opts;
+ struct ip6_moptions *mopts;
+ struct route_in6 *ro;
+ struct in6_addr *laddr;
+ int *errorp;
+{
+ struct in6_addr *dst;
+ struct in6_ifaddr *ia6 = 0;
+ struct in6_pktinfo *pi = NULL;
+
+ dst = &dstsock->sin6_addr;
+ *errorp = 0;
+
+ /*
+ * If the source address is explicitly specified by the caller,
+ * use it.
+ */
+ if (opts && (pi = opts->ip6po_pktinfo) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr))
+ return(&pi->ipi6_addr);
+
+ /*
+ * If the source address is not specified but the socket(if any)
+ * is already bound, use the bound address.
+ */
+ if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr))
+ return(laddr);
+
+ /*
+ * If the caller doesn't specify the source address but
+ * the outgoing interface, use an address associated with
+ * the interface.
+ */
+ if (pi && pi->ipi6_ifindex) {
+ /* XXX boundary check is assumed to be already done. */
+ ia6 = in6_ifawithscope(ifindex2ifnet[pi->ipi6_ifindex],
+ dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ /*
+ * If the destination address is a link-local unicast address or
+ * a multicast address, and if the outgoing interface is specified
+ * by the sin6_scope_id filed, use an address associated with the
+ * interface.
+ * XXX: We're now trying to define more specific semantics of
+ * sin6_scope_id field, so this part will be rewritten in
+ * the near future.
+ */
+ if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MULTICAST(dst)) &&
+ dstsock->sin6_scope_id) {
+ /*
+ * I'm not sure if boundary check for scope_id is done
+ * somewhere...
+ */
+ if (dstsock->sin6_scope_id < 0 ||
+ if_index < dstsock->sin6_scope_id) {
+ *errorp = ENXIO; /* XXX: better error? */
+ return(0);
+ }
+ ia6 = in6_ifawithscope(ifindex2ifnet[dstsock->sin6_scope_id],
+ dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ /*
+ * If the destination address is a multicast address and
+ * the outgoing interface for the address is specified
+ * by the caller, use an address associated with the interface.
+ * There is a sanity check here; if the destination has node-local
+ * scope, the outgoing interfacde should be a loopback address.
+ * Even if the outgoing interface is not specified, we also
+ * choose a loopback interface as the outgoing interface.
+ */
+ if (IN6_IS_ADDR_MULTICAST(dst)) {
+ struct ifnet *ifp = mopts ? mopts->im6o_multicast_ifp : NULL;
+
+ if (ifp == NULL && IN6_IS_ADDR_MC_NODELOCAL(dst)) {
+ ifp = &loif[0];
+ }
+
+ if (ifp) {
+ ia6 = in6_ifawithscope(ifp, dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&ia6->ia_addr.sin6_addr);
+ }
+ }
+
+ /*
+ * If the next hop address for the packet is specified
+ * by caller, use an address associated with the route
+ * to the next hop.
+ */
+ {
+ struct sockaddr_in6 *sin6_next;
+ struct rtentry *rt;
+
+ if (opts && opts->ip6po_nexthop) {
+ sin6_next = satosin6(opts->ip6po_nexthop);
+ rt = nd6_lookup(&sin6_next->sin6_addr, 1, NULL);
+ if (rt) {
+ ia6 = in6_ifawithscope(rt->rt_ifp, dst);
+ if (ia6 == 0)
+ ia6 = ifatoia6(rt->rt_ifa);
+ }
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+ }
+
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+ if (ro) {
+ if (ro->ro_rt &&
+ !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr, dst)) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if (ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0) {
+ /* No route yet, so try to acquire one */
+ bzero(&ro->ro_dst, sizeof(struct sockaddr_in6));
+ ro->ro_dst.sin6_family = AF_INET6;
+ ro->ro_dst.sin6_len = sizeof(struct sockaddr_in6);
+ ro->ro_dst.sin6_addr = *dst;
+ if (IN6_IS_ADDR_MULTICAST(dst)) {
+ ro->ro_rt = rtalloc1(&((struct route *)ro)
+ ->ro_dst, 0, 0UL);
+ } else {
+ rtcalloc((struct route *)ro);
+ }
+ }
+
+ /*
+ * in_pcbconnect() checks out IFF_LOOPBACK to skip using
+ * the address. But we don't know why it does so.
+ * It is necessary to ensure the scope even for lo0
+ * so doesn't check out IFF_LOOPBACK.
+ */
+
+ if (ro->ro_rt) {
+ ia6 = in6_ifawithscope(ro->ro_rt->rt_ifa->ifa_ifp, dst);
+ if (ia6 == 0) /* xxx scope error ?*/
+ ia6 = ifatoia6(ro->ro_rt->rt_ifa);
+ }
+ if (ia6 == 0) {
+ *errorp = EHOSTUNREACH; /* no route */
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+}
+
+/*
+ * Default hop limit selection. The precedence is as follows:
+ * 1. Hoplimit valued specified via ioctl.
+ * 2. (If the outgoing interface is detected) the current
+ * hop limit of the interface specified by router advertisement.
+ * 3. The system default hoplimit.
+*/
+int
+in6_selecthlim(in6p, ifp)
+ struct in6pcb *in6p;
+ struct ifnet *ifp;
+{
+ if (in6p && in6p->in6p_hops >= 0)
+ return(in6p->in6p_hops);
+ else if (ifp)
+ return(nd_ifinfo[ifp->if_index].chlim);
+ else
+ return(ip6_defhlim);
+}
+
+void
+in6_pcbdisconnect(inp)
+ struct inpcb *inp;
+{
+ bzero((caddr_t)&inp->in6p_faddr, sizeof(inp->in6p_faddr));
+ inp->inp_fport = 0;
+ in_pcbrehash(inp);
+ if (inp->inp_socket->so_state & SS_NOFDREF)
+ in6_pcbdetach(inp);
+}
+
+void
+in6_pcbdetach(inp)
+ struct inpcb *inp;
+{
+ struct socket *so = inp->inp_socket;
+ struct inpcbinfo *ipi = inp->inp_pcbinfo;
+
+#ifdef IPSEC
+ if (sotoinpcb(so) != 0)
+ key_freeso(so);
+ ipsec6_delete_pcbpolicy(inp);
+#endif /* IPSEC */
+ inp->inp_gencnt = ++ipi->ipi_gencnt;
+ in_pcbremlists(inp);
+ sotoinpcb(so) = 0;
+ sofree(so);
+ if (inp->in6p_options)
+ m_freem(inp->in6p_options);
+ if (inp->in6p_outputopts) {
+ if (inp->in6p_outputopts->ip6po_rthdr &&
+ inp->in6p_outputopts->ip6po_route.ro_rt)
+ RTFREE(inp->in6p_outputopts->ip6po_route.ro_rt);
+ if (inp->in6p_outputopts->ip6po_m)
+ (void)m_free(inp->in6p_outputopts->ip6po_m);
+ free(inp->in6p_outputopts, M_IP6OPT);
+ }
+ if (inp->in6p_route.ro_rt)
+ rtfree(inp->in6p_route.ro_rt);
+ ip6_freemoptions(inp->in6p_moptions);
+ inp->inp_vflag = 0;
+ zfreei(ipi->ipi_zone, inp);
+}
+
+/*
+ * The calling convention of in6_setsockaddr() and in6_setpeeraddr() was
+ * modified to match the pru_sockaddr() and pru_peeraddr() entry points
+ * in struct pr_usrreqs, so that protocols can just reference then directly
+ * without the need for a wrapper function. The socket must have a valid
+ * (i.e., non-nil) PCB, but it should be impossible to get an invalid one
+ * except through a kernel programming error, so it is acceptable to panic
+ * (or in this case trap) if the PCB is invalid. (Actually, we don't trap
+ * because there actually /is/ a programming error somewhere... XXX)
+ */
+int
+in6_setsockaddr(so, nam)
+ struct socket *so;
+ struct sockaddr **nam;
+{
+ int s;
+ register struct inpcb *inp;
+ register struct sockaddr_in6 *sin6;
+
+ /*
+ * Do the malloc first in case it blocks.
+ */
+ MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME, M_WAITOK);
+ bzero(sin6, sizeof *sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(*sin6);
+
+ s = splnet();
+ inp = sotoinpcb(so);
+ if (!inp) {
+ splx(s);
+ free(sin6, M_SONAME);
+ return EINVAL;
+ }
+ sin6->sin6_port = inp->inp_lport;
+ sin6->sin6_addr = inp->in6p_laddr;
+ splx(s);
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+ sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]);
+ else
+ sin6->sin6_scope_id = 0; /*XXX*/
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+ sin6->sin6_addr.s6_addr16[1] = 0;
+
+ *nam = (struct sockaddr *)sin6;
+ return 0;
+}
+
+int
+in6_setpeeraddr(so, nam)
+ struct socket *so;
+ struct sockaddr **nam;
+{
+ int s;
+ struct inpcb *inp;
+ register struct sockaddr_in6 *sin6;
+
+ /*
+ * Do the malloc first in case it blocks.
+ */
+ MALLOC(sin6, struct sockaddr_in6 *, sizeof(*sin6), M_SONAME, M_WAITOK);
+ bzero((caddr_t)sin6, sizeof (*sin6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+
+ s = splnet();
+ inp = sotoinpcb(so);
+ if (!inp) {
+ splx(s);
+ free(sin6, M_SONAME);
+ return EINVAL;
+ }
+ sin6->sin6_port = inp->inp_fport;
+ sin6->sin6_addr = inp->in6p_faddr;
+ splx(s);
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+ sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]);
+ else
+ sin6->sin6_scope_id = 0; /*XXX*/
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+ sin6->sin6_addr.s6_addr16[1] = 0;
+
+ *nam = (struct sockaddr *)sin6;
+ return 0;
+}
+
+int
+in6_mapped_sockaddr(struct socket *so, struct sockaddr **nam)
+{
+ struct inpcb *inp = sotoinpcb(so);
+ int error;
+
+ if (inp == NULL)
+ return EINVAL;
+ if (inp->inp_vflag & INP_IPV4) {
+ error = in_setsockaddr(so, nam);
+ if (error == NULL)
+ in6_sin_2_v4mapsin6_in_sock(nam);
+ } else
+ error = in6_setsockaddr(so, nam);
+
+ return error;
+}
+
+int
+in6_mapped_peeraddr(struct socket *so, struct sockaddr **nam)
+{
+ struct inpcb *inp = sotoinpcb(so);
+ int error;
+
+ if (inp == NULL)
+ return EINVAL;
+ if (inp->inp_vflag & INP_IPV4) {
+ error = in_setpeeraddr(so, nam);
+ if (error == NULL)
+ in6_sin_2_v4mapsin6_in_sock(nam);
+ } else
+ error = in6_setpeeraddr(so, nam);
+
+ return error;
+}
+
+/*
+ * Pass some notification to all connections of a protocol
+ * associated with address dst. The local address and/or port numbers
+ * may be specified to limit the search. The "usual action" will be
+ * taken, depending on the ctlinput cmd. The caller must filter any
+ * cmds that are uninteresting (e.g., no error in the map).
+ * Call the protocol specific routine (if any) to report
+ * any errors for each matching socket.
+ *
+ * Must be called at splnet.
+ */
+void
+in6_pcbnotify(head, dst, fport_arg, laddr6, lport_arg, cmd, notify)
+ struct inpcbhead *head;
+ struct sockaddr *dst;
+ u_int fport_arg, lport_arg;
+ struct in6_addr *laddr6;
+ int cmd;
+ void (*notify) __P((struct inpcb *, int));
+{
+ struct inpcb *inp, *oinp;
+ struct in6_addr faddr6;
+ u_short fport = fport_arg, lport = lport_arg;
+ int errno, s;
+
+ if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET6)
+ return;
+ faddr6 = ((struct sockaddr_in6 *)dst)->sin6_addr;
+ if (IN6_IS_ADDR_UNSPECIFIED(&faddr6))
+ return;
+
+ /*
+ * Redirects go to all references to the destination,
+ * and use in_rtchange to invalidate the route cache.
+ * Dead host indications: notify all references to the destination.
+ * Otherwise, if we have knowledge of the local port and address,
+ * deliver only to that socket.
+ */
+ if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
+ fport = 0;
+ lport = 0;
+ bzero((caddr_t)laddr6, sizeof(*laddr6));
+ if (cmd != PRC_HOSTDEAD)
+ notify = in6_rtchange;
+ }
+ errno = inet6ctlerrmap[cmd];
+ s = splnet();
+ for (inp = LIST_FIRST(head); inp != NULL;) {
+ if ((inp->inp_vflag & INP_IPV6) == NULL) {
+ inp = LIST_NEXT(inp, inp_list);
+ continue;
+ }
+ if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, &faddr6) ||
+ inp->inp_socket == 0 ||
+ (lport && inp->inp_lport != lport) ||
+ (!IN6_IS_ADDR_UNSPECIFIED(laddr6) &&
+ !IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr6)) ||
+ (fport && inp->inp_fport != fport)) {
+ inp = LIST_NEXT(inp, inp_list);
+ continue;
+ }
+ oinp = inp;
+ inp = LIST_NEXT(inp, inp_list);
+ if (notify)
+ (*notify)(oinp, errno);
+ }
+ splx(s);
+}
+
+/*
+ * Lookup a PCB based on the local address and port.
+ */
+struct inpcb *
+in6_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay)
+ struct inpcbinfo *pcbinfo;
+ struct in6_addr *laddr;
+ u_int lport_arg;
+ int wild_okay;
+{
+ register struct inpcb *inp;
+ int matchwild = 3, wildcard;
+ u_short lport = lport_arg;
+
+ if (!wild_okay) {
+ struct inpcbhead *head;
+ /*
+ * Look for an unconnected (wildcard foreign addr) PCB that
+ * matches the local address and port we're looking for.
+ */
+ head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0,
+ pcbinfo->hashmask)];
+ LIST_FOREACH(inp, head, inp_hash) {
+ if ((inp->inp_vflag & INP_IPV6) == NULL)
+ continue;
+ if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) &&
+ IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr) &&
+ inp->inp_lport == lport) {
+ /*
+ * Found.
+ */
+ return (inp);
+ }
+ }
+ /*
+ * Not found.
+ */
+ return (NULL);
+ } else {
+ struct inpcbporthead *porthash;
+ struct inpcbport *phd;
+ struct inpcb *match = NULL;
+ /*
+ * Best fit PCB lookup.
+ *
+ * First see if this local port is in use by looking on the
+ * port hash list.
+ */
+ porthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(lport,
+ pcbinfo->porthashmask)];
+ LIST_FOREACH(phd, porthash, phd_hash) {
+ if (phd->phd_port == lport)
+ break;
+ }
+ if (phd != NULL) {
+ /*
+ * Port is in use by one or more PCBs. Look for best
+ * fit.
+ */
+ LIST_FOREACH(inp, &phd->phd_pcblist, inp_portlist) {
+ wildcard = 0;
+ if ((inp->inp_vflag & INP_IPV6) == NULL)
+ continue;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
+ wildcard++;
+ if (!IN6_IS_ADDR_UNSPECIFIED(
+ &inp->in6p_laddr)) {
+ if (IN6_IS_ADDR_UNSPECIFIED(laddr))
+ wildcard++;
+ else if (!IN6_ARE_ADDR_EQUAL(
+ &inp->in6p_laddr, laddr))
+ continue;
+ } else {
+ if (!IN6_IS_ADDR_UNSPECIFIED(laddr))
+ wildcard++;
+ }
+ if (wildcard < matchwild) {
+ match = inp;
+ matchwild = wildcard;
+ if (matchwild == 0) {
+ break;
+ }
+ }
+ }
+ }
+ return (match);
+ }
+}
+
+/*
+ * Check for alternatives when higher level complains
+ * about service problems. For now, invalidate cached
+ * routing information. If the route was created dynamically
+ * (by a redirect), time to try a default gateway again.
+ */
+void
+in6_losing(in6p)
+ struct inpcb *in6p;
+{
+ struct rtentry *rt;
+ struct rt_addrinfo info;
+
+ if ((rt = in6p->in6p_route.ro_rt) != NULL) {
+ in6p->in6p_route.ro_rt = 0;
+ bzero((caddr_t)&info, sizeof(info));
+ info.rti_info[RTAX_DST] =
+ (struct sockaddr *)&in6p->in6p_route.ro_dst;
+ info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
+ info.rti_info[RTAX_NETMASK] = rt_mask(rt);
+ rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
+ if (rt->rt_flags & RTF_DYNAMIC)
+ (void)rtrequest(RTM_DELETE, rt_key(rt),
+ rt->rt_gateway, rt_mask(rt), rt->rt_flags,
+ (struct rtentry **)0);
+ else
+ /*
+ * A new route can be allocated
+ * the next time output is attempted.
+ */
+ rtfree(rt);
+ }
+}
+
+/*
+ * After a routing change, flush old routing
+ * and allocate a (hopefully) better one.
+ */
+void
+in6_rtchange(inp, errno)
+ struct inpcb *inp;
+ int errno;
+{
+ if (inp->in6p_route.ro_rt) {
+ rtfree(inp->in6p_route.ro_rt);
+ inp->in6p_route.ro_rt = 0;
+ /*
+ * A new route can be allocated the next time
+ * output is attempted.
+ */
+ }
+}
+
+/*
+ * Lookup PCB in hash list.
+ */
+struct inpcb *
+in6_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard, ifp)
+ struct inpcbinfo *pcbinfo;
+ struct in6_addr *faddr, *laddr;
+ u_int fport_arg, lport_arg;
+ int wildcard;
+ struct ifnet *ifp;
+{
+ struct inpcbhead *head;
+ register struct inpcb *inp;
+ u_short fport = fport_arg, lport = lport_arg;
+
+ /*
+ * First look for an exact match.
+ */
+ head = &pcbinfo->hashbase[INP_PCBHASH(faddr->s6_addr32[3] /* XXX */,
+ lport, fport,
+ pcbinfo->hashmask)];
+ for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
+ if ((inp->inp_vflag & INP_IPV6) == NULL)
+ continue;
+ if (IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, faddr) &&
+ IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr) &&
+ inp->inp_fport == fport &&
+ inp->inp_lport == lport) {
+ /*
+ * Found.
+ */
+ return (inp);
+ }
+ }
+ if (wildcard) {
+ struct inpcb *local_wild = NULL;
+
+ head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0,
+ pcbinfo->hashmask)];
+ for (inp = head->lh_first; inp != NULL;
+ inp = inp->inp_hash.le_next) {
+ if ((inp->inp_vflag & INP_IPV6) == NULL)
+ continue;
+ if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) &&
+ inp->inp_lport == lport) {
+#if defined(NFAITH) && NFAITH > 0
+ if (ifp && ifp->if_type == IFT_FAITH &&
+ (inp->inp_flags & INP_FAITH) == 0)
+ continue;
+#endif
+ if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr,
+ laddr))
+ return (inp);
+ else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))
+ local_wild = inp;
+ }
+ }
+ return (local_wild);
+ }
+
+ /*
+ * Not found.
+ */
+ return (NULL);
+}
+
+void
+init_sin6(struct sockaddr_in6 *sin6, struct mbuf *m)
+{
+ struct ip6_hdr *ip;
+
+ ip = mtod(m, struct ip6_hdr *);
+ bzero(sin6, sizeof(*sin6));
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = ip->ip6_src;
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+ sin6->sin6_addr.s6_addr16[1] = 0;
+ sin6->sin6_scope_id =
+ (m->m_pkthdr.rcvif && IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+ ? m->m_pkthdr.rcvif->if_index : 0;
+
+ return;
+}
diff --git a/sys/netinet6/in6_pcb.h b/sys/netinet6/in6_pcb.h
new file mode 100644
index 0000000..d962b21
--- /dev/null
+++ b/sys/netinet6/in6_pcb.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_pcb.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _NETINET6_IN6_PCB_H_
+#define _NETINET6_IN6_PCB_H_
+
+#ifdef KERNEL
+#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
+#define sin6tosa(sin6) ((struct sockaddr *)(sin6))
+#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa))
+
+void in6_losing __P((struct inpcb *));
+int in6_pcballoc __P((struct socket *, struct inpcbinfo *, struct proc *));
+int in6_pcbbind __P((struct inpcb *, struct sockaddr *, struct proc *));
+int in6_pcbconnect __P((struct inpcb *, struct sockaddr *, struct proc *));
+void in6_pcbdetach __P((struct inpcb *));
+void in6_pcbdisconnect __P((struct inpcb *));
+int in6_pcbladdr __P((struct inpcb *, struct sockaddr *,
+ struct in6_addr **));
+struct inpcb *
+ in6_pcblookup_local __P((struct inpcbinfo *,
+ struct in6_addr *, u_int, int));
+struct inpcb *
+ in6_pcblookup_hash __P((struct inpcbinfo *,
+ struct in6_addr *, u_int, struct in6_addr *,
+ u_int, int, struct ifnet *));
+void in6_pcbnotify __P((struct inpcbhead *, struct sockaddr *,
+ u_int, struct in6_addr *, u_int, int,
+ void (*)(struct inpcb *, int)));
+void in6_rtchange __P((struct inpcb *, int));
+int in6_setpeeraddr __P((struct socket *so, struct sockaddr **nam));
+int in6_setsockaddr __P((struct socket *so, struct sockaddr **nam));
+int in6_mapped_sockaddr __P((struct socket *so, struct sockaddr **nam));
+int in6_mapped_peeraddr __P((struct socket *so, struct sockaddr **nam));
+struct in6_addr *in6_selectsrc __P((struct sockaddr_in6 *,
+ struct ip6_pktopts *,
+ struct ip6_moptions *,
+ struct route_in6 *,
+ struct in6_addr *, int *));
+int in6_selecthlim __P((struct inpcb *, struct ifnet *));
+
+void init_sin6 __P((struct sockaddr_in6 *sin6, struct mbuf *m));
+#endif /* KERNEL */
+
+#endif /* !_NETINET6_IN6_PCB_H_ */
diff --git a/sys/netinet6/in6_prefix.c b/sys/netinet6/in6_prefix.c
new file mode 100644
index 0000000..8da07c4
--- /dev/null
+++ b/sys/netinet6/in6_prefix.c
@@ -0,0 +1,1106 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in.c 8.2 (Berkeley) 11/15/93
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sockio.h>
+#include <sys/systm.h>
+#include <sys/syslog.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/in6_prefix.h>
+#include <netinet6/ip6_var.h>
+
+static MALLOC_DEFINE(M_IP6RR, "ip6rr", "IPv6 Router Renumbering Prefix");
+static MALLOC_DEFINE(M_RR_ADDR, "rp_addr", "IPv6 Router Renumbering Ifid");
+
+struct rr_prhead rr_prefix;
+
+#include <net/net_osdep.h>
+
+static int create_ra_entry __P((struct rp_addr **rapp));
+static int add_each_prefix __P((struct socket *so,
+ struct rr_prefix *rpp));
+static void free_rp_entries __P((struct rr_prefix *rpp));
+static int link_stray_ia6s __P((struct rr_prefix *rpp));
+
+/*
+ * Copy bits from src to tgt, from off bit for len bits.
+ * Caller must specify collect tgtsize and srcsize.
+ */
+static void
+bit_copy(char *tgt, u_int tgtsize, char *src, u_int srcsize,
+ u_int off, u_int len)
+{
+ char *sp, *tp;
+
+ /* arg values check */
+ if (srcsize < off || srcsize < (off + len) ||
+ tgtsize < off || tgtsize < (off + len)) {
+ log(LOG_ERR,
+ "in6_prefix.c: bit_copy: invalid args: srcsize %d,\n"
+ "tgtsize %d, off %d, len %d\n", srcsize, tgtsize, off,
+ len);
+ return;
+ }
+
+ /* search start point */
+ for (sp = src, tp = tgt; off >= 8; sp++, tp++)
+ off-=8;
+ /* copy starting bits */
+ if (off) {
+ char setbit;
+ int startbits;
+
+ startbits = min((8 - off), len);
+
+ for (setbit = (0x80 >> off); startbits;
+ setbit >>= 1, startbits--, len--)
+ *tp |= (setbit & *sp);
+ tp++;
+ sp++;
+ }
+ /* copy midium bits */
+ for (; len >= 8; sp++, tp++) {
+ *tp = *sp;
+ len-=8;
+ }
+ /* copy ending bits */
+ if (len) {
+ char setbit;
+
+ for (setbit = 0x80; len; setbit >>= 1, len--)
+ *tp |= (setbit & *sp);
+ }
+}
+
+static struct ifprefix *
+in6_prefixwithifp(struct ifnet *ifp, int plen, struct in6_addr *dst)
+{
+ struct ifprefix *ifpr;
+
+ /* search matched prefix */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list)) {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (plen <= in6_matchlen(dst, IFPR_IN6(ifpr)))
+ break;
+ }
+ return (ifpr);
+}
+
+/*
+ * Search prefix which matches arg prefix as specified in
+ * draft-ietf-ipngwg-router-renum-08.txt
+ */
+static struct rr_prefix *
+search_matched_prefix(struct ifnet *ifp, struct in6_prefixreq *ipr)
+{
+ struct ifprefix *ifpr;
+ struct ifaddr *ifa;
+ struct rr_prefix *rpp;
+
+ /* search matched prefix */
+ ifpr = in6_prefixwithifp(ifp, ipr->ipr_plen,
+ &ipr->ipr_prefix.sin6_addr);
+ if (ifpr != NULL)
+ return ifpr2rp(ifpr);
+
+ /*
+ * search matched addr, and then search prefix
+ * which matches the addr
+ */
+
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (ipr->ipr_plen <=
+ in6_matchlen(&ipr->ipr_prefix.sin6_addr, IFA_IN6(ifa)))
+ break;
+ }
+ if (ifa == NULL)
+ return NULL;
+
+ rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
+ if (rpp != 0)
+ return rpp;
+
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list)) {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (ifpr->ifpr_plen <= in6_matchlen(IFA_IN6(ifa),
+ IFPR_IN6(ifpr)))
+ break;
+ }
+ if (ifpr != NULL)
+ log(LOG_ERR, "in6_prefix.c: search_matched_prefix: addr %s"
+ "has no pointer to prefix %s", ip6_sprintf(IFA_IN6(ifa)),
+ ip6_sprintf(IFPR_IN6(ifpr)));
+ return ifpr2rp(ifpr);
+}
+
+/*
+ * Search prefix which matches arg prefix as specified in
+ * draft-ietf-ipngwg-router-renum-08.txt, and mark it if exists.
+ * Return 1 if anything matched, and 0 if nothing matched.
+ */
+static int
+mark_matched_prefixes(u_long cmd, struct ifnet *ifp, struct in6_rrenumreq *irr)
+{
+ struct ifprefix *ifpr;
+ struct ifaddr *ifa;
+ int matchlen, matched = 0;
+
+ /* search matched prefixes */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list)) {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr,
+ IFPR_IN6(ifpr));
+ if (irr->irr_m_minlen > ifpr->ifpr_plen ||
+ irr->irr_m_maxlen < ifpr->ifpr_plen ||
+ irr->irr_m_len > matchlen)
+ continue;
+ matched = 1;
+ ifpr2rp(ifpr)->rp_statef_addmark = 1;
+ if (cmd == SIOCCIFPREFIX_IN6)
+ ifpr2rp(ifpr)->rp_statef_delmark = 1;
+ }
+
+ /*
+ * search matched addr, and then search prefixes
+ * which matche the addr
+ */
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ struct rr_prefix *rpp;
+
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr,
+ IFA_IN6(ifa));
+ if (irr->irr_m_minlen > matchlen ||
+ irr->irr_m_maxlen < matchlen || irr->irr_m_len > matchlen)
+ continue;
+ rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
+ if (rpp != 0) {
+ matched = 1;
+ rpp->rp_statef_addmark = 1;
+ if (cmd == SIOCCIFPREFIX_IN6)
+ rpp->rp_statef_delmark = 1;
+ } else
+ log(LOG_WARNING, "in6_prefix.c: mark_matched_prefixes:"
+ "no back pointer to ifprefix for %s. "
+ "ND autoconfigured addr?",
+ ip6_sprintf(IFA_IN6(ifa)));
+ }
+ return matched;
+}
+
+/*
+ * Mark global prefixes as to be deleted.
+ */
+static void
+delmark_global_prefixes(struct ifnet *ifp, struct in6_rrenumreq *irr)
+{
+ struct ifprefix *ifpr;
+
+ /* search matched prefixes */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list)) {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ /* mark delete global prefix */
+ if (in6_addrscope(RP_IN6(ifpr2rp(ifpr))) ==
+ IPV6_ADDR_SCOPE_GLOBAL)
+ ifpr2rp(ifpr)->rp_statef_delmark = 1;
+ }
+}
+
+/* Unmark prefixes */
+static void
+unmark_prefixes(struct ifnet *ifp)
+{
+ struct ifprefix *ifpr;
+
+ /* unmark all prefix */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list)) {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ /* unmark prefix */
+ ifpr2rp(ifpr)->rp_statef_addmark = 0;
+ ifpr2rp(ifpr)->rp_statef_delmark = 0;
+ }
+}
+
+static void
+init_prefix_ltimes(struct rr_prefix *rpp)
+{
+ if (rpp->rp_pltime == RR_INFINITE_LIFETIME ||
+ rpp->rp_rrf_decrprefd == 0)
+ rpp->rp_preferred = 0;
+ else
+ rpp->rp_preferred = time_second + rpp->rp_pltime;
+ if (rpp->rp_vltime == RR_INFINITE_LIFETIME ||
+ rpp->rp_rrf_decrvalid == 0)
+ rpp->rp_expire = 0;
+ else
+ rpp->rp_expire = time_second + rpp->rp_vltime;
+}
+
+static int
+rr_are_ifid_equal(struct in6_addr *ii1, struct in6_addr *ii2, int ii_len)
+{
+ int ii_bytelen, ii_bitlen;
+ int p_bytelen, p_bitlen;
+
+ /* sanity check */
+ if (1 > ii_len ||
+ ii_len > 124) { /* as RFC2373, prefix is at least 4 bit */
+ log(LOG_ERR, "rr_are_ifid_equal: invalid ifid length(%d)\n",
+ ii_len);
+ return(0);
+ }
+
+ ii_bytelen = ii_len / 8;
+ ii_bitlen = ii_len % 8;
+
+ p_bytelen = sizeof(struct in6_addr) - ii_bytelen - 1;
+ p_bitlen = 8 - ii_bitlen;
+
+ if (bcmp(ii1->s6_addr + p_bytelen + 1, ii2->s6_addr + p_bytelen + 1,
+ ii_bytelen))
+ return(0);
+ if (((ii1->s6_addr[p_bytelen] << p_bitlen) & 0xff) !=
+ ((ii2->s6_addr[p_bytelen] << p_bitlen) & 0xff))
+ return(0);
+
+ return(1);
+}
+
+static struct rp_addr *
+search_ifidwithprefix(struct rr_prefix *rpp, struct in6_addr *ifid)
+{
+ struct rp_addr *rap;
+
+ for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
+ rap = rap->ra_entry.le_next)
+ if (rr_are_ifid_equal(ifid, &rap->ra_ifid,
+ (sizeof(struct in6_addr) << 3) -
+ rpp->rp_plen))
+ break;
+ return rap;
+}
+
+static int
+assigne_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia)
+{
+ int error = 0;
+ struct rp_addr *rap;
+ int s;
+
+ if ((error = create_ra_entry(&rap)) != 0)
+ return error;
+
+ /* copy interface id part */
+ bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3,
+ (caddr_t)IA6_IN6(ia),
+ sizeof(*IA6_IN6(ia)) << 3, rpp->rp_plen, iilen);
+ /* link to ia, and put into list */
+ rap->ra_addr = ia;
+ /*
+ * Can't point rp2ifpr(rpp) from ia->ia6_ifpr now,
+ * because rpp may be on th stack. should fix it?
+ */
+ s = splnet();
+ LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
+ splx(s);
+
+ return 0;
+}
+
+int
+in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia)
+{
+ int plen = (sizeof(*IA6_IN6(ia)) << 3) - iilen;
+ struct ifprefix *ifpr;
+ struct rp_addr *rap;
+ int error = 0;
+
+ ifpr = in6_prefixwithifp(ia->ia_ifp, plen, IA6_IN6(ia));
+ if (ifpr == NULL) {
+ struct rr_prefix rp;
+ struct socket so;
+ int pplen = (plen == 128) ? 64 : plen;
+
+ /* allocate a prefix for ia, with default properties */
+
+ /* init rp */
+ bzero(&rp, sizeof(rp));
+ rp.rp_type = IN6_PREFIX_RR;
+ rp.rp_ifp = ia->ia_ifp;
+ rp.rp_plen = pplen;
+ rp.rp_prefix.sin6_len = sizeof(rp.rp_prefix);
+ rp.rp_prefix.sin6_family = AF_INET6;
+ bit_copy((char *)RP_IN6(&rp), sizeof(*RP_IN6(&rp)) << 3,
+ (char *)&ia->ia_addr.sin6_addr,
+ sizeof(ia->ia_addr.sin6_addr) << 3,
+ 0, pplen);
+ rp.rp_vltime = rp.rp_pltime = RR_INFINITE_LIFETIME;
+ rp.rp_raf_onlink = 1;
+ rp.rp_raf_auto = 1;
+ /* Is some FlagMasks for rrf necessary? */
+ rp.rp_rrf_decrvalid = rp.rp_rrf_decrprefd = 0;
+ rp.rp_origin = PR_ORIG_RR; /* can be renumbered */
+
+ /* create ra_entry */
+ error = link_stray_ia6s(&rp);
+ if (error != 0) {
+ free_rp_entries(&rp);
+ return error;
+ }
+
+ /* XXX: init dummy so */
+ bzero(&so, sizeof(so));
+
+ error = add_each_prefix(&so, &rp);
+
+ /* free each rp_addr entry */
+ free_rp_entries(&rp);
+
+ if (error != 0)
+ return error;
+
+ /* search again */
+ ifpr = in6_prefixwithifp(ia->ia_ifp, pplen, IA6_IN6(ia));
+ if (ifpr == NULL)
+ return 0;
+ }
+ rap = search_ifidwithprefix(ifpr2rp(ifpr), IA6_IN6(ia));
+ if (rap != NULL) {
+ if (rap->ra_addr == NULL)
+ rap->ra_addr = ia;
+ else if (rap->ra_addr != ia) {
+ /* There may be some inconsistencies between addrs. */
+ log(LOG_ERR, "ip6_prefix.c: addr %s/%d matched prefix"
+ "has already another ia %p(%s) on its ifid list",
+ ip6_sprintf(IA6_IN6(ia)), plen,
+ rap->ra_addr,
+ ip6_sprintf(IA6_IN6(rap->ra_addr)));
+ return EADDRINUSE /* XXX */;
+ }
+ ia->ia6_ifpr = ifpr;
+ return 0;
+ }
+ error = assigne_ra_entry(ifpr2rp(ifpr), iilen, ia);
+ if (error == 0)
+ ia->ia6_ifpr = ifpr;
+ return (error);
+}
+
+void
+in6_prefix_remove_ifid(int iilen, struct in6_ifaddr *ia)
+{
+ struct rp_addr *rap;
+
+ if (ia->ia6_ifpr == NULL)
+ return;
+ rap = search_ifidwithprefix(ifpr2rp(ia->ia6_ifpr), IA6_IN6(ia));
+ if (rap != NULL) {
+ int s = splnet();
+ LIST_REMOVE(rap, ra_entry);
+ splx(s);
+ free(rap, M_RR_ADDR);
+ }
+}
+
+static void
+add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap)
+{
+ struct in6_ifaddr *ia6;
+ struct in6_aliasreq ifra;
+ int error;
+
+ /* init ifra */
+ bzero(&ifra, sizeof(ifra));
+ strncpy(ifra.ifra_name, if_name(rpp->rp_ifp), sizeof(ifra.ifra_name));
+ ifra.ifra_addr.sin6_family = ifra.ifra_prefixmask.sin6_family =
+ AF_INET6;
+ ifra.ifra_addr.sin6_len = ifra.ifra_prefixmask.sin6_len =
+ sizeof(ifra.ifra_addr);
+ /* copy prefix part */
+ bit_copy((char *)&ifra.ifra_addr.sin6_addr,
+ sizeof(ifra.ifra_addr.sin6_addr) << 3,
+ (char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
+ 0, rpp->rp_plen);
+ /* copy interface id part */
+ bit_copy((char *)&ifra.ifra_addr.sin6_addr,
+ sizeof(ifra.ifra_addr.sin6_addr) << 3,
+ (char *)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3,
+ rpp->rp_plen, (sizeof(rap->ra_ifid) << 3) - rpp->rp_plen);
+ in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, rpp->rp_plen);
+ /* don't care ifra_flags for now */
+
+ ia6 = in6ifa_ifpwithaddr(rpp->rp_ifp, &ifra.ifra_addr.sin6_addr);
+ if (ia6 != NULL) {
+ if (ia6->ia6_ifpr == NULL) {
+ /* link this addr and the prefix each other */
+ rap->ra_addr = ia6;
+ ia6->ia6_ifpr = rp2ifpr(rpp);
+ return;
+ }
+ if (ia6->ia6_ifpr == rp2ifpr(rpp)) {
+ rap->ra_addr = ia6;
+ return;
+ }
+ /*
+ * The addr is already assigned to other
+ * prefix.
+ * There may be some inconsistencies between
+ * prefixes.
+ * e.g. overraped prefixes with common starting
+ * part and different plefixlen.
+ * Or, completely duplicated prefixes?
+ * log it and return.
+ */
+ log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr"
+ "%s/%d failed because there is already another addr %s/%d",
+ ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen,
+ ip6_sprintf(IA6_IN6(ia6)),
+ in6_mask2len(&ia6->ia_prefixmask.sin6_addr));
+ return;
+ }
+ /* propagate ANYCAST flag if it is set for ancestor addr */
+ if (rap->ra_flags.anycast != 0)
+ ifra.ifra_flags |= IN6_IFF_ANYCAST;
+ error = in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, rpp->rp_ifp,
+ curproc);
+ if (error != 0)
+ log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr"
+ "%s/%d failed because in6_control failed for error %d",
+ ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen,
+ error);
+ return;
+
+ /*
+ * link beween this addr and the prefix will be done
+ * in in6_prefix_add_ifid
+ */
+}
+
+static int
+rrpr_update(struct socket *so, struct rr_prefix *new)
+{
+ struct rr_prefix *rpp;
+ struct ifprefix *ifpr;
+ struct rp_addr *rap;
+ int s;
+
+ /* search existing prefix */
+ for (ifpr = TAILQ_FIRST(&new->rp_ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list)) {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (ifpr->ifpr_plen == new->rp_plen &&
+ in6_are_prefix_equal(IFPR_IN6(ifpr), RP_IN6(new),
+ ifpr->ifpr_plen))
+ break;
+ }
+ rpp = ifpr2rp(ifpr);
+ if (rpp != NULL) {
+ /*
+ * We got a prefix which we have seen in the past.
+ */
+ /*
+ * If the origin of the already-installed prefix is more
+ * preferable than the new one, ignore installation request.
+ */
+ if (rpp->rp_origin > new->rp_origin)
+ return(EPERM);
+
+ /* update prefix information */
+ rpp->rp_flags.prf_ra = new->rp_flags.prf_ra;
+ if (rpp->rp_origin >= PR_ORIG_RR)
+ rpp->rp_flags.prf_rr = new->rp_flags.prf_rr;
+ rpp->rp_vltime = new->rp_vltime;
+ rpp->rp_pltime = new->rp_pltime;
+ rpp->rp_expire = new->rp_expire;
+ rpp->rp_preferred = new->rp_preferred;
+ rpp->rp_statef_delmark = 0; /* cancel deletion */
+ /*
+ * Interface id related update.
+ * add rp_addr entries in new into rpp, if they have not
+ * been already included in rpp.
+ */
+ while (!LIST_EMPTY(&new->rp_addrhead))
+ {
+ rap = LIST_FIRST(&new->rp_addrhead);
+ LIST_REMOVE(rap, ra_entry);
+ if (search_ifidwithprefix(rpp, &rap->ra_ifid)
+ != NULL) {
+ free(rap, M_RR_ADDR);
+ continue;
+ }
+ s = splnet();
+ LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
+ splx(s);
+ }
+ } else {
+ /*
+ * We got a fresh prefix.
+ */
+ /* create new prefix */
+ rpp = (struct rr_prefix *)malloc(sizeof(*rpp), M_IP6RR,
+ M_NOWAIT);
+ if (rpp == NULL) {
+ log(LOG_ERR, "in6_prefix.c: rrpr_update:%d"
+ ": ENOBUFS for rr_prefix", __LINE__);
+ return(ENOBUFS);
+ }
+ /* initilization */
+ *rpp = *new;
+ LIST_INIT(&rpp->rp_addrhead);
+ /* move rp_addr entries of new to rpp */
+ while (!LIST_EMPTY(&new->rp_addrhead))
+ {
+ rap = LIST_FIRST(&new->rp_addrhead);
+ LIST_REMOVE(rap, ra_entry);
+ LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
+ }
+
+ /* let rp_ifpr.ifpr_prefix point rr_prefix. */
+ rpp->rp_ifpr.ifpr_prefix = (struct sockaddr *)&rpp->rp_prefix;
+ /* link rr_prefix entry to if_prefixhead */
+ {
+ struct ifnet *ifp = rpp->rp_ifp;
+ struct ifprefix *ifpr;
+
+ if ((ifpr = TAILQ_FIRST(&ifp->if_prefixhead))
+ != NULL) {
+ for ( ; TAILQ_NEXT(ifpr, ifpr_list);
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list))
+ continue;
+ TAILQ_NEXT(ifpr, ifpr_list) = rp2ifpr(rpp);
+ } else
+ TAILQ_FIRST(&ifp->if_prefixhead) =
+ rp2ifpr(rpp);
+ rp2ifpr(rpp)->ifpr_type = IN6_PREFIX_RR;
+ }
+ /* link rr_prefix entry to rr_prefix list */
+ s = splnet();
+ LIST_INSERT_HEAD(&rr_prefix, rpp, rp_entry);
+ splx(s);
+ }
+
+ if (!new->rp_raf_auto)
+ return 0;
+
+ /*
+ * Add an address for each interface id, if it is not yet
+ * If it existed but not pointing to the prefix yet,
+ * init the prefix pointer.
+ */
+ for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
+ rap = rap->ra_entry.le_next) {
+ if (rap->ra_addr != NULL) {
+ if (rap->ra_addr->ia6_ifpr == NULL)
+ rap->ra_addr->ia6_ifpr = rp2ifpr(rpp);
+ continue;
+ }
+ add_each_addr(so, rpp, rap);
+ }
+ return 0;
+}
+
+static int
+add_each_prefix(struct socket *so, struct rr_prefix *rpp)
+{
+ init_prefix_ltimes(rpp);
+ return(rrpr_update(so, rpp));
+}
+
+static void
+rp_remove(struct rr_prefix *rpp)
+{
+ int s;
+
+ s = splnet();
+ /* unlink rp_entry from if_prefixhead */
+ {
+ struct ifnet *ifp = rpp->rp_ifp;
+ struct ifprefix *ifpr;
+
+ if ((ifpr = TAILQ_FIRST(&ifp->if_prefixhead)) == rp2ifpr(rpp))
+ TAILQ_FIRST(&ifp->if_prefixhead) =
+ TAILQ_NEXT(ifpr, ifpr_list);
+ else {
+ while (TAILQ_NEXT(ifpr, ifpr_list) != NULL &&
+ (TAILQ_NEXT(ifpr, ifpr_list) != rp2ifpr(rpp)))
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list);
+ if (TAILQ_NEXT(ifpr, ifpr_list))
+ TAILQ_NEXT(ifpr, ifpr_list) =
+ TAILQ_NEXT(rp2ifpr(rpp), ifpr_list);
+ else
+ printf("Couldn't unlink rr_prefix from ifp\n");
+ }
+ }
+ /* unlink rp_entry from rr_prefix list */
+ LIST_REMOVE(rpp, rp_entry);
+ splx(s);
+ free(rpp, M_IP6RR);
+}
+
+static int
+create_ra_entry(struct rp_addr **rapp)
+{
+ *rapp = (struct rp_addr *)malloc(sizeof(struct rp_addr), M_RR_ADDR,
+ M_NOWAIT);
+ if (*rapp == NULL) {
+ log(LOG_ERR, "in6_prefix.c: init_newprefix:%d: ENOBUFS"
+ "for rp_addr", __LINE__);
+ return ENOBUFS;
+ }
+ bzero(*rapp, sizeof(*(*rapp)));
+
+ return 0;
+}
+
+static int
+init_newprefix(struct in6_rrenumreq *irr, struct ifprefix *ifpr,
+ struct rr_prefix *rpp)
+{
+ struct rp_addr *orap;
+
+ /* init rp */
+ bzero(rpp, sizeof(*rpp));
+ rpp->rp_type = IN6_PREFIX_RR;
+ rpp->rp_ifp = ifpr->ifpr_ifp;
+ rpp->rp_plen = ifpr->ifpr_plen;
+ rpp->rp_prefix.sin6_len = sizeof(rpp->rp_prefix);
+ rpp->rp_prefix.sin6_family = AF_INET6;
+ bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
+ (char *)&irr->irr_useprefix.sin6_addr,
+ sizeof(irr->irr_useprefix.sin6_addr) << 3,
+ 0, irr->irr_u_uselen);
+ /* copy keeplen part if necessary as necessary len */
+ if (irr->irr_u_uselen < ifpr->ifpr_plen)
+ bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
+ (char *)IFPR_IN6(ifpr), sizeof(*IFPR_IN6(ifpr)) << 3,
+ irr->irr_u_uselen,
+ min(ifpr->ifpr_plen - irr->irr_u_uselen,
+ irr->irr_u_keeplen));
+ for (orap = (ifpr2rp(ifpr)->rp_addrhead).lh_first; orap != NULL;
+ orap = orap->ra_entry.le_next) {
+ struct rp_addr *rap;
+ int error = 0;
+
+ if ((error = create_ra_entry(&rap)) != 0)
+ return error;
+ rap->ra_ifid = orap->ra_ifid;
+ rap->ra_flags.anycast = (orap->ra_addr != NULL &&
+ (orap->ra_addr->ia_flags &
+ IN6_IFF_ANYCAST) != 0) ? 1 : 0;
+ LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
+ }
+ rpp->rp_vltime = irr->irr_vltime;
+ rpp->rp_pltime = irr->irr_pltime;
+ rpp->rp_raf_onlink = irr->irr_raf_mask_onlink ? irr->irr_raf_onlink :
+ ifpr2rp(ifpr)->rp_raf_onlink;
+ rpp->rp_raf_auto = irr->irr_raf_mask_auto ? irr->irr_raf_auto :
+ ifpr2rp(ifpr)->rp_raf_auto;
+ /* Is some FlagMasks for rrf necessary? */
+ rpp->rp_rrf = irr->irr_rrf;
+ rpp->rp_origin = irr->irr_origin;
+
+ return 0;
+}
+
+static void
+free_rp_entries(struct rr_prefix *rpp)
+{
+ /*
+ * This func is only called with rpp on stack(not on list).
+ * So no splnet() here
+ */
+ while (!LIST_EMPTY(&rpp->rp_addrhead))
+ {
+ struct rp_addr *rap;
+
+ rap = LIST_FIRST(&rpp->rp_addrhead);
+ LIST_REMOVE(rap, ra_entry);
+ free(rap, M_RR_ADDR);
+ }
+}
+
+static int
+add_useprefixes(struct socket *so, struct ifnet *ifp,
+ struct in6_rrenumreq *irr)
+{
+ struct ifprefix *ifpr, *nextifpr;
+ struct rr_prefix rp;
+ int error = 0;
+
+ /* add prefixes to each of marked prefix */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) {
+ nextifpr = TAILQ_NEXT(ifpr, ifpr_list);
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (ifpr2rp(ifpr)->rp_statef_addmark) {
+ if ((error = init_newprefix(irr, ifpr, &rp)) != 0)
+ break;
+ error = add_each_prefix(so, &rp);
+ }
+ }
+ /* free each rp_addr entry */
+ free_rp_entries(&rp);
+
+ return error;
+}
+
+static void
+unprefer_prefix(struct rr_prefix *rpp)
+{
+ struct rp_addr *rap;
+
+ for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
+ rap = rap->ra_entry.le_next) {
+ if (rap->ra_addr == NULL)
+ continue;
+ rap->ra_addr->ia6_lifetime.ia6t_preferred = time_second;
+ rap->ra_addr->ia6_lifetime.ia6t_pltime = 0;
+ }
+}
+
+int
+delete_each_prefix(struct socket *so, struct rr_prefix *rpp, u_char origin)
+{
+ struct in6_aliasreq ifra;
+ int error = 0;
+
+ if (rpp->rp_origin > origin)
+ return(EPERM);
+
+ while (rpp->rp_addrhead.lh_first != NULL) {
+ struct rp_addr *rap;
+ int s;
+
+ s = splnet();
+ rap = LIST_FIRST(&rpp->rp_addrhead);
+ if (rap == NULL)
+ break;
+ LIST_REMOVE(rap, ra_entry);
+ splx(s);
+ if (rap->ra_addr == NULL) {
+ free(rap, M_RR_ADDR);
+ continue;
+ }
+ rap->ra_addr->ia6_ifpr = NULL;
+
+ bzero(&ifra, sizeof(ifra));
+ strncpy(ifra.ifra_name, if_name(rpp->rp_ifp),
+ sizeof(ifra.ifra_name));
+ ifra.ifra_addr = rap->ra_addr->ia_addr;
+ ifra.ifra_dstaddr = rap->ra_addr->ia_dstaddr;
+ ifra.ifra_prefixmask = rap->ra_addr->ia_prefixmask;
+
+ error = in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra,
+ rpp->rp_ifp, curproc);
+ if (error != 0)
+ log(LOG_ERR, "in6_prefix.c: delete_each_prefix:"
+ "deletion of an addr %s/%d failed because"
+ "in6_control failed for error %d",
+ ip6_sprintf(&ifra.ifra_addr.sin6_addr),
+ rpp->rp_plen, error);
+
+ free(rap, M_RR_ADDR);
+ }
+ rp_remove(rpp);
+
+ return error;
+}
+
+static void
+delete_prefixes(struct socket *so, struct ifnet *ifp, u_char origin)
+{
+ struct ifprefix *ifpr, *nextifpr;
+
+ /* delete prefixes marked as tobe deleted */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) {
+ nextifpr = TAILQ_NEXT(ifpr, ifpr_list);
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (ifpr2rp(ifpr)->rp_statef_delmark)
+ (void)delete_each_prefix(so, ifpr2rp(ifpr), origin);
+ }
+}
+
+static int
+link_stray_ia6s(struct rr_prefix *rpp)
+{
+ struct ifaddr *ifa;
+
+ for (ifa = rpp->rp_ifp->if_addrlist.tqh_first; ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ struct rp_addr *rap;
+ struct rr_prefix *orpp;
+ int error = 0;
+
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (rpp->rp_plen > in6_matchlen(RP_IN6(rpp), IFA_IN6(ifa)))
+ continue;
+
+ orpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
+ if (orpp != NULL) {
+ if (!in6_are_prefix_equal(RP_IN6(orpp), RP_IN6(rpp),
+ rpp->rp_plen))
+ log(LOG_ERR, "in6_prefix.c: link_stray_ia6s:"
+ "addr %s/%d already linked to a prefix"
+ "and it matches also %s/%d",
+ ip6_sprintf(IFA_IN6(ifa)), orpp->rp_plen,
+ ip6_sprintf(RP_IN6(rpp)),
+ rpp->rp_plen);
+ continue;
+ }
+ if ((error = assigne_ra_entry(rpp,
+ (sizeof(rap->ra_ifid) << 3) -
+ rpp->rp_plen,
+ (struct in6_ifaddr *)ifa)) != 0)
+ return error;
+ }
+ return 0;
+}
+
+int
+in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data,
+ struct ifnet *ifp)
+{
+ struct rr_prefix *rpp, rp_tmp;
+ struct rp_addr *rap;
+ struct in6_prefixreq *ipr = (struct in6_prefixreq *)data;
+ struct in6_rrenumreq *irr = (struct in6_rrenumreq *)data;
+ struct ifaddr *ifa;
+ int error = 0;
+
+ /*
+ * Failsafe for errneous address config program.
+ * Let's hope rrenumd don't make a mistakes.
+ */
+ if (ipr->ipr_origin <= PR_ORIG_RA)
+ ipr->ipr_origin = PR_ORIG_STATIC;
+
+ switch (cmd) {
+ case SIOCSGIFPREFIX_IN6:
+ delmark_global_prefixes(ifp, irr);
+ /* FALL THROUGH */
+ case SIOCAIFPREFIX_IN6:
+ case SIOCCIFPREFIX_IN6:
+ /* check if preferred lifetime > valid lifetime */
+ if (irr->irr_pltime > irr->irr_vltime) {
+ log(LOG_NOTICE,
+ "in6_prefix_ioctl: preferred lifetime"
+ "(%ld) is greater than valid lifetime(%ld)",
+ (u_long)irr->irr_pltime, (u_long)irr->irr_vltime);
+ error = EINVAL;
+ break;
+ }
+ if (mark_matched_prefixes(cmd, ifp, irr)) {
+ if (irr->irr_u_uselen != 0)
+ if ((error = add_useprefixes(so, ifp, irr))
+ != 0)
+ goto failed;
+ if (cmd != SIOCAIFPREFIX_IN6)
+ delete_prefixes(so, ifp, irr->irr_origin);
+ } else
+ return (EADDRNOTAVAIL);
+ failed:
+ unmark_prefixes(ifp);
+ break;
+ case SIOCGIFPREFIX_IN6:
+ rpp = search_matched_prefix(ifp, ipr);
+ if (rpp == NULL || ifp != rpp->rp_ifp)
+ return (EADDRNOTAVAIL);
+
+ ipr->ipr_origin = rpp->rp_origin;
+ ipr->ipr_plen = rpp->rp_plen;
+ ipr->ipr_vltime = rpp->rp_vltime;
+ ipr->ipr_pltime = rpp->rp_pltime;
+ ipr->ipr_flags = rpp->rp_flags;
+ ipr->ipr_prefix = rpp->rp_prefix;
+
+ break;
+ case SIOCSIFPREFIX_IN6:
+ /* check if preferred lifetime > valid lifetime */
+ if (ipr->ipr_pltime > ipr->ipr_vltime) {
+ log(LOG_NOTICE,
+ "in6_prefix_ioctl: preferred lifetime"
+ "(%ld) is greater than valid lifetime(%ld)",
+ (u_long)ipr->ipr_pltime, (u_long)ipr->ipr_vltime);
+ error = EINVAL;
+ break;
+ }
+
+ /* init rp_tmp */
+ bzero((caddr_t)&rp_tmp, sizeof(rp_tmp));
+ rp_tmp.rp_ifp = ifp;
+ rp_tmp.rp_plen = ipr->ipr_plen;
+ rp_tmp.rp_prefix = ipr->ipr_prefix;
+ rp_tmp.rp_vltime = ipr->ipr_vltime;
+ rp_tmp.rp_pltime = ipr->ipr_pltime;
+ rp_tmp.rp_flags = ipr->ipr_flags;
+ rp_tmp.rp_origin = ipr->ipr_origin;
+
+ /* create rp_addr entries, usually at least for lladdr */
+ if ((error = link_stray_ia6s(&rp_tmp)) != 0) {
+ free_rp_entries(&rp_tmp);
+ break;
+ }
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
+ if (ifa != NULL) {
+ if ((error = create_ra_entry(&rap)) != 0) {
+ free_rp_entries(&rp_tmp);
+ break;
+ }
+ /* copy interface id part */
+ bit_copy((caddr_t)&rap->ra_ifid,
+ sizeof(rap->ra_ifid) << 3,
+ (caddr_t)IFA_IN6(ifa),
+ sizeof(*IFA_IN6(ifa)) << 3,
+ rp_tmp.rp_plen,
+ (sizeof(rap->ra_ifid) << 3) - rp_tmp.rp_plen);
+ /* insert into list */
+ LIST_INSERT_HEAD(&rp_tmp.rp_addrhead, rap, ra_entry);
+ }
+
+ error = add_each_prefix(so, &rp_tmp);
+
+ /* free each rp_addr entry */
+ free_rp_entries(&rp_tmp);
+
+ break;
+ case SIOCDIFPREFIX_IN6:
+ rpp = search_matched_prefix(ifp, ipr);
+ if (rpp == NULL || ifp != rpp->rp_ifp)
+ return (EADDRNOTAVAIL);
+
+ error = delete_each_prefix(so, rpp, ipr->ipr_origin);
+ break;
+ }
+ return error;
+}
+
+void
+in6_rr_timer(void *ignored_arg)
+{
+ int s;
+ struct rr_prefix *rpp;
+
+ timeout(in6_rr_timer, (caddr_t)0, ip6_rr_prune * hz);
+
+ s = splnet();
+ /* expire */
+ rpp = LIST_FIRST(&rr_prefix);
+ while (rpp) {
+ if (rpp->rp_expire && rpp->rp_expire < time_second) {
+ struct rr_prefix *next_rpp;
+ struct socket so;
+
+ /* XXX: init dummy so */
+ bzero(&so, sizeof(so));
+
+ next_rpp = LIST_NEXT(rpp, rp_entry);
+ delete_each_prefix(&so, rpp, PR_ORIG_KERNEL);
+ rpp = next_rpp;
+ continue;
+ }
+ if (rpp->rp_preferred && rpp->rp_preferred < time_second)
+ unprefer_prefix(rpp);
+ rpp = LIST_NEXT(rpp, rp_entry);
+ }
+ splx(s);
+}
diff --git a/sys/netinet6/in6_prefix.h b/sys/netinet6/in6_prefix.h
new file mode 100644
index 0000000..fa5a0e3
--- /dev/null
+++ b/sys/netinet6/in6_prefix.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998 and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct rr_prefix {
+ struct ifprefix rp_ifpr;
+ LIST_ENTRY(rr_prefix) rp_entry;
+ LIST_HEAD(rp_addrhead, rp_addr) rp_addrhead;
+ struct sockaddr_in6 rp_prefix; /* prefix */
+ u_int32_t rp_vltime; /* advertised valid lifetime */
+ u_int32_t rp_pltime; /* advertised preferred lifetime */
+ time_t rp_expire; /* expiration time of the prefix */
+ time_t rp_preferred; /* preferred time of the prefix */
+ struct in6_prflags rp_flags;
+ u_char rp_origin; /* from where this prefix info is obtained */
+ struct rp_stateflags {
+ /* if some prefix should be added to this prefix */
+ u_char addmark : 1;
+ u_char delmark : 1; /* if this prefix will be deleted */
+ } rp_stateflags;
+};
+
+#define rp_type rp_ifpr.ifpr_type
+#define rp_ifp rp_ifpr.ifpr_ifp
+#define rp_plen rp_ifpr.ifpr_plen
+
+#define rp_raf rp_flags.prf_ra
+#define rp_raf_onlink rp_flags.prf_ra.onlink
+#define rp_raf_auto rp_flags.prf_ra.autonomous
+
+#define rp_statef_addmark rp_stateflags.addmark
+#define rp_statef_delmark rp_stateflags.delmark
+
+#define rp_rrf rp_flags.prf_rr
+#define rp_rrf_decrvalid rp_flags.prf_rr.decrvalid
+#define rp_rrf_decrprefd rp_flags.prf_rr.decrprefd
+
+struct rp_addr {
+ LIST_ENTRY(rp_addr) ra_entry;
+ struct in6_addr ra_ifid;
+ struct in6_ifaddr *ra_addr;
+ struct ra_flags {
+ u_char anycast : 1;
+ } ra_flags;
+};
+
+#define ifpr2rp(ifpr) ((struct rr_prefix *)(ifpr))
+#define rp2ifpr(rp) ((struct ifprefix *)(rp))
+
+#define RP_IN6(rp) (&(rp)->rp_prefix.sin6_addr)
+
+#define RR_INFINITE_LIFETIME 0xffffffff
+
+
+LIST_HEAD(rr_prhead, rr_prefix);
+
+extern struct rr_prhead rr_prefix;
+
+void in6_rr_timer __P((void *));
+int delete_each_prefix __P((struct socket *so, struct rr_prefix *rpp,
+ u_char origin));
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
new file mode 100644
index 0000000..8189d01
--- /dev/null
+++ b/sys/netinet6/in6_proto.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_proto.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include "opt_inet.h"
+#include "opt_ipsec.h"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/kernel.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/radix.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+
+#include <netinet/tcp.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <netinet6/tcp6_var.h>
+#include <netinet6/udp6_var.h>
+
+#include <netinet6/pim6_var.h>
+
+#include <netinet6/nd6.h>
+#include <netinet6/in6_prefix.h>
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif /* INET6 */
+#include <netinet6/ah.h>
+#ifdef IPSEC_ESP
+#include <netinet6/esp.h>
+#endif
+#include <netinet6/ipcomp.h>
+#endif /*IPSEC*/
+
+#include <netinet6/ip6protosw.h>
+
+/* #include "gif.h" */
+#if NGIF > 0
+#include <netinet6/in6_gif.h>
+#endif
+
+#include <net/net_osdep.h>
+
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
+
+/*
+ * TCP/IP protocol family: IP6, ICMP6, UDP, TCP.
+ */
+
+extern struct domain inet6domain;
+static struct pr_usrreqs nousrreqs;
+
+struct ip6protosw inet6sw[] = {
+{ 0, &inet6domain, IPPROTO_IPV6, 0,
+ 0, 0, 0, 0,
+ 0,
+ ip6_init, 0, frag6_slowtimo, frag6_drain,
+ &nousrreqs,
+},
+{ SOCK_RAW, &inet6domain, IPPROTO_RAW, PR_ATOMIC | PR_ADDR,
+ rip6_input, rip6_output, 0, rip6_ctloutput,
+ 0,
+ 0, 0, 0, 0,
+ &rip6_usrreqs
+},
+{ SOCK_RAW, &inet6domain, IPPROTO_ICMPV6, PR_ATOMIC | PR_ADDR,
+ icmp6_input, rip6_output, 0, rip6_ctloutput,
+ 0,
+ icmp6_init, icmp6_fasttimo, 0, 0,
+ &rip6_usrreqs
+},
+{ SOCK_RAW, &inet6domain, IPPROTO_DSTOPTS,PR_ATOMIC|PR_ADDR,
+ dest6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs
+},
+{ SOCK_RAW, &inet6domain, IPPROTO_ROUTING,PR_ATOMIC|PR_ADDR,
+ route6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs
+},
+{ SOCK_RAW, &inet6domain, IPPROTO_FRAGMENT,PR_ATOMIC|PR_ADDR,
+ frag6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs
+},
+#ifdef IPSEC
+{ SOCK_RAW, &inet6domain, IPPROTO_AH, PR_ATOMIC|PR_ADDR,
+ ah6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs,
+},
+#ifdef IPSEC_ESP
+{ SOCK_RAW, &inet6domain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR,
+ esp6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs,
+},
+#endif
+{ SOCK_RAW, &inet6domain, IPPROTO_IPCOMP, PR_ATOMIC|PR_ADDR,
+ ipcomp6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs,
+},
+#endif /* IPSEC */
+#if NGIF > 0
+{ SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR,
+ in6_gif_input,0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs
+},
+#ifdef INET6
+{ SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR,
+ in6_gif_input,0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs
+},
+#endif /* INET6 */
+#endif /* GIF */
+/* raw wildcard */
+{ SOCK_RAW, &inet6domain, 0, PR_ATOMIC | PR_ADDR,
+ rip6_input, rip6_output, 0, rip6_ctloutput,
+ 0,
+ 0, 0, 0, 0,
+ &rip6_usrreqs
+},
+};
+
+extern int in6_inithead __P((void **, int));
+
+struct domain inet6domain =
+ { AF_INET6, "internet6", 0, 0, 0,
+ (struct protosw *)inet6sw,
+ (struct protosw *)&inet6sw[sizeof(inet6sw)/sizeof(inet6sw[0])], 0,
+ in6_inithead,
+ offsetof(struct sockaddr_in6, sin6_addr) << 3,
+ sizeof(struct sockaddr_in6) };
+
+DOMAIN_SET(inet6);
+
+/*
+ * Internet configuration info
+ */
+#ifndef IPV6FORWARDING
+#ifdef GATEWAY6
+#define IPV6FORWARDING 1 /* forward IP6 packets not for us */
+#else
+#define IPV6FORWARDING 0 /* don't forward IP6 packets not for us */
+#endif /* GATEWAY6 */
+#endif /* !IPV6FORWARDING */
+
+#ifndef IPV6_SENDREDIRECTS
+#define IPV6_SENDREDIRECTS 1
+#endif
+
+int ip6_forwarding = IPV6FORWARDING; /* act as router? */
+int ip6_sendredirects = IPV6_SENDREDIRECTS;
+int ip6_defhlim = IPV6_DEFHLIM;
+int ip6_defmcasthlim = IPV6_DEFAULT_MULTICAST_HOPS;
+int ip6_accept_rtadv = 0; /* "IPV6FORWARDING ? 0 : 1" is dangerous */
+int ip6_maxfragpackets = 200;
+int ip6_log_interval = 5;
+int ip6_hdrnestlimit = 50; /* appropriate? */
+int ip6_dad_count = 1; /* DupAddrDetectionTransmits */
+u_int32_t ip6_flow_seq;
+int ip6_auto_flowlabel = 1;
+#if NGIF > 0
+int ip6_gif_hlim = GIF_HLIM;
+#else
+int ip6_gif_hlim = 0;
+#endif
+int ip6_use_deprecated = 1; /* allow deprecated addr (RFC2462 5.5.4) */
+int ip6_rr_prune = 5; /* router renumbering prefix
+ * walk list every 5 sec. */
+int ip6_mapped_addr_on = 1;
+
+u_int32_t ip6_id = 0UL;
+int ip6_keepfaith = 0;
+time_t ip6_log_time = (time_t)0L;
+
+/* icmp6 */
+/*
+ * BSDI4 defines these variables in in_proto.c...
+ * XXX: what if we don't define INET? Should we define pmtu6_expire
+ * or so? (jinmei@kame.net 19990310)
+ */
+int pmtu_expire = 60*10;
+int pmtu_probe = 60*2;
+
+/* raw IP6 parameters */
+/*
+ * Nominal space allocated to a raw ip socket.
+ */
+#define RIPV6SNDQ 8192
+#define RIPV6RCVQ 8192
+
+u_long rip6_sendspace = RIPV6SNDQ;
+u_long rip6_recvspace = RIPV6RCVQ;
+
+/* ICMPV6 parameters */
+int icmp6_rediraccept = 1; /* accept and process redirects */
+int icmp6_redirtimeout = 10 * 60; /* 10 minutes */
+u_int icmp6errratelim = 1000; /* 1000usec = 1msec */
+
+/* UDP on IP6 parameters */
+int udp6_sendspace = 9216; /* really max datagram size */
+int udp6_recvspace = 40 * (1024 + sizeof(struct sockaddr_in6));
+ /* 40 1K datagrams */
+
+/*
+ * sysctl related items.
+ */
+SYSCTL_NODE(_net, PF_INET6, inet6, CTLFLAG_RW, 0,
+ "Internet6 Family");
+
+/* net.inet6 */
+SYSCTL_NODE(_net_inet6, IPPROTO_IPV6, ip6, CTLFLAG_RW, 0, "IP6");
+SYSCTL_NODE(_net_inet6, IPPROTO_ICMPV6, icmp6, CTLFLAG_RW, 0, "ICMP6");
+SYSCTL_NODE(_net_inet6, IPPROTO_UDP, udp6, CTLFLAG_RW, 0, "UDP6");
+SYSCTL_NODE(_net_inet6, IPPROTO_TCP, tcp6, CTLFLAG_RW, 0, "TCP6");
+#ifdef IPSEC
+SYSCTL_NODE(_net_inet6, IPPROTO_ESP, ipsec6, CTLFLAG_RW, 0, "IPSEC6");
+#endif /* IPSEC */
+
+/* net.inet6.ip6 */
+static int
+sysctl_ip6_forwarding SYSCTL_HANDLER_ARGS
+{
+ int error = 0;
+ int old_ip6_forwarding;
+ int changed;
+
+ error = SYSCTL_OUT(req, arg1, sizeof(int));
+ if (error || !req->newptr)
+ return (error);
+ old_ip6_forwarding = ip6_forwarding;
+ error = SYSCTL_IN(req, arg1, sizeof(int));
+ if (error != 0)
+ return (error);
+ changed = (ip6_forwarding ? 1 : 0) ^ (old_ip6_forwarding ? 1 : 0);
+ if (changed == 0)
+ return (error);
+ if (ip6_forwarding != 0) { /* host becomes router */
+ int s = splnet();
+ struct nd_prefix *pr, *next;
+
+ for (pr = nd_prefix.lh_first; pr; pr = next) {
+ next = pr->ndpr_next;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr);
+ prelist_remove(pr);
+ }
+ splx(s);
+ } else { /* router becomes host */
+ struct socket so;
+
+ /* XXX: init dummy so */
+ bzero(&so, sizeof(so));
+ while(!LIST_EMPTY(&rr_prefix))
+ delete_each_prefix(&so, LIST_FIRST(&rr_prefix),
+ PR_ORIG_KERNEL);
+ }
+
+ return (error);
+}
+
+SYSCTL_OID(_net_inet6_ip6, IPV6CTL_FORWARDING, forwarding,
+ CTLTYPE_INT|CTLFLAG_RW, &ip6_forwarding, 0, sysctl_ip6_forwarding,
+ "I", "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_SENDREDIRECTS,
+ redirect, CTLFLAG_RW, &ip6_sendredirects, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DEFHLIM,
+ hlim, CTLFLAG_RW, &ip6_defhlim, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAXFRAGPACKETS,
+ maxfragpackets, CTLFLAG_RW, &ip6_maxfragpackets, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_ACCEPT_RTADV,
+ accept_rtadv, CTLFLAG_RW, &ip6_accept_rtadv, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_KEEPFAITH,
+ keepfaith, CTLFLAG_RW, &ip6_keepfaith, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_LOG_INTERVAL,
+ log_interval, CTLFLAG_RW, &ip6_log_interval, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_HDRNESTLIMIT,
+ hdrnestlimit, CTLFLAG_RW, &ip6_hdrnestlimit, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DAD_COUNT,
+ dad_count, CTLFLAG_RW, &ip6_dad_count, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_AUTO_FLOWLABEL,
+ auto_flowlabel, CTLFLAG_RW, &ip6_auto_flowlabel, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DEFMCASTHLIM,
+ defmcasthlim, CTLFLAG_RW, &ip6_defmcasthlim, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_GIF_HLIM,
+ gifhlim, CTLFLAG_RW, &ip6_gif_hlim, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_USE_DEPRECATED,
+ use_deprecated, CTLFLAG_RW, &ip6_use_deprecated, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_RR_PRUNE,
+ rr_prune, CTLFLAG_RW, &ip6_rr_prune, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAPPED_ADDR,
+ mapped_addr, CTLFLAG_RW, &ip6_mapped_addr_on, 0, "");
+
+/* net.inet6.icmp6 */
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_REDIRACCEPT,
+ rediraccept, CTLFLAG_RW, &icmp6_rediraccept, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_REDIRTIMEOUT,
+ redirtimeout, CTLFLAG_RW, &icmp6_redirtimeout, 0, "");
+SYSCTL_STRUCT(_net_inet6_icmp6, ICMPV6CTL_STATS, stats, CTLFLAG_RD,
+ &icmp6stat, icmp6stat, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ERRRATELIMIT,
+ errratelimit, CTLFLAG_RW, &icmp6errratelim, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_PRUNE,
+ nd6_prune, CTLFLAG_RW, &nd6_prune, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_DELAY,
+ nd6_delay, CTLFLAG_RW, &nd6_delay, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_UMAXTRIES,
+ nd6_umaxtries, CTLFLAG_RW, &nd6_umaxtries, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MMAXTRIES,
+ nd6_mmaxtries, CTLFLAG_RW, &nd6_mmaxtries, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_USELOOPBACK,
+ nd6_useloopback, CTLFLAG_RW, &nd6_useloopback, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_PROXYALL,
+ nd6_proxyall, CTLFLAG_RW, &nd6_proxyall, 0, "");
diff --git a/sys/netinet6/in6_rmx.c b/sys/netinet6/in6_rmx.c
new file mode 100644
index 0000000..56e9d94
--- /dev/null
+++ b/sys/netinet6/in6_rmx.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright 1994, 1995 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: in6_rmx.c,v 1.3 1999/08/16 13:42:53 itojun Exp $
+ */
+
+/*
+ * This code does two things necessary for the enhanced TCP metrics to
+ * function in a useful manner:
+ * 1) It marks all non-host routes as `cloning', thus ensuring that
+ * every actual reference to such a route actually gets turned
+ * into a reference to a host route to the specific destination
+ * requested.
+ * 2) When such routes lose all their references, it arranges for them
+ * to be deleted in some random collection of circumstances, so that
+ * a large quantity of stale routing data is not kept in kernel memory
+ * indefinitely. See in6_rtqtimo() below for the exact mechanism.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/mbuf.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/ip_var.h>
+#include <netinet/in_var.h>
+
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+
+#include <netinet6/icmp6.h>
+
+#include <netinet/tcp.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+
+extern int in6_inithead __P((void **head, int off));
+
+#define RTPRF_OURS RTF_PROTO3 /* set on routes we manage */
+
+/*
+ * Do what we need to do when inserting a route.
+ */
+static struct radix_node *
+in6_addroute(void *v_arg, void *n_arg, struct radix_node_head *head,
+ struct radix_node *treenodes)
+{
+ struct rtentry *rt = (struct rtentry *)treenodes;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rt_key(rt);
+ struct radix_node *ret;
+
+ /*
+ * For IPv6, all unicast non-host routes are automatically cloning.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
+ rt->rt_flags |= RTF_MULTICAST;
+
+ if (!(rt->rt_flags & (RTF_HOST | RTF_CLONING | RTF_MULTICAST))) {
+ rt->rt_flags |= RTF_PRCLONING;
+ }
+
+ /*
+ * A little bit of help for both IPv6 output and input:
+ * For local addresses, we make sure that RTF_LOCAL is set,
+ * with the thought that this might one day be used to speed up
+ * ip_input().
+ *
+ * We also mark routes to multicast addresses as such, because
+ * it's easy to do and might be useful (but this is much more
+ * dubious since it's so easy to inspect the address). (This
+ * is done above.)
+ *
+ * XXX
+ * should elaborate the code.
+ */
+ if (rt->rt_flags & RTF_HOST) {
+ if (IN6_ARE_ADDR_EQUAL(&satosin6(rt->rt_ifa->ifa_addr)
+ ->sin6_addr,
+ &sin6->sin6_addr)) {
+ rt->rt_flags |= RTF_LOCAL;
+ }
+ }
+
+ /*
+ * We also specify a send and receive pipe size for every
+ * route added, to help TCP a bit. TCP doesn't actually
+ * want a true pipe size, which would be prohibitive in memory
+ * costs and is hard to compute anyway; it simply uses these
+ * values to size its buffers. So, we fill them in with the
+ * same values that TCP would have used anyway, and allow the
+ * installing program or the link layer to override these values
+ * as it sees fit. This will hopefully allow TCP more
+ * opportunities to save its ssthresh value.
+ */
+ if (!rt->rt_rmx.rmx_sendpipe && !(rt->rt_rmx.rmx_locks & RTV_SPIPE))
+ rt->rt_rmx.rmx_sendpipe = tcp_sendspace;
+
+ if (!rt->rt_rmx.rmx_recvpipe && !(rt->rt_rmx.rmx_locks & RTV_RPIPE))
+ rt->rt_rmx.rmx_recvpipe = tcp_recvspace;
+
+ if (!rt->rt_rmx.rmx_mtu && !(rt->rt_rmx.rmx_locks & RTV_MTU)
+ && rt->rt_ifp)
+ rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
+
+ ret = rn_addroute(v_arg, n_arg, head, treenodes);
+ if (ret == NULL && rt->rt_flags & RTF_HOST) {
+ struct rtentry *rt2;
+ /*
+ * We are trying to add a host route, but can't.
+ * Find out if it is because of an
+ * ARP entry and delete it if so.
+ */
+ rt2 = rtalloc1((struct sockaddr *)sin6, 0,
+ RTF_CLONING | RTF_PRCLONING);
+ if (rt2) {
+ if (rt2->rt_flags & RTF_LLINFO &&
+ rt2->rt_flags & RTF_HOST &&
+ rt2->rt_gateway &&
+ rt2->rt_gateway->sa_family == AF_LINK) {
+ rtrequest(RTM_DELETE,
+ (struct sockaddr *)rt_key(rt2),
+ rt2->rt_gateway,
+ rt_mask(rt2), rt2->rt_flags, 0);
+ ret = rn_addroute(v_arg, n_arg, head,
+ treenodes);
+ }
+ RTFREE(rt2);
+ }
+ } else if (ret == NULL && rt->rt_flags & RTF_CLONING) {
+ struct rtentry *rt2;
+ /*
+ * We are trying to add a net route, but can't.
+ * The following case should be allowed, so we'll make a
+ * special check for this:
+ * Two IPv6 addresses with the same prefix is assigned
+ * to a single interrface.
+ * # ifconfig if0 inet6 3ffe:0501::1 prefix 64 alias (*1)
+ * # ifconfig if0 inet6 3ffe:0501::2 prefix 64 alias (*2)
+ * In this case, (*1) and (*2) want to add the same
+ * net route entry, 3ffe:0501:: -> if0.
+ * This case should not raise an error.
+ */
+ rt2 = rtalloc1((struct sockaddr *)sin6, 0,
+ RTF_CLONING | RTF_PRCLONING);
+ if (rt2) {
+ if ((rt2->rt_flags & (RTF_CLONING|RTF_HOST|RTF_GATEWAY))
+ == RTF_CLONING
+ && rt2->rt_gateway
+ && rt2->rt_gateway->sa_family == AF_LINK
+ && rt2->rt_ifp == rt->rt_ifp) {
+ ret = rt2->rt_nodes;
+ }
+ RTFREE(rt2);
+ }
+ }
+ return ret;
+}
+
+/*
+ * This code is the inverse of in6_clsroute: on first reference, if we
+ * were managing the route, stop doing so and set the expiration timer
+ * back off again.
+ */
+static struct radix_node *
+in6_matroute(void *v_arg, struct radix_node_head *head)
+{
+ struct radix_node *rn = rn_match(v_arg, head);
+ struct rtentry *rt = (struct rtentry *)rn;
+
+ if (rt && rt->rt_refcnt == 0) { /* this is first reference */
+ if (rt->rt_flags & RTPRF_OURS) {
+ rt->rt_flags &= ~RTPRF_OURS;
+ rt->rt_rmx.rmx_expire = 0;
+ }
+ }
+ return rn;
+}
+
+static int rtq_reallyold = 60*60;
+ /* one hour is ``really old'' */
+SYSCTL_INT(_net_inet_ip, IPCTL_RTEXPIRE, rtexpire,
+ CTLFLAG_RW, &rtq_reallyold , 0, "");
+
+static int rtq_minreallyold = 10;
+ /* never automatically crank down to less */
+SYSCTL_INT(_net_inet_ip, IPCTL_RTMINEXPIRE, rtminexpire,
+ CTLFLAG_RW, &rtq_minreallyold , 0, "");
+
+static int rtq_toomany = 128;
+ /* 128 cached routes is ``too many'' */
+SYSCTL_INT(_net_inet_ip, IPCTL_RTMAXCACHE, rtmaxcache,
+ CTLFLAG_RW, &rtq_toomany , 0, "");
+
+
+/*
+ * On last reference drop, mark the route as belong to us so that it can be
+ * timed out.
+ */
+static void
+in6_clsroute(struct radix_node *rn, struct radix_node_head *head)
+{
+ struct rtentry *rt = (struct rtentry *)rn;
+
+ if (!(rt->rt_flags & RTF_UP))
+ return; /* prophylactic measures */
+
+ if ((rt->rt_flags & (RTF_LLINFO | RTF_HOST)) != RTF_HOST)
+ return;
+
+ if ((rt->rt_flags & (RTF_WASCLONED | RTPRF_OURS))
+ != RTF_WASCLONED)
+ return;
+
+ /*
+ * As requested by David Greenman:
+ * If rtq_reallyold is 0, just delete the route without
+ * waiting for a timeout cycle to kill it.
+ */
+ if (rtq_reallyold != 0) {
+ rt->rt_flags |= RTPRF_OURS;
+ rt->rt_rmx.rmx_expire = time_second + rtq_reallyold;
+ } else {
+ rtrequest(RTM_DELETE,
+ (struct sockaddr *)rt_key(rt),
+ rt->rt_gateway, rt_mask(rt),
+ rt->rt_flags, 0);
+ }
+}
+
+struct rtqk_arg {
+ struct radix_node_head *rnh;
+ int mode;
+ int updating;
+ int draining;
+ int killed;
+ int found;
+ time_t nextstop;
+};
+
+/*
+ * Get rid of old routes. When draining, this deletes everything, even when
+ * the timeout is not expired yet. When updating, this makes sure that
+ * nothing has a timeout longer than the current value of rtq_reallyold.
+ */
+static int
+in6_rtqkill(struct radix_node *rn, void *rock)
+{
+ struct rtqk_arg *ap = rock;
+ struct rtentry *rt = (struct rtentry *)rn;
+ int err;
+
+ if (rt->rt_flags & RTPRF_OURS) {
+ ap->found++;
+
+ if (ap->draining || rt->rt_rmx.rmx_expire <= time_second) {
+ if (rt->rt_refcnt > 0)
+ panic("rtqkill route really not free");
+
+ err = rtrequest(RTM_DELETE,
+ (struct sockaddr *)rt_key(rt),
+ rt->rt_gateway, rt_mask(rt),
+ rt->rt_flags, 0);
+ if (err) {
+ log(LOG_WARNING, "in6_rtqkill: error %d", err);
+ } else {
+ ap->killed++;
+ }
+ } else {
+ if (ap->updating
+ && (rt->rt_rmx.rmx_expire - time_second
+ > rtq_reallyold)) {
+ rt->rt_rmx.rmx_expire = time_second
+ + rtq_reallyold;
+ }
+ ap->nextstop = lmin(ap->nextstop,
+ rt->rt_rmx.rmx_expire);
+ }
+ }
+
+ return 0;
+}
+
+#define RTQ_TIMEOUT 60*10 /* run no less than once every ten minutes */
+static int rtq_timeout = RTQ_TIMEOUT;
+
+static void
+in6_rtqtimo(void *rock)
+{
+ struct radix_node_head *rnh = rock;
+ struct rtqk_arg arg;
+ struct timeval atv;
+ static time_t last_adjusted_timeout = 0;
+ int s;
+
+ arg.found = arg.killed = 0;
+ arg.rnh = rnh;
+ arg.nextstop = time_second + rtq_timeout;
+ arg.draining = arg.updating = 0;
+ s = splnet();
+ rnh->rnh_walktree(rnh, in6_rtqkill, &arg);
+ splx(s);
+
+ /*
+ * Attempt to be somewhat dynamic about this:
+ * If there are ``too many'' routes sitting around taking up space,
+ * then crank down the timeout, and see if we can't make some more
+ * go away. However, we make sure that we will never adjust more
+ * than once in rtq_timeout seconds, to keep from cranking down too
+ * hard.
+ */
+ if ((arg.found - arg.killed > rtq_toomany)
+ && (time_second - last_adjusted_timeout >= rtq_timeout)
+ && rtq_reallyold > rtq_minreallyold) {
+ rtq_reallyold = 2*rtq_reallyold / 3;
+ if (rtq_reallyold < rtq_minreallyold) {
+ rtq_reallyold = rtq_minreallyold;
+ }
+
+ last_adjusted_timeout = time_second;
+#ifdef DIAGNOSTIC
+ log(LOG_DEBUG, "in6_rtqtimo: adjusted rtq_reallyold to %d",
+ rtq_reallyold);
+#endif
+ arg.found = arg.killed = 0;
+ arg.updating = 1;
+ s = splnet();
+ rnh->rnh_walktree(rnh, in6_rtqkill, &arg);
+ splx(s);
+ }
+
+ atv.tv_usec = 0;
+ atv.tv_sec = arg.nextstop;
+ timeout(in6_rtqtimo, rock, tvtohz(&atv));
+}
+
+/*
+ * Age old PMTUs.
+ */
+struct mtuex_arg {
+ struct radix_node_head *rnh;
+ time_t nextstop;
+};
+
+static int
+in6_mtuexpire(struct radix_node *rn, void *rock)
+{
+ struct rtentry *rt = (struct rtentry *)rn;
+ struct mtuex_arg *ap = rock;
+
+ /* sanity */
+ if (!rt)
+ panic("rt == NULL in in6_mtuexpire");
+
+ if (rt->rt_rmx.rmx_expire && !(rt->rt_flags & RTF_PROBEMTU)) {
+ if (rt->rt_rmx.rmx_expire <= time_second) {
+ rt->rt_flags |= RTF_PROBEMTU;
+ } else {
+ ap->nextstop = lmin(ap->nextstop,
+ rt->rt_rmx.rmx_expire);
+ }
+ }
+
+ return 0;
+}
+
+#define MTUTIMO_DEFAULT (60*1)
+
+static void
+in6_mtutimo(void *rock)
+{
+ struct radix_node_head *rnh = rock;
+ struct mtuex_arg arg;
+ struct timeval atv;
+ int s;
+
+ arg.rnh = rnh;
+ arg.nextstop = time_second + MTUTIMO_DEFAULT;
+ s = splnet();
+ rnh->rnh_walktree(rnh, in6_mtuexpire, &arg);
+ splx(s);
+
+ atv.tv_usec = 0;
+ atv.tv_sec = arg.nextstop;
+ if (atv.tv_sec < time_second) {
+ printf("invalid mtu expiration time on routing table\n");
+ arg.nextstop = time_second + 30; /*last resort*/
+ }
+ timeout(in6_mtutimo, rock, tvtohz(&atv));
+}
+
+/*
+ * Initialize our routing tree.
+ */
+int
+in6_inithead(void **head, int off)
+{
+ struct radix_node_head *rnh;
+
+ if (!rn_inithead(head, off))
+ return 0;
+
+ if (head != (void **)&rt_tables[AF_INET6]) /* BOGUS! */
+ return 1; /* only do this for the real routing table */
+
+ rnh = *head;
+ rnh->rnh_addaddr = in6_addroute;
+ rnh->rnh_matchaddr = in6_matroute;
+ rnh->rnh_close = in6_clsroute;
+ in6_rtqtimo(rnh); /* kick off timeout first time */
+ in6_mtutimo(rnh); /* kick off timeout first time */
+ return 1;
+}
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index a1aa13e..4117c20 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -1,7 +1,7 @@
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -13,7 +13,7 @@
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -98,10 +98,6 @@ struct in6_ifaddr {
struct sockaddr_in6 ia_prefixmask; /* prefix mask */
u_int32_t ia_plen; /* prefix length */
struct in6_ifaddr *ia_next; /* next in6 list of IP6 addresses */
-#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
- LIST_HEAD(in6_multihead, in6_multi) ia6_multiaddrs;
- /* list of multicast addresses */
-#endif
int ia6_flags;
struct in6_addrlifetime ia6_lifetime; /* NULL = infty */
@@ -189,7 +185,7 @@ struct icmp6_ifstat {
u_int64_t ifs6_in_mlddone;
/*
- * Output statistics. We should solve unresolved routing problem...
+ * Output statistics. We should solve unresolved routing problem...
*/
/* ipv6IfIcmpOutMsgs, total # of output messages */
u_int64_t ifs6_out_msg;
@@ -428,34 +424,14 @@ extern struct ifqueue ip6intrq; /* IP6 packet input queue */
extern struct in6_addr zeroin6_addr;
extern u_char inet6ctlerrmap[];
extern u_long in6_maxmtu;
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3
#ifdef MALLOC_DECLARE
MALLOC_DECLARE(M_IPMADDR);
#endif /* MALLOC_DECLARE */
-#endif
/*
* Macro for finding the internet address structure (in6_ifaddr) corresponding
* to a given interface (ifnet structure).
*/
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-
-#define IFP_TO_IA6(ifp, ia) \
-/* struct ifnet *ifp; */ \
-/* struct in6_ifaddr *ia; */ \
-do { \
- struct ifaddr *ifa; \
- for (ifa = (ifp)->if_addrlist; ifa; ifa = ifa->ifa_next) { \
- if (!ifa->ifa_addr) \
- continue; \
- if (ifa->ifa_addr->sa_family == AF_INET6) \
- break; \
- } \
- (ia) = (struct in6_ifaddr *)ifa; \
-} while (0)
-
-#else
-
#define IFP_TO_IA6(ifp, ia) \
/* struct ifnet *ifp; */ \
/* struct in6_ifaddr *ia; */ \
@@ -471,8 +447,6 @@ do { \
} while (0)
#endif /* _KERNEL */
-#endif
-
/*
* Multi-cast membership entry. One for each group/ifp that a PCB
* belongs to.
@@ -486,20 +460,14 @@ struct in6_multi {
LIST_ENTRY(in6_multi) in6m_entry; /* list glue */
struct in6_addr in6m_addr; /* IP6 multicast address */
struct ifnet *in6m_ifp; /* back pointer to ifnet */
-#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
- struct in6_ifaddr *in6m_ia; /* back pointer to in6_ifaddr */
-#else
struct ifmultiaddr *in6m_ifma; /* back pointer to ifmultiaddr */
-#endif
u_int in6m_refcount; /* # membership claims by sockets */
u_int in6m_state; /* state of the membership */
u_int in6m_timer; /* MLD6 listener report timer */
};
#ifdef _KERNEL
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3
extern LIST_HEAD(in6_multihead, in6_multi) in6_multihead;
-#endif
/*
* Structure used by macros below to remember position when stepping through
@@ -516,8 +484,6 @@ struct in6_multistep {
* returns NLL.
*/
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3
-
#define IN6_LOOKUP_MULTI(addr, ifp, in6m) \
/* struct in6_addr addr; */ \
/* struct ifnet *ifp; */ \
@@ -557,61 +523,6 @@ do { \
IN6_NEXT_MULTI((step), (in6m)); \
} while(0)
-#else /* not FreeBSD3 */
-
-#define IN6_LOOKUP_MULTI(addr, ifp, in6m) \
-/* struct in6_addr addr; */ \
-/* struct ifnet *ifp; */ \
-/* struct in6_multi *in6m; */ \
-do { \
- register struct in6_ifaddr *ia; \
- \
- IFP_TO_IA6((ifp), ia); \
- if (ia == NULL) \
- (in6m) = NULL; \
- else \
- for ((in6m) = ia->ia6_multiaddrs.lh_first; \
- (in6m) != NULL && \
- !IN6_ARE_ADDR_EQUAL(&(in6m)->in6m_addr, &(addr)); \
- (in6m) = in6m->in6m_entry.le_next) \
- continue; \
-} while (0)
-
-/*
- * Macro to step through all of the in6_multi records, one at a time.
- * The current position is remembered in "step", which the caller must
- * provide. IN6_FIRST_MULTI(), below, must be called to initialize "step"
- * and get the first record. Both macros return a NULL "in6m" when there
- * are no remaining records.
- */
-#define IN6_NEXT_MULTI(step, in6m) \
-/* struct in6_multistep step; */ \
-/* struct in6_multi *in6m; */ \
-do { \
- if (((in6m) = (step).i_in6m) != NULL) \
- (step).i_in6m = (in6m)->in6m_entry.le_next; \
- else \
- while ((step).i_ia != NULL) { \
- (in6m) = (step).i_ia->ia6_multiaddrs.lh_first; \
- (step).i_ia = (step).i_ia->ia_next; \
- if ((in6m) != NULL) { \
- (step).i_in6m = (in6m)->in6m_entry.le_next; \
- break; \
- } \
- } \
-} while (0)
-
-#define IN6_FIRST_MULTI(step, in6m) \
-/* struct in6_multistep step; */ \
-/* struct in6_multi *in6m */ \
-do { \
- (step).i_ia = in6_ifaddr; \
- (step).i_in6m = NULL; \
- IN6_NEXT_MULTI((step), (in6m)); \
-} while (0)
-
-#endif /* not FreeBSD3 */
-
int in6_ifinit __P((struct ifnet *,
struct in6_ifaddr *, struct sockaddr_in6 *, int));
struct in6_multi *in6_addmulti __P((struct in6_addr *, struct ifnet *,
@@ -621,12 +532,8 @@ void in6_ifscrub __P((struct ifnet *, struct in6_ifaddr *));
extern int in6_ifindex2scopeid __P((int));
extern int in6_mask2len __P((struct in6_addr *));
extern void in6_len2mask __P((struct in6_addr *, int));
-#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
int in6_control __P((struct socket *,
- u_long, caddr_t, struct ifnet *, struct proc *));
-#else
-int in6_control __P((struct socket *, u_long, caddr_t, struct ifnet *));
-#endif
+ u_long, caddr_t, struct ifnet *, struct proc *));
void in6_savemkludge __P((struct in6_ifaddr *));
void in6_setmaxmtu __P((void));
void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *));
diff --git a/sys/netinet6/ip6.h b/sys/netinet6/ip6.h
new file mode 100644
index 0000000..1dacb10
--- /dev/null
+++ b/sys/netinet6/ip6.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _NETINET6_IPV6_H_
+#define _NETINET6_IPV6_H_
+
+/*
+ * Definition for internet protocol version 6.
+ * RFC 2460
+ */
+
+struct ip6_hdr {
+ union {
+ struct ip6_hdrctl {
+ u_int32_t ip6_un1_flow; /* 20 bits of flow-ID */
+ u_int16_t ip6_un1_plen; /* payload length */
+ u_int8_t ip6_un1_nxt; /* next header */
+ u_int8_t ip6_un1_hlim; /* hop limit */
+ } ip6_un1;
+ u_int8_t ip6_un2_vfc; /* 4 bits version, 4 bits class */
+ } ip6_ctlun;
+ struct in6_addr ip6_src; /* source address */
+ struct in6_addr ip6_dst; /* destination address */
+};
+
+#define ip6_vfc ip6_ctlun.ip6_un2_vfc
+#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
+#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
+#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt
+#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim
+#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim
+
+#define IPV6_VERSION 0x60
+#define IPV6_VERSION_MASK 0xf0
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define IPV6_FLOWINFO_MASK 0x0fffffff /* flow info (28 bits) */
+#define IPV6_FLOWLABEL_MASK 0x000fffff /* flow label (20 bits) */
+#endif /* BIG_ENDIAN */
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define IPV6_FLOWINFO_MASK 0xffffff0f /* flow info (28 bits) */
+#define IPV6_FLOWLABEL_MASK 0xffff0f00 /* flow label (20 bits) */
+#endif /* LITTLE_ENDIAN */
+/* ECN bits proposed by Sally Floyd */
+#define IP6TOS_CE 0x01 /* congestion experienced */
+#define IP6TOS_ECT 0x02 /* ECN-capable transport */
+
+/*
+ * Extension Headers
+ */
+
+struct ip6_ext {
+ u_char ip6e_nxt;
+ u_char ip6e_len;
+};
+
+/* Hop-by-Hop options header */
+/* XXX should we pad it to force alignment on an 8-byte boundary? */
+struct ip6_hbh {
+ u_int8_t ip6h_nxt; /* next header */
+ u_int8_t ip6h_len; /* length in units of 8 octets */
+ /* followed by options */
+};
+
+/* Destination options header */
+/* XXX should we pad it to force alignment on an 8-byte boundary? */
+struct ip6_dest {
+ u_int8_t ip6d_nxt; /* next header */
+ u_int8_t ip6d_len; /* length in units of 8 octets */
+ /* followed by options */
+};
+
+/* Option types and related macros */
+#define IP6OPT_PAD1 0x00 /* 00 0 00000 */
+#define IP6OPT_PADN 0x01 /* 00 0 00001 */
+#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */
+#define IP6OPT_JUMBO_LEN 6
+#define IP6OPT_RTALERT 0x05 /* 00 0 00101 */
+#define IP6OPT_RTALERT_LEN 4
+#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */
+#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */
+#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */
+#define IP6OPT_MINLEN 2
+
+#define IP6OPT_TYPE(o) ((o) & 0xC0)
+#define IP6OPT_TYPE_SKIP 0x00
+#define IP6OPT_TYPE_DISCARD 0x40
+#define IP6OPT_TYPE_FORCEICMP 0x80
+#define IP6OPT_TYPE_ICMP 0xC0
+
+#define IP6OPT_MUTABLE 0x20
+
+/* Routing header */
+struct ip6_rthdr {
+ u_int8_t ip6r_nxt; /* next header */
+ u_int8_t ip6r_len; /* length in units of 8 octets */
+ u_int8_t ip6r_type; /* routing type */
+ u_int8_t ip6r_segleft; /* segments left */
+ /* followed by routing type specific data */
+};
+
+/* Type 0 Routing header */
+struct ip6_rthdr0 {
+ u_int8_t ip6r0_nxt; /* next header */
+ u_int8_t ip6r0_len; /* length in units of 8 octets */
+ u_int8_t ip6r0_type; /* always zero */
+ u_int8_t ip6r0_segleft; /* segments left */
+ u_int8_t ip6r0_reserved; /* reserved field */
+ u_int8_t ip6r0_slmap[3]; /* strict/loose bit map */
+ struct in6_addr ip6r0_addr[1]; /* up to 23 addresses */
+};
+
+/* Fragment header */
+struct ip6_frag {
+ u_int8_t ip6f_nxt; /* next header */
+ u_int8_t ip6f_reserved; /* reserved field */
+ u_int16_t ip6f_offlg; /* offset, reserved, and flag */
+ u_int32_t ip6f_ident; /* identification */
+};
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define IP6F_OFF_MASK 0xfff8 /* mask out offset from _offlg */
+#define IP6F_RESERVED_MASK 0x0006 /* reserved bits in ip6f_offlg */
+#define IP6F_MORE_FRAG 0x0001 /* more-fragments flag */
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+#define IP6F_OFF_MASK 0xf8ff /* mask out offset from _offlg */
+#define IP6F_RESERVED_MASK 0x0600 /* reserved bits in ip6f_offlg */
+#define IP6F_MORE_FRAG 0x0100 /* more-fragments flag */
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+/*
+ * Internet implementation parameters.
+ */
+#define IPV6_MAXHLIM 255 /* maximun hoplimit */
+#define IPV6_DEFHLIM 64 /* default hlim */
+#define IPV6_FRAGTTL 120 /* ttl for fragment packets, in slowtimo tick */
+#define IPV6_HLIMDEC 1 /* subtracted when forwaeding */
+
+#define IPV6_MMTU 1280 /* minimal MTU and reassembly. 1024 + 256 */
+#define IPV6_MAXPACKET 65535 /* ip6 max packet size without Jumbo payload*/
+
+/*
+ * IP6_EXTHDR_CHECK ensures that region between the IP6 header and the
+ * target header (including IPv6 itself, extension headers and
+ * TCP/UDP/ICMP6 headers) are continuous. KAME requires drivers
+ * to store incoming data into one internal mbuf or one or more external
+ * mbufs(never into two or more internal mbufs). Thus, the third case is
+ * supposed to never be matched but is prepared just in case.
+ */
+
+#define IP6_EXTHDR_CHECK(m, off, hlen, ret) \
+do { \
+ if ((m)->m_next != NULL) { \
+ if (((m)->m_flags & M_LOOP) && \
+ ((m)->m_len < (off) + (hlen)) && \
+ (((m) = m_pullup((m), (off) + (hlen))) == NULL)) { \
+ ip6stat.ip6s_exthdrtoolong++; \
+ return ret; \
+ } else if ((m)->m_flags & M_EXT) { \
+ if ((m)->m_len < (off) + (hlen)) { \
+ ip6stat.ip6s_exthdrtoolong++; \
+ m_freem(m); \
+ return ret; \
+ } \
+ } else { \
+ if ((m)->m_len < (off) + (hlen)) { \
+ ip6stat.ip6s_exthdrtoolong++; \
+ m_freem(m); \
+ return ret; \
+ } \
+ } \
+ } else { \
+ if ((m)->m_len < (off) + (hlen)) { \
+ ip6stat.ip6s_tooshort++; \
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); \
+ m_freem(m); \
+ return ret; \
+ } \
+ } \
+} while (0)
+
+#endif /* not _NETINET_IPV6_H_ */
diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c
new file mode 100644
index 0000000..0a4745b
--- /dev/null
+++ b/sys/netinet6/ip6_forward.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_ip6fw.h"
+#include "opt_inet.h"
+#include "opt_ipsec.h"
+#include "opt_key.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+#include <netinet6/nd6.h>
+
+#ifdef IPSEC_IPV6FWD
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif /* INET6 */
+#include <netkey/key.h>
+#ifdef KEY_DEBUG
+#include <netkey/key_debug.h>
+#ifdef INET6
+#include <netkey/key_debug6.h>
+#endif /* INET6 */
+#else
+#define DPRINTF(lev,arg)
+#define DDO(lev, stmt)
+#define DP(x, y, z)
+#endif /* KEY_DEBUG */
+#endif /* IPSEC_IPV6FWD */
+
+#ifdef IPV6FIREWALL
+#include <netinet6/ip6_fw.h>
+#endif
+
+#include <net/net_osdep.h>
+
+struct route_in6 ip6_forward_rt;
+
+/*
+ * Forward a packet. If some error occurs return the sender
+ * an icmp packet. Note we can't always generate a meaningful
+ * icmp message because icmp doesn't have a large enough repertoire
+ * of codes and types.
+ *
+ * If not forwarding, just drop the packet. This could be confusing
+ * if ipforwarding was zero but some routing protocol was advancing
+ * us as a gateway to somewhere. However, we must let the routing
+ * protocol deal with that.
+ *
+ */
+
+void
+ip6_forward(m, srcrt)
+ struct mbuf *m;
+ int srcrt;
+{
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ register struct sockaddr_in6 *dst;
+ register struct rtentry *rt;
+ int error, type = 0, code = 0;
+ struct mbuf *mcopy;
+#ifdef IPSEC_IPV6FWD
+ struct secpolicy *sp = NULL;
+#endif
+
+#ifdef IPSEC_IPV6FWD
+ /*
+ * Check AH/ESP integrity.
+ */
+ /*
+ * Don't increment ip6s_cantforward because this is the check
+ * before forwarding packet actually.
+ */
+ if (ipsec6_in_reject(m, NULL)) {
+ ipsec6stat.in_polvio++;
+ m_freem(m);
+ return;
+ }
+#endif /*IPSEC_IPV6FWD*/
+
+ if (m->m_flags & (M_BCAST|M_MCAST) ||
+ in6_canforward(&ip6->ip6_src, &ip6->ip6_dst) == 0) {
+ ip6stat.ip6s_cantforward++;
+ ip6stat.ip6s_badscope++;
+ /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
+ if (ip6_log_time + ip6_log_interval < time_second) {
+ char addr[INET6_ADDRSTRLEN];
+ ip6_log_time = time_second;
+ strncpy(addr, ip6_sprintf(&ip6->ip6_src), sizeof(addr));
+ log(LOG_DEBUG,
+ "cannot forward "
+ "from %s to %s nxt %d received on %s\n",
+ addr, ip6_sprintf(&ip6->ip6_dst),
+ ip6->ip6_nxt,
+ if_name(m->m_pkthdr.rcvif));
+ }
+ m_freem(m);
+ return;
+ }
+
+ if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
+ /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
+ icmp6_error(m, ICMP6_TIME_EXCEEDED,
+ ICMP6_TIME_EXCEED_TRANSIT, 0);
+ return;
+ }
+ ip6->ip6_hlim -= IPV6_HLIMDEC;
+
+#ifdef IPSEC_IPV6FWD
+ /* get a security policy for this packet */
+ sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
+ if (sp == NULL) {
+ ipsec6stat.out_inval++;
+ ip6stat.ip6s_cantforward++;
+ /* XXX: any icmp ? */
+ m_freem(m);
+ return;
+ }
+
+ error = 0;
+
+ /* check policy */
+ switch (sp->policy) {
+ case IPSEC_POLICY_DISCARD:
+ /*
+ * This packet is just discarded.
+ */
+ ipsec6stat.out_polvio++;
+ ip6stat.ip6s_cantforward++;
+ key_freesp(sp);
+ /* XXX: any icmp ? */
+ m_freem(m);
+ return;
+
+ case IPSEC_POLICY_BYPASS:
+ case IPSEC_POLICY_NONE:
+ /* no need to do IPsec. */
+ key_freesp(sp);
+ goto skip_ipsec;
+
+ case IPSEC_POLICY_IPSEC:
+ if (sp->req == NULL) {
+ /* XXX should be panic ? */
+ printf("ip6_forward: No IPsec request specified.\n");
+ ip6stat.ip6s_cantforward++;
+ key_freesp(sp);
+ /* XXX: any icmp ? */
+ m_freem(m);
+ return;
+ }
+ /* do IPsec */
+ break;
+
+ case IPSEC_POLICY_ENTRUST:
+ default:
+ /* should be panic ?? */
+ printf("ip6_forward: Invalid policy found. %d\n", sp->policy);
+ key_freesp(sp);
+ goto skip_ipsec;
+ }
+
+ {
+ struct ipsec_output_state state;
+
+ /*
+ * All the extension headers will become inaccessible
+ * (since they can be encrypted).
+ * Don't panic, we need no more updates to extension headers
+ * on inner IPv6 packet (since they are now encapsulated).
+ *
+ * IPv6 [ESP|AH] IPv6 [extension headers] payload
+ */
+ bzero(&state, sizeof(state));
+ state.m = m;
+ state.ro = NULL; /* update at ipsec6_output_tunnel() */
+ state.dst = NULL; /* update at ipsec6_output_tunnel() */
+
+ error = ipsec6_output_tunnel(&state, sp, 0);
+
+ m = state.m;
+ /* XXX allocate a route (ro, dst) again later */
+ key_freesp(sp);
+
+ if (error) {
+ /* mbuf is already reclaimed in ipsec6_output_tunnel. */
+ switch (error) {
+ case EHOSTUNREACH:
+ case ENETUNREACH:
+ case EMSGSIZE:
+ case ENOBUFS:
+ case ENOMEM:
+ break;
+ default:
+ printf("ip6_output (ipsec): error code %d\n", error);
+ /*fall through*/
+ case ENOENT:
+ /* don't show these error codes to the user */
+ break;
+ }
+ ip6stat.ip6s_cantforward++;
+ /* XXX: any icmp ? */
+ m_freem(m);
+ return;
+ }
+ }
+ skip_ipsec:
+#endif /* IPSEC_IPV6FWD */
+
+ dst = &ip6_forward_rt.ro_dst;
+ if (!srcrt) {
+ /*
+ * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst
+ */
+ if (ip6_forward_rt.ro_rt == 0 ||
+ (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0) {
+ if (ip6_forward_rt.ro_rt) {
+ RTFREE(ip6_forward_rt.ro_rt);
+ ip6_forward_rt.ro_rt = 0;
+ }
+ /* this probably fails but give it a try again */
+ rtalloc_ign((struct route *)&ip6_forward_rt,
+ RTF_PRCLONING);
+ }
+
+ if (ip6_forward_rt.ro_rt == 0) {
+ ip6stat.ip6s_noroute++;
+ /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
+ icmp6_error(m, ICMP6_DST_UNREACH,
+ ICMP6_DST_UNREACH_NOROUTE, 0);
+ return;
+ }
+ } else if ((rt = ip6_forward_rt.ro_rt) == 0 ||
+ !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) {
+ if (ip6_forward_rt.ro_rt) {
+ RTFREE(ip6_forward_rt.ro_rt);
+ ip6_forward_rt.ro_rt = 0;
+ }
+ bzero(dst, sizeof(*dst));
+ dst->sin6_len = sizeof(struct sockaddr_in6);
+ dst->sin6_family = AF_INET6;
+ dst->sin6_addr = ip6->ip6_dst;
+
+ rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
+ if (ip6_forward_rt.ro_rt == 0) {
+ ip6stat.ip6s_noroute++;
+ /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
+ icmp6_error(m, ICMP6_DST_UNREACH,
+ ICMP6_DST_UNREACH_NOROUTE, 0);
+ return;
+ }
+ }
+ rt = ip6_forward_rt.ro_rt;
+ if (m->m_pkthdr.len > rt->rt_ifp->if_mtu){
+ in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
+ icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, rt->rt_ifp->if_mtu);
+ return;
+ }
+
+ if (rt->rt_flags & RTF_GATEWAY)
+ dst = (struct sockaddr_in6 *)rt->rt_gateway;
+ /*
+ * Save at most 528 bytes of the packet in case
+ * we need to generate an ICMP6 message to the src.
+ * Thanks to M_EXT, in most cases copy will not occur.
+ */
+ mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN));
+
+ /*
+ * If we are to forward the packet using the same interface
+ * as one we got the packet from, perhaps we should send a redirect
+ * to sender to shortcut a hop.
+ * Only send redirect if source is sending directly to us,
+ * and if packet was not source routed (or has any options).
+ * Also, don't send redirect if forwarding using a route
+ * modified by a redirect.
+ */
+ if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt &&
+ (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0)
+ type = ND_REDIRECT;
+
+#ifdef IPV6FIREWALL
+ /*
+ * Check with the firewall...
+ */
+ if (ip6_fw_chk_ptr) {
+ u_short port = 0;
+ /* If ipfw says divert, we have to just drop packet */
+ if ((*ip6_fw_chk_ptr)(&ip6, rt->rt_ifp, &port, &m)) {
+ m_freem(m);
+ goto freecopy;
+ }
+ if (!m)
+ goto freecopy;
+ }
+#endif
+
+ error = nd6_output(rt->rt_ifp, m, dst, rt);
+ if (error) {
+ in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard);
+ ip6stat.ip6s_cantforward++;
+ } else {
+ ip6stat.ip6s_forward++;
+ in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward);
+ if (type)
+ ip6stat.ip6s_redirectsent++;
+ else {
+ if (mcopy)
+ goto freecopy;
+ }
+ }
+ if (mcopy == NULL)
+ return;
+
+ switch (error) {
+ case 0:
+ if (type == ND_REDIRECT) {
+ icmp6_redirect_output(mcopy, rt);
+ return;
+ }
+ goto freecopy;
+
+ case EMSGSIZE:
+ /* xxx MTU is constant in PPP? */
+ goto freecopy;
+
+ case ENOBUFS:
+ /* Tell source to slow down like source quench in IP? */
+ goto freecopy;
+
+ case ENETUNREACH: /* shouldn't happen, checked above */
+ case EHOSTUNREACH:
+ case ENETDOWN:
+ case EHOSTDOWN:
+ default:
+ type = ICMP6_DST_UNREACH;
+ code = ICMP6_DST_UNREACH_ADDR;
+ break;
+ }
+ icmp6_error(mcopy, type, code, 0);
+ return;
+
+ freecopy:
+ m_freem(mcopy);
+ return;
+}
diff --git a/sys/netinet6/ip6_fw.h b/sys/netinet6/ip6_fw.h
new file mode 100644
index 0000000..a356ac3
--- /dev/null
+++ b/sys/netinet6/ip6_fw.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 1993 Daniel Boulet
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * $Id: ip6_fw.h,v 1.1 1999/08/06 14:10:09 itojun Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _IP6_FW_H
+#define _IP6_FW_H
+
+#include <net/if.h>
+
+/*
+ * This union structure identifies an interface, either explicitly
+ * by name or implicitly by IP address. The flags IP_FW_F_IIFNAME
+ * and IP_FW_F_OIFNAME say how to interpret this structure. An
+ * interface unit number of -1 matches any unit number, while an
+ * IP address of 0.0.0.0 indicates matches any interface.
+ *
+ * The receive and transmit interfaces are only compared against the
+ * the packet if the corresponding bit (IP_FW_F_IIFACE or IP_FW_F_OIFACE)
+ * is set. Note some packets lack a receive or transmit interface
+ * (in which case the missing "interface" never matches).
+ */
+
+union ip6_fw_if {
+ struct in6_addr fu_via_ip6; /* Specified by IPv6 address */
+ struct { /* Specified by interface name */
+#define FW_IFNLEN IFNAMSIZ
+ char name[FW_IFNLEN];
+ short unit; /* -1 means match any unit */
+ } fu_via_if;
+};
+
+/*
+ * Format of an IP firewall descriptor
+ *
+ * fw_src, fw_dst, fw_smsk, fw_dmsk are always stored in network byte order.
+ * fw_flg and fw_n*p are stored in host byte order (of course).
+ * Port numbers are stored in HOST byte order.
+ * Warning: setsockopt() will fail if sizeof(struct ip_fw) > MLEN (108)
+ */
+
+struct ip6_fw {
+ u_long fw_pcnt,fw_bcnt; /* Packet and byte counters */
+ struct in6_addr fw_src, fw_dst; /* Source and destination IPv6 addr */
+ /* Mask for src and dest IPv6 addr */
+ struct in6_addr fw_smsk, fw_dmsk;
+ u_short fw_number; /* Rule number */
+ u_short fw_flg; /* Flags word */
+#define IPV6_FW_MAX_PORTS 10 /* A reasonable maximum */
+ /* Array of port numbers to match */
+ u_short fw_pts[IPV6_FW_MAX_PORTS];
+ u_char fw_ip6opt,fw_ip6nopt; /* IPv6 options set/unset */
+ u_char fw_tcpf,fw_tcpnf; /* TCP flags set/unset */
+#define IPV6_FW_ICMPTYPES_DIM (32 / (sizeof(unsigned) * 8))
+ /* ICMP types bitmap */
+ unsigned fw_icmp6types[IPV6_FW_ICMPTYPES_DIM];
+ long timestamp; /* timestamp (tv_sec) of last match */
+ /* Incoming and outgoing interfaces */
+ union ip6_fw_if fw_in_if, fw_out_if;
+ union {
+ u_short fu_divert_port; /* Divert/tee port (options IP6DIVERT) */
+ u_short fu_skipto_rule; /* SKIPTO command rule number */
+ u_short fu_reject_code; /* REJECT response code */
+ } fw_un;
+ u_char fw_prot; /* IPv6 protocol */
+ u_char fw_nports; /* N'of src ports and # of dst ports */
+ /* in ports array (dst ports follow */
+ /* src ports; max of 10 ports in all; */
+ /* count of 0 means match all ports) */
+};
+
+#define IPV6_FW_GETNSRCP(rule) ((rule)->fw_nports & 0x0f)
+#define IPV6_FW_SETNSRCP(rule, n) do { \
+ (rule)->fw_nports &= ~0x0f; \
+ (rule)->fw_nports |= (n); \
+ } while (0)
+#define IPV6_FW_GETNDSTP(rule) ((rule)->fw_nports >> 4)
+#define IPV6_FW_SETNDSTP(rule, n) do { \
+ (rule)->fw_nports &= ~0xf0; \
+ (rule)->fw_nports |= (n) << 4;\
+ } while (0)
+
+#define fw_divert_port fw_un.fu_divert_port
+#define fw_skipto_rule fw_un.fu_skipto_rule
+#define fw_reject_code fw_un.fu_reject_code
+
+struct ip6_fw_chain {
+ LIST_ENTRY(ip6_fw_chain) chain;
+ struct ip6_fw *rule;
+};
+
+/*
+ * Values for "flags" field .
+ */
+#define IPV6_FW_F_IN 0x0001 /* Check inbound packets */
+#define IPV6_FW_F_OUT 0x0002 /* Check outbound packets */
+#define IPV6_FW_F_IIFACE 0x0004 /* Apply inbound interface test */
+#define IPV6_FW_F_OIFACE 0x0008 /* Apply outbound interface test */
+
+#define IPV6_FW_F_COMMAND 0x0070 /* Mask for type of chain entry: */
+#define IPV6_FW_F_DENY 0x0000 /* This is a deny rule */
+#define IPV6_FW_F_REJECT 0x0010 /* Deny and send a response packet */
+#define IPV6_FW_F_ACCEPT 0x0020 /* This is an accept rule */
+#define IPV6_FW_F_COUNT 0x0030 /* This is a count rule */
+#define IPV6_FW_F_DIVERT 0x0040 /* This is a divert rule */
+#define IPV6_FW_F_TEE 0x0050 /* This is a tee rule */
+#define IPV6_FW_F_SKIPTO 0x0060 /* This is a skipto rule */
+
+#define IPV6_FW_F_PRN 0x0080 /* Print if this rule matches */
+
+#define IPV6_FW_F_SRNG 0x0100 /* The first two src ports are a min *
+ * and max range (stored in host byte *
+ * order). */
+
+#define IPV6_FW_F_DRNG 0x0200 /* The first two dst ports are a min *
+ * and max range (stored in host byte *
+ * order). */
+
+/* In interface by name/unit (not IP) */
+#define IPV6_FW_F_IIFNAME 0x0400
+/* Out interface by name/unit (not IP) */
+#define IPV6_FW_F_OIFNAME 0x0800
+
+#define IPV6_FW_F_INVSRC 0x1000 /* Invert sense of src check */
+#define IPV6_FW_F_INVDST 0x2000 /* Invert sense of dst check */
+
+#define IPV6_FW_F_FRAG 0x4000 /* Fragment */
+
+#define IPV6_FW_F_ICMPBIT 0x8000 /* ICMP type bitmap is valid */
+
+#define IPV6_FW_F_MASK 0xFFFF /* All possible flag bits mask */
+
+/*
+ * For backwards compatibility with rules specifying "via iface" but
+ * not restricted to only "in" or "out" packets, we define this combination
+ * of bits to represent this configuration.
+ */
+
+#define IF6_FW_F_VIAHACK (IPV6_FW_F_IN|IPV6_FW_F_OUT|IPV6_FW_F_IIFACE|\
+ IPV6_FW_F_OIFACE)
+
+/*
+ * Definitions for REJECT response codes.
+ * Values less than 256 correspond to ICMP unreachable codes.
+ */
+#define IPV6_FW_REJECT_RST 0x0100 /* TCP packets: send RST */
+
+/*
+ * Definitions for IPv6 option names.
+ */
+#define IPV6_FW_IP6OPT_HOPOPT 0x01
+#define IPV6_FW_IP6OPT_ROUTE 0x02
+#define IPV6_FW_IP6OPT_FRAG 0x04
+#define IPV6_FW_IP6OPT_ESP 0x08
+#define IPV6_FW_IP6OPT_AH 0x10
+#define IPV6_FW_IP6OPT_NONXT 0x20
+#define IPV6_FW_IP6OPT_OPTS 0x40
+
+/*
+ * Definitions for TCP flags.
+ */
+#define IPV6_FW_TCPF_FIN TH_FIN
+#define IPV6_FW_TCPF_SYN TH_SYN
+#define IPV6_FW_TCPF_RST TH_RST
+#define IPV6_FW_TCPF_PSH TH_PUSH
+#define IPV6_FW_TCPF_ACK TH_ACK
+#define IPV6_FW_TCPF_URG TH_URG
+#define IPV6_FW_TCPF_ESTAB 0x40
+
+/*
+ * Main firewall chains definitions and global var's definitions.
+ */
+#ifdef _KERNEL
+
+/*
+ * Function definitions.
+ */
+void ip6_fw_init(void);
+
+/* Firewall hooks */
+struct ip6_hdr;
+typedef int ip6_fw_chk_t __P((struct ip6_hdr**, struct ifnet*,
+ u_short *, struct mbuf**));
+typedef int ip6_fw_ctl_t __P((int, struct mbuf**));
+extern ip6_fw_chk_t *ip6_fw_chk_ptr;
+extern ip6_fw_ctl_t *ip6_fw_ctl_ptr;
+
+#endif /* _KERNEL */
+
+#endif /* _IP6_FW_H */
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
new file mode 100644
index 0000000..dc12b0f
--- /dev/null
+++ b/sys/netinet6/ip6_input.c
@@ -0,0 +1,1016 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_input.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include "opt_ip6fw.h"
+#include "opt_inet.h"
+#include "opt_ipsec.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <net/netisr.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#ifdef INET
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#endif /*INET*/
+#include <netinet/in_pcb.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+#include <netinet6/in6_ifattach.h>
+#include <netinet6/nd6.h>
+#include <netinet6/in6_prefix.h>
+
+#ifdef IPV6FIREWALL
+#include <netinet6/ip6_fw.h>
+#endif
+
+#ifdef ALTQ
+#include <netinet/altq_cdnr.h>
+#endif
+
+#include <netinet6/ip6protosw.h>
+
+/* we need it for NLOOP. */
+#include "loop.h"
+/* #include "faith.h" */
+
+/* #include "gif.h" */
+
+#include <net/net_osdep.h>
+
+extern struct domain inet6domain;
+extern struct ip6protosw inet6sw[];
+
+u_char ip6_protox[IPPROTO_MAX];
+static int ip6qmaxlen = IFQ_MAXLEN;
+struct in6_ifaddr *in6_ifaddr;
+struct ifqueue ip6intrq;
+
+int ip6_forward_srcrt; /* XXX */
+int ip6_sourcecheck; /* 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
+
+struct ip6stat ip6stat;
+
+static void ip6_init2 __P((void *));
+
+static int ip6_hopopts_input __P((u_int32_t *, u_int32_t *, struct mbuf **, int *));
+
+#if defined(PTR)
+extern int ip6_protocol_tr;
+
+int ptr_in6 __P((struct mbuf *, struct mbuf **));
+extern void ip_forward __P((struct mbuf *, int));
+#endif
+
+/*
+ * IP6 initialization: fill in IP6 protocol switch table.
+ * All protocols not implemented in kernel go to raw IP6 protocol handler.
+ */
+void
+ip6_init()
+{
+ register struct ip6protosw *pr;
+ register int i;
+ struct timeval tv;
+
+ pr = (struct ip6protosw *)pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW);
+ if (pr == 0)
+ panic("ip6_init");
+ for (i = 0; i < IPPROTO_MAX; i++)
+ ip6_protox[i] = pr - inet6sw;
+ for (pr = (struct ip6protosw *)inet6domain.dom_protosw;
+ pr < (struct ip6protosw *)inet6domain.dom_protoswNPROTOSW; pr++)
+ if (pr->pr_domain->dom_family == PF_INET6 &&
+ pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
+ ip6_protox[pr->pr_protocol] = pr - inet6sw;
+ ip6intrq.ifq_maxlen = ip6qmaxlen;
+ nd6_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;
+}
+
+static void
+ip6_init2(dummy)
+ void *dummy;
+{
+ int i;
+ int ret;
+
+ /* get EUI64 from somewhere */
+ ret = in6_ifattach_getifid(NULL);
+
+ /*
+ * to route local address of p2p link to loopback,
+ * assign loopback address first.
+ */
+ for (i = 0; i < NLOOP; i++)
+ in6_ifattach(&loif[i], IN6_IFT_LOOP, NULL, 0);
+
+ /* attach pseudo interfaces */
+ if (ret == 0)
+ in6_ifattach_p2p();
+
+ /* nd6_timer_init */
+ timeout(nd6_timer, (caddr_t)0, hz);
+ /* router renumbering prefix list maintenance */
+ timeout(in6_rr_timer, (caddr_t)0, hz);
+}
+
+/* cheat */
+SYSINIT(netinet6init2, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, ip6_init2, NULL);
+
+/*
+ * IP6 input interrupt handling. Just pass the packet to ip6_input.
+ */
+void
+ip6intr()
+{
+ int s;
+ struct mbuf *m;
+
+ for (;;) {
+ s = splimp();
+ IF_DEQUEUE(&ip6intrq, m);
+ splx(s);
+ if (m == 0)
+ return;
+ ip6_input(m);
+ }
+}
+
+NETISR_SET(NETISR_IPV6, ip6intr);
+
+extern struct route_in6 ip6_forward_rt;
+
+void
+ip6_input(m)
+ struct mbuf *m;
+{
+ struct ip6_hdr *ip6;
+ int off = sizeof(struct ip6_hdr), nest;
+ u_int32_t plen;
+ u_int32_t rtalert = ~0;
+ int nxt, ours = 0;
+ struct ifnet *deliverifp = NULL;
+
+#ifdef IPSEC
+ /*
+ * should the inner packet be considered authentic?
+ * see comment in ah4_input().
+ */
+ if (m) {
+ m->m_flags &= ~M_AUTHIPHDR;
+ m->m_flags &= ~M_AUTHIPDGM;
+ }
+#endif
+
+ /*
+ * mbuf statistics by kazu
+ */
+ if (m->m_flags & M_EXT) {
+ if (m->m_next)
+ ip6stat.ip6s_mext2m++;
+ else
+ ip6stat.ip6s_mext1++;
+ } else {
+ 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)
+ ip6stat.ip6s_m2m[m->m_pkthdr.rcvif->if_index]++;
+ else
+ ip6stat.ip6s_m2m[0]++;
+ } else
+ ip6stat.ip6s_m1++;
+ }
+
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_receive);
+ ip6stat.ip6s_total++;
+
+ IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), /*nothing*/);
+
+ if (m->m_len < sizeof(struct ip6_hdr)) {
+ struct ifnet *inifp;
+ inifp = m->m_pkthdr.rcvif;
+ if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == 0) {
+ ip6stat.ip6s_toosmall++;
+ in6_ifstat_inc(inifp, ifs6_in_hdrerr);
+ return;
+ }
+ }
+
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
+ ip6stat.ip6s_badvers++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr);
+ goto bad;
+ }
+
+ ip6stat.ip6s_nxthist[ip6->ip6_nxt]++;
+
+#ifdef IPV6FIREWALL
+ /*
+ * Check with the firewall...
+ */
+ if (ip6_fw_chk_ptr) {
+ u_short port = 0;
+ /* If ipfw says divert, we have to just drop packet */
+ /* use port as a dummy argument */
+ if ((*ip6_fw_chk_ptr)(&ip6, NULL, &port, &m)) {
+ m_freem(m);
+ m = NULL;
+ }
+ if (!m)
+ return;
+ }
+#endif
+
+#ifdef ALTQ
+ if (altq_input != NULL && (*altq_input)(m, AF_INET6) == 0) {
+ /* packet is dropped by traffic conditioner */
+ return;
+ }
+#endif
+
+ /*
+ * Scope check
+ */
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src) ||
+ IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) {
+ 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)) {
+ if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) {
+ ours = 1;
+ deliverifp = m->m_pkthdr.rcvif;
+ goto hbhcheck;
+ } else {
+ ip6stat.ip6s_badscope++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr);
+ goto bad;
+ }
+ }
+
+ if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) {
+ if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) {
+ ours = 1;
+ deliverifp = m->m_pkthdr.rcvif;
+ goto hbhcheck;
+ }
+ } else {
+ 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 defined(PTR)
+ /*
+ *
+ */
+ if (ip6_protocol_tr)
+ {
+ struct mbuf *m1 = NULL;
+
+ switch (ptr_in6(m, &m1))
+ {
+ case IPPROTO_IP: goto mcastcheck;
+ case IPPROTO_IPV4: ip_forward(m1, 0); break;
+ case IPPROTO_IPV6: ip6_forward(m1, 0); break;
+ case IPPROTO_MAX: /* discard this packet */
+ default:
+ }
+
+ if (m != m1)
+ m_freem(m);
+
+ return;
+ }
+
+ mcastcheck:
+#endif
+
+ /*
+ * Multicast check
+ */
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ struct in6_multi *in6m = 0;
+
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mcast);
+ /*
+ * See if we belong to the destination multicast group on the
+ * arrival interface.
+ */
+ IN6_LOOKUP_MULTI(ip6->ip6_dst, m->m_pkthdr.rcvif, in6m);
+ if (in6m)
+ ours = 1;
+ else {
+ ip6stat.ip6s_notmember++;
+ ip6stat.ip6s_cantforward++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
+ goto bad;
+ }
+ deliverifp = m->m_pkthdr.rcvif;
+ goto hbhcheck;
+ }
+
+ /*
+ * Unicast check
+ */
+ if (ip6_forward_rt.ro_rt == 0 ||
+ !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst,
+ &ip6_forward_rt.ro_dst.sin6_addr)) {
+ if (ip6_forward_rt.ro_rt) {
+ RTFREE(ip6_forward_rt.ro_rt);
+ ip6_forward_rt.ro_rt = 0;
+ }
+ 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;
+
+ rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
+ }
+
+#define rt6_key(r) ((struct sockaddr_in6 *)((r)->rt_nodes->rn_key))
+
+ /*
+ * Accept the packet if the forwarding interface to the destination
+ * according to the routing table is the loopback interface,
+ * unless the associated route has a gateway.
+ * Note that this approach causes to accept a packet if there is a
+ * 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.
+ */
+ if (ip6_forward_rt.ro_rt &&
+ (ip6_forward_rt.ro_rt->rt_flags &
+ (RTF_HOST|RTF_GATEWAY)) == RTF_HOST &&
+ /*
+ * The comparison of the destination and the key of the rtentry
+ * has already done through looking up the routing table,
+ * so no need to do such a comparison here again.
+ */
+ 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;
+ /* packet to tentative address must not be received */
+ if (ia6->ia6_flags & IN6_IFF_ANYCAST)
+ m->m_flags |= M_ANYCAST6;
+ if (!(ia6->ia6_flags & IN6_IFF_NOTREADY)) {
+ /* this interface is ready */
+ ours = 1;
+ deliverifp = ia6->ia_ifp; /* correct? */
+ goto hbhcheck;
+ } else {
+ /* this interface is not ready, fall through */
+ }
+ }
+
+ /*
+ * FAITH(Firewall Aided Internet Translator)
+ */
+#if defined(NFAITH) && 0 < NFAITH
+ if (ip6_keepfaith) {
+ if (ip6_forward_rt.ro_rt && ip6_forward_rt.ro_rt->rt_ifp
+ && ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_FAITH) {
+ /* XXX do we need more sanity checks? */
+ ours = 1;
+ deliverifp = ip6_forward_rt.ro_rt->rt_ifp; /*faith*/
+ goto hbhcheck;
+ }
+ }
+#endif
+
+ /*
+ * Now there is no reason to process the packet if it's not our own
+ * and we're not a router.
+ */
+ if (!ip6_forwarding) {
+ ip6stat.ip6s_cantforward++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
+ goto bad;
+ }
+
+ hbhcheck:
+ /*
+ * 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.
+ */
+ plen = (u_int32_t)ntohs(ip6->ip6_plen);
+ if (ip6->ip6_nxt == IPPROTO_HOPOPTS) {
+ if (ip6_hopopts_input(&plen, &rtalert, &m, &off)) {
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
+ return; /* m have already been freed */
+ }
+ /* adjust pointer */
+ ip6 = mtod(m, struct ip6_hdr *);
+ nxt = ((struct ip6_hbh *)(ip6 + 1))->ip6h_nxt;
+
+ /*
+ * accept the packet if a router alert option is included
+ * and we act as an IPv6 router.
+ */
+ if (rtalert != ~0 && ip6_forwarding)
+ ours = 1;
+ } else
+ nxt = ip6->ip6_nxt;
+
+ /*
+ * Check that the amount of data in the buffers
+ * is as at least much as the IPv6 header would have us expect.
+ * Trim mbufs if longer than we expect.
+ * Drop packet if shorter than we expect.
+ */
+ if (m->m_pkthdr.len - sizeof(struct ip6_hdr) < plen) {
+ ip6stat.ip6s_tooshort++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated);
+ goto bad;
+ }
+ if (m->m_pkthdr.len > sizeof(struct ip6_hdr) + plen) {
+ if (m->m_len == m->m_pkthdr.len) {
+ m->m_len = sizeof(struct ip6_hdr) + plen;
+ m->m_pkthdr.len = sizeof(struct ip6_hdr) + plen;
+ } else
+ m_adj(m, sizeof(struct ip6_hdr) + plen - m->m_pkthdr.len);
+ }
+
+ /*
+ * Forward if desirable.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ if (!ours) {
+ m_freem(m);
+ return;
+ }
+ } else if (!ours) {
+ ip6_forward(m, 0);
+ return;
+ }
+
+ /*
+ * Tell launch routine the next header
+ */
+#if defined(__NetBSD__) && defined(IFA_STATS)
+ if (IFA_STATS && deliverifp != NULL) {
+ struct in6_ifaddr *ia6;
+ ip6 = mtod(m, struct ip6_hdr *);
+ ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst);
+ if (ia6)
+ ia6->ia_ifa.ifa_data.ifad_inbytes += m->m_pkthdr.len;
+ }
+#endif
+ 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++;
+ goto bad;
+ }
+
+ /*
+ * protection against faulty packet - there should be
+ * more sanity checks in header chain processing.
+ */
+ if (m->m_pkthdr.len < off) {
+ ip6stat.ip6s_tooshort++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated);
+ goto bad;
+ }
+
+ nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt);
+ }
+ return;
+ bad:
+ m_freem(m);
+}
+
+/*
+ * Hop-by-Hop options header processing. If a valid jumbo payload option is
+ * included, the real payload length will be stored in plenp.
+ */
+static int
+ip6_hopopts_input(plenp, rtalertp, mp, offp)
+ u_int32_t *plenp;
+ u_int32_t *rtalertp; /* XXX: should be stored more smart way */
+ struct mbuf **mp;
+ int *offp;
+{
+ register struct mbuf *m = *mp;
+ int off = *offp, hbhlen;
+ struct ip6_hbh *hbh;
+ u_int8_t *opt;
+
+ /* validation of the length of the header */
+ 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);
+ off += hbhlen;
+ 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),
+ hbhlen, rtalertp, plenp) < 0)
+ return(-1);
+
+ *offp = off;
+ *mp = m;
+ return(0);
+}
+
+/*
+ * Search header for all Hop-by-hop options and process each option.
+ * 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().
+ */
+int
+ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
+ struct mbuf *m;
+ u_int8_t *opthead;
+ int hbhlen;
+ u_int32_t *rtalertp;
+ u_int32_t *plenp;
+{
+ struct ip6_hdr *ip6;
+ int optlen = 0;
+ u_int8_t *opt = opthead;
+ u_int16_t rtalert_val;
+
+ 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:
+ /* 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));
+ optlen = IP6OPT_JUMBO_LEN;
+
+ bcopy(opt + 2, plenp, sizeof(*plenp));
+ *plenp = htonl(*plenp);
+ if (*plenp <= IPV6_MAXPACKET) {
+ /*
+ * jumbo payload length must be larger
+ * than 65535
+ */
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ sizeof(struct ip6_hdr) +
+ sizeof(struct ip6_hbh) +
+ opt + 2 - opthead);
+ return(-1);
+ }
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ if (ip6->ip6_plen) {
+ /*
+ * IPv6 packets that have non 0 payload length
+ * must not contain a jumbo paylod option.
+ */
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ sizeof(struct ip6_hdr) +
+ sizeof(struct ip6_hbh) +
+ opt - opthead);
+ return(-1);
+ }
+ 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;
+ }
+ }
+
+ return(0);
+
+ bad:
+ m_freem(m);
+ return(-1);
+}
+
+/*
+ * Unknown option processing.
+ * The third argument `off' is the offset from the IPv6 header to the option,
+ * which is necessary if the IPv6 header the and option header and IPv6 header
+ * is not continuous in order to return an ICMPv6 error.
+ */
+int
+ip6_unknown_opt(optp, m, off)
+ u_int8_t *optp;
+ struct mbuf *m;
+ int 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);
+ }
+
+ m_freem(m); /* XXX: NOTREACHED */
+ return(-1);
+}
+
+/*
+ * Create the "control" list for this pcb
+ */
+void
+ip6_savecontrol(in6p, mp, ip6, m)
+ register struct inpcb *in6p;
+ register struct mbuf **mp;
+ register struct ip6_hdr *ip6;
+ register struct mbuf *m;
+{
+ struct proc *p = curproc; /* XXX */
+ int privileged;
+
+ privileged = 0;
+ if (p && !suser(p))
+ privileged++;
+
+#ifdef SO_TIMESTAMP
+ if (in6p->in6p_socket->so_options & SO_TIMESTAMP) {
+ struct timeval tv;
+
+ microtime(&tv);
+ *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
+ SCM_TIMESTAMP, SOL_SOCKET);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+#endif
+ if (in6p->in6p_flags & IN6P_RECVDSTADDR) {
+ *mp = sbcreatecontrol((caddr_t) &ip6->ip6_dst,
+ sizeof(struct in6_addr), IPV6_RECVDSTADDR,
+ IPPROTO_IPV6);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+
+ /* RFC 2292 sec. 5 */
+ if (in6p->in6p_flags & IN6P_PKTINFO) {
+ struct in6_pktinfo pi6;
+ bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr));
+ if (IN6_IS_SCOPE_LINKLOCAL(&pi6.ipi6_addr))
+ pi6.ipi6_addr.s6_addr16[1] = 0;
+ pi6.ipi6_ifindex = (m && m->m_pkthdr.rcvif)
+ ? m->m_pkthdr.rcvif->if_index
+ : 0;
+ *mp = sbcreatecontrol((caddr_t) &pi6,
+ sizeof(struct in6_pktinfo), IPV6_PKTINFO,
+ IPPROTO_IPV6);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+ if (in6p->in6p_flags & IN6P_HOPLIMIT) {
+ 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
+ * for the option, but it might be too strict, since there might
+ * 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) {
+ /*
+ * Check if a hop-by-hop options header is contatined in the
+ * received packet, and if so, store the options as ancillary
+ * data. Note that a hop-by-hop options header must be
+ * just after the IPv6 header, which fact is assured through
+ * the IPv6 input processing.
+ */
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ if (ip6->ip6_nxt == IPPROTO_HOPOPTS) {
+ struct ip6_hbh *hbh = (struct ip6_hbh *)(ip6 + 1);
+
+ /*
+ * 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...
+ */
+ *mp = sbcreatecontrol((caddr_t)hbh,
+ (hbh->ip6h_len + 1) << 3,
+ IPV6_HOPOPTS, IPPROTO_IPV6);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+ }
+
+ /* IPV6_DSTOPTS and IPV6_RTHDR socket options */
+ if (in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDR)) {
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr);;
+
+ /*
+ * Search for destination options headers or routing
+ * header(s) through the header chain, and stores each
+ * header as ancillary data.
+ * 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 =
+ (struct ip6_ext *)(mtod(m, caddr_t) + off);
+
+ 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,
+ (ip6e->ip6e_len + 1) << 3,
+ 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,
+ (ip6e->ip6e_len + 1) << 3,
+ 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. */
+ if (nxt == IPPROTO_AH)
+ off += (ip6e->ip6e_len + 2) << 2;
+ else
+ off += (ip6e->ip6e_len + 1) << 3;
+ nxt = ip6e->ip6e_nxt;
+ }
+ loopend:
+ }
+ if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) {
+ /* to be done */
+ }
+ if ((in6p->in6p_flags & IN6P_DSTOPTS) && privileged) {
+ /* to be done */
+ }
+ /* IN6P_RTHDR - to be done */
+}
+
+/*
+ * Get pointer to the previous header followed by the header
+ * currently processed.
+ * XXX: This function supposes that
+ * M includes all headers,
+ * the next header field and the header length field of each header
+ * are valid, and
+ * the sum of each header length equals to OFF.
+ * Because of these assumptions, this function must be called very
+ * carefully. Moreover, it will not be used in the near future when
+ * we develop `neater' mechanism to process extension headers.
+ */
+char *
+ip6_get_prevhdr(m, off)
+ struct mbuf *m;
+ int off;
+{
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+
+ if (off == sizeof(struct ip6_hdr))
+ return(&ip6->ip6_nxt);
+ else {
+ int len, nxt;
+ struct ip6_ext *ip6e = NULL;
+
+ nxt = ip6->ip6_nxt;
+ len = sizeof(struct ip6_hdr);
+ while (len < off) {
+ ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + len);
+
+ switch(nxt) {
+ case IPPROTO_FRAGMENT:
+ len += sizeof(struct ip6_frag);
+ break;
+ case IPPROTO_AH:
+ len += (ip6e->ip6e_len + 2) << 2;
+ break;
+ default:
+ len += (ip6e->ip6e_len + 1) << 3;
+ break;
+ }
+ nxt = ip6e->ip6e_nxt;
+ }
+ if (ip6e)
+ return(&ip6e->ip6e_nxt);
+ else
+ return NULL;
+ }
+}
+
+/*
+ * System control for IP6
+ */
+
+u_char inet6ctlerrmap[PRC_NCMDS] = {
+ 0, 0, 0, 0,
+ 0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH,
+ EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
+ EMSGSIZE, EHOSTUNREACH, 0, 0,
+ 0, 0, 0, 0,
+ ENOPROTOOPT
+};
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
new file mode 100644
index 0000000..c452ad3
--- /dev/null
+++ b/sys/netinet6/ip6_output.c
@@ -0,0 +1,2176 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_output.c 8.3 (Berkeley) 1/21/94
+ */
+
+#include "opt_ip6fw.h"
+#include "opt_inet.h"
+#include "opt_ipsec.h"
+#include "opt_key.h"
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/icmp6.h>
+#include <netinet/in_pcb.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif /* INET6 */
+#include <netkey/key.h>
+#ifdef KEY_DEBUG
+#include <netkey/key_debug.h>
+#ifdef INET6
+#include <netkey/key_debug6.h>
+#endif /* INET6 */
+#else
+#define DPRINTF(lev,arg)
+#define DDO(lev, stmt)
+#define DP(x, y, z)
+#endif /* KEY_DEBUG */
+#endif /* IPSEC */
+
+#include "loop.h"
+
+#include <net/net_osdep.h>
+
+#ifdef IPV6FIREWALL
+#include <netinet6/ip6_fw.h>
+#endif
+
+static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options");
+
+struct ip6_exthdrs {
+ struct mbuf *ip6e_ip6;
+ struct mbuf *ip6e_hbh;
+ struct mbuf *ip6e_dest1;
+ struct mbuf *ip6e_rthdr;
+ struct mbuf *ip6e_dest2;
+};
+
+static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *,
+ struct socket *, struct sockopt *sopt));
+static int ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *));
+static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **));
+static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int));
+static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int,
+ struct ip6_frag **));
+static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t));
+static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *));
+
+/*
+ * IP6 output. The packet in mbuf chain m contains a skeletal IP6
+ * header (with pri, len, nxt, hlim, src, dst).
+ * This function may modify ver and hlim only.
+ * The mbuf chain containing the packet will be freed.
+ * The mbuf opt, if present, will not be freed.
+ */
+int
+ip6_output(m0, opt, ro, flags, im6o, ifpp)
+ struct mbuf *m0;
+ struct ip6_pktopts *opt;
+ struct route_in6 *ro;
+ int flags;
+ struct ip6_moptions *im6o;
+ struct ifnet **ifpp; /* XXX: just for statistics */
+{
+ struct ip6_hdr *ip6, *mhip6;
+ struct ifnet *ifp;
+ struct mbuf *m = m0;
+ int hlen, tlen, len, off;
+ struct route_in6 ip6route;
+ struct sockaddr_in6 *dst;
+ int error = 0;
+ struct in6_ifaddr *ia;
+ u_long mtu;
+ u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
+ struct ip6_exthdrs exthdrs;
+ struct in6_addr finaldst;
+ struct route_in6 *ro_pmtu = NULL;
+ int hdrsplit = 0;
+ int needipsec = 0;
+#ifdef IPSEC
+ int needipsectun = 0;
+ struct socket *so;
+ struct secpolicy *sp = NULL;
+
+ /* for AH processing. stupid to have "socket" variable in IP layer... */
+ so = (struct socket *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+ ip6 = mtod(m, struct ip6_hdr *);
+#endif /* IPSEC */
+
+#define MAKE_EXTHDR(hp,mp) \
+ { \
+ if (hp) { \
+ struct ip6_ext *eh = (struct ip6_ext *)(hp); \
+ error = ip6_copyexthdr((mp), (caddr_t)(hp), \
+ ((eh)->ip6e_len + 1) << 3); \
+ if (error) \
+ goto freehdrs; \
+ } \
+ }
+
+ bzero(&exthdrs, sizeof(exthdrs));
+ if (opt) {
+ /* Hop-by-Hop options header */
+ MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh);
+ /* Destination options header(1st part) */
+ MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1);
+ /* Routing header */
+ MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr);
+ /* Destination options header(2nd part) */
+ MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2);
+ }
+
+#ifdef IPSEC
+ /* get a security policy for this packet */
+ if (so == NULL)
+ sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
+ else
+ sp = ipsec6_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error);
+
+ if (sp == NULL) {
+ ipsec6stat.out_inval++;
+ goto bad;
+ }
+
+ error = 0;
+
+ /* check policy */
+ switch (sp->policy) {
+ case IPSEC_POLICY_DISCARD:
+ /*
+ * This packet is just discarded.
+ */
+ ipsec6stat.out_polvio++;
+ goto bad;
+
+ case IPSEC_POLICY_BYPASS:
+ case IPSEC_POLICY_NONE:
+ /* no need to do IPsec. */
+ needipsec = 0;
+ break;
+
+ case IPSEC_POLICY_IPSEC:
+ if (sp->req == NULL) {
+ /* XXX should be panic ? */
+ printf("ip6_output: No IPsec request specified.\n");
+ error = EINVAL;
+ goto bad;
+ }
+ needipsec = 1;
+ break;
+
+ case IPSEC_POLICY_ENTRUST:
+ default:
+ printf("ip6_output: Invalid policy found. %d\n", sp->policy);
+ }
+#endif /* IPSEC */
+
+ /*
+ * Calculate the total length of the extension header chain.
+ * Keep the length of the unfragmentable part for fragmentation.
+ */
+ optlen = 0;
+ if (exthdrs.ip6e_hbh) optlen += exthdrs.ip6e_hbh->m_len;
+ if (exthdrs.ip6e_dest1) optlen += exthdrs.ip6e_dest1->m_len;
+ if (exthdrs.ip6e_rthdr) optlen += exthdrs.ip6e_rthdr->m_len;
+ unfragpartlen = optlen + sizeof(struct ip6_hdr);
+ /* NOTE: we don't add AH/ESP length here. do that later. */
+ if (exthdrs.ip6e_dest2) optlen += exthdrs.ip6e_dest2->m_len;
+
+ /*
+ * If we need IPsec, or there is at least one extension header,
+ * separate IP6 header from the payload.
+ */
+ if ((needipsec || optlen) && !hdrsplit) {
+ if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
+ m = NULL;
+ goto freehdrs;
+ }
+ m = exthdrs.ip6e_ip6;
+ hdrsplit++;
+ }
+
+ /* adjust pointer */
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ /* adjust mbuf packet header length */
+ m->m_pkthdr.len += optlen;
+ plen = m->m_pkthdr.len - sizeof(*ip6);
+
+ /* If this is a jumbo payload, insert a jumbo payload option. */
+ if (plen > IPV6_MAXPACKET) {
+ if (!hdrsplit) {
+ if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
+ m = NULL;
+ goto freehdrs;
+ }
+ m = exthdrs.ip6e_ip6;
+ hdrsplit++;
+ }
+ /* adjust pointer */
+ ip6 = mtod(m, struct ip6_hdr *);
+ if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0)
+ goto freehdrs;
+ ip6->ip6_plen = 0;
+ } else
+ ip6->ip6_plen = htons(plen);
+
+ /*
+ * Concatenate headers and fill in next header fields.
+ * Here we have, on "m"
+ * IPv6 payload
+ * and we insert headers accordingly. Finally, we should be getting:
+ * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
+ *
+ * during the header composing process, "m" points to IPv6 header.
+ * "mprev" points to an extension header prior to esp.
+ */
+ {
+ u_char *nexthdrp = &ip6->ip6_nxt;
+ struct mbuf *mprev = m;
+
+ /*
+ * we treat dest2 specially. this makes IPsec processing
+ * much easier.
+ *
+ * result: IPv6 dest2 payload
+ * m and mprev will point to IPv6 header.
+ */
+ if (exthdrs.ip6e_dest2) {
+ if (!hdrsplit)
+ panic("assumption failed: hdr not split");
+ exthdrs.ip6e_dest2->m_next = m->m_next;
+ m->m_next = exthdrs.ip6e_dest2;
+ *mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt;
+ ip6->ip6_nxt = IPPROTO_DSTOPTS;
+ }
+
+#define MAKE_CHAIN(m,mp,p,i)\
+ {\
+ if (m) {\
+ if (!hdrsplit) \
+ panic("assumption failed: hdr not split"); \
+ *mtod((m), u_char *) = *(p);\
+ *(p) = (i);\
+ p = mtod((m), u_char *);\
+ (m)->m_next = (mp)->m_next;\
+ (mp)->m_next = (m);\
+ (mp) = (m);\
+ }\
+ }
+ /*
+ * result: IPv6 hbh dest1 rthdr dest2 payload
+ * m will point to IPv6 header. mprev will point to the
+ * extension header prior to dest2 (rthdr in the above case).
+ */
+ MAKE_CHAIN(exthdrs.ip6e_hbh, mprev,
+ nexthdrp, IPPROTO_HOPOPTS);
+ MAKE_CHAIN(exthdrs.ip6e_dest1, mprev,
+ nexthdrp, IPPROTO_DSTOPTS);
+ MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev,
+ nexthdrp, IPPROTO_ROUTING);
+
+#ifdef IPSEC
+ if (!needipsec)
+ goto skip_ipsec2;
+
+ /*
+ * pointers after IPsec headers are not valid any more.
+ * other pointers need a great care too.
+ * (IPsec routines should not mangle mbufs prior to AH/ESP)
+ */
+ exthdrs.ip6e_dest2 = NULL;
+
+ {
+ struct ip6_rthdr *rh = NULL;
+ int segleft_org = 0;
+ struct ipsec_output_state state;
+
+ if (exthdrs.ip6e_rthdr) {
+ rh = mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *);
+ segleft_org = rh->ip6r_segleft;
+ rh->ip6r_segleft = 0;
+ }
+
+ bzero(&state, sizeof(state));
+ state.m = m;
+ error = ipsec6_output_trans(&state, nexthdrp, mprev, sp, flags,
+ &needipsectun);
+ m = state.m;
+ if (error) {
+ /* mbuf is already reclaimed in ipsec6_output_trans. */
+ m = NULL;
+ switch (error) {
+ case EHOSTUNREACH:
+ case ENETUNREACH:
+ case EMSGSIZE:
+ case ENOBUFS:
+ case ENOMEM:
+ break;
+ default:
+ printf("ip6_output (ipsec): error code %d\n", error);
+ /*fall through*/
+ case ENOENT:
+ /* don't show these error codes to the user */
+ error = 0;
+ break;
+ }
+ goto bad;
+ }
+ if (exthdrs.ip6e_rthdr) {
+ /* ah6_output doesn't modify mbuf chain */
+ rh->ip6r_segleft = segleft_org;
+ }
+ }
+skip_ipsec2:;
+#endif
+ }
+
+ /*
+ * If there is a routing header, replace destination address field
+ * with the first hop of the routing header.
+ */
+ if (exthdrs.ip6e_rthdr) {
+ struct ip6_rthdr *rh =
+ (struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr,
+ struct ip6_rthdr *));
+ struct ip6_rthdr0 *rh0;
+
+ finaldst = ip6->ip6_dst;
+ switch(rh->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ rh0 = (struct ip6_rthdr0 *)rh;
+ ip6->ip6_dst = rh0->ip6r0_addr[0];
+ bcopy((caddr_t)&rh0->ip6r0_addr[1],
+ (caddr_t)&rh0->ip6r0_addr[0],
+ sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1)
+ );
+ rh0->ip6r0_addr[rh0->ip6r0_segleft - 1] = finaldst;
+ break;
+ default: /* is it possible? */
+ error = EINVAL;
+ goto bad;
+ }
+ }
+
+ /* Source address validation */
+ if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) &&
+ (flags & IPV6_DADOUTPUT) == 0) {
+ error = EOPNOTSUPP;
+ ip6stat.ip6s_badscope++;
+ goto bad;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) {
+ error = EOPNOTSUPP;
+ ip6stat.ip6s_badscope++;
+ goto bad;
+ }
+
+ ip6stat.ip6s_localout++;
+
+ /*
+ * Route packet.
+ */
+ if (ro == 0) {
+ ro = &ip6route;
+ bzero((caddr_t)ro, sizeof(*ro));
+ }
+ ro_pmtu = ro;
+ if (opt && opt->ip6po_rthdr)
+ ro = &opt->ip6po_route;
+ dst = (struct sockaddr_in6 *)&ro->ro_dst;
+ /*
+ * If there is a cached route,
+ * check that it is to the same destination
+ * and is still up. If not, free it and try again.
+ */
+ if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
+ !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst))) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if (ro->ro_rt == 0) {
+ bzero(dst, sizeof(*dst));
+ dst->sin6_family = AF_INET6;
+ dst->sin6_len = sizeof(struct sockaddr_in6);
+ dst->sin6_addr = ip6->ip6_dst;
+ }
+#ifdef IPSEC
+ if (needipsec && needipsectun) {
+ struct ipsec_output_state state;
+
+ /*
+ * All the extension headers will become inaccessible
+ * (since they can be encrypted).
+ * Don't panic, we need no more updates to extension headers
+ * on inner IPv6 packet (since they are now encapsulated).
+ *
+ * IPv6 [ESP|AH] IPv6 [extension headers] payload
+ */
+ bzero(&exthdrs, sizeof(exthdrs));
+ exthdrs.ip6e_ip6 = m;
+
+ bzero(&state, sizeof(state));
+ state.m = m;
+ state.ro = (struct route *)ro;
+ state.dst = (struct sockaddr *)dst;
+
+ error = ipsec6_output_tunnel(&state, sp, flags);
+
+ m = state.m;
+ ro = (struct route_in6 *)state.ro;
+ dst = (struct sockaddr_in6 *)state.dst;
+ if (error) {
+ /* mbuf is already reclaimed in ipsec6_output_tunnel. */
+ m0 = m = NULL;
+ m = NULL;
+ switch (error) {
+ case EHOSTUNREACH:
+ case ENETUNREACH:
+ case EMSGSIZE:
+ case ENOBUFS:
+ case ENOMEM:
+ break;
+ default:
+ printf("ip6_output (ipsec): error code %d\n", error);
+ /*fall through*/
+ case ENOENT:
+ /* don't show these error codes to the user */
+ error = 0;
+ break;
+ }
+ goto bad;
+ }
+
+ exthdrs.ip6e_ip6 = m;
+ }
+#endif /*IPESC*/
+
+ if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ /* Unicast */
+
+#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa))
+#define sin6tosa(sin6) ((struct sockaddr *)(sin6))
+ /* xxx
+ * interface selection comes here
+ * if an interface is specified from an upper layer,
+ * ifp must point it.
+ */
+ if (ro->ro_rt == 0) {
+ if (ro == &ip6route) /* xxx kazu */
+ rtalloc((struct route *)ro);
+ else
+ rtcalloc((struct route *)ro);
+ }
+ if (ro->ro_rt == 0) {
+ ip6stat.ip6s_noroute++;
+ error = EHOSTUNREACH;
+ /* XXX in6_ifstat_inc(ifp, ifs6_out_discard); */
+ goto bad;
+ }
+ ia = ifatoia6(ro->ro_rt->rt_ifa);
+ ifp = ro->ro_rt->rt_ifp;
+ ro->ro_rt->rt_use++;
+ if (ro->ro_rt->rt_flags & RTF_GATEWAY)
+ dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway;
+ m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
+
+ in6_ifstat_inc(ifp, ifs6_out_request);
+
+ /*
+ * Check if there is the outgoing interface conflicts with
+ * the interface specified by ifi6_ifindex(if specified).
+ * Note that loopback interface is always okay.
+ * (this happens when we are sending packet toward my
+ * interface)
+ */
+ if (opt && opt->ip6po_pktinfo
+ && opt->ip6po_pktinfo->ipi6_ifindex) {
+ if (!(ifp->if_flags & IFF_LOOPBACK)
+ && ifp->if_index != opt->ip6po_pktinfo->ipi6_ifindex) {
+ ip6stat.ip6s_noroute++;
+ in6_ifstat_inc(ifp, ifs6_out_discard);
+ error = EHOSTUNREACH;
+ goto bad;
+ }
+ }
+
+ if (opt && opt->ip6po_hlim != -1)
+ ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
+ } else {
+ /* Multicast */
+ struct in6_multi *in6m;
+
+ m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST;
+
+ /*
+ * See if the caller provided any multicast options
+ */
+ ifp = NULL;
+ if (im6o != NULL) {
+ ip6->ip6_hlim = im6o->im6o_multicast_hlim;
+ if (im6o->im6o_multicast_ifp != NULL)
+ ifp = im6o->im6o_multicast_ifp;
+ } else
+ ip6->ip6_hlim = ip6_defmcasthlim;
+
+ /*
+ * See if the caller provided the outgoing interface
+ * as an ancillary data.
+ * Boundary check for ifindex is assumed to be already done.
+ */
+ if (opt && opt->ip6po_pktinfo && opt->ip6po_pktinfo->ipi6_ifindex)
+ ifp = ifindex2ifnet[opt->ip6po_pktinfo->ipi6_ifindex];
+
+ /*
+ * If the destination is a node-local scope multicast,
+ * the packet should be loop-backed only.
+ */
+ if (IN6_IS_ADDR_MC_NODELOCAL(&ip6->ip6_dst)) {
+ /*
+ * If the outgoing interface is already specified,
+ * it should be a loopback interface.
+ */
+ if (ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) {
+ ip6stat.ip6s_badscope++;
+ error = ENETUNREACH; /* XXX: better error? */
+ /* XXX correct ifp? */
+ in6_ifstat_inc(ifp, ifs6_out_discard);
+ goto bad;
+ } else {
+ ifp = &loif[0];
+ }
+ }
+
+ if (opt && opt->ip6po_hlim != -1)
+ ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
+
+ /*
+ * If caller did not provide an interface lookup a
+ * default in the routing table. This is either a
+ * default for the speicfied group (i.e. a host
+ * route), or a multicast default (a route for the
+ * ``net'' ff00::/8).
+ */
+ if (ifp == NULL) {
+ if (ro->ro_rt == 0) {
+ ro->ro_rt = rtalloc1((struct sockaddr *)
+ &ro->ro_dst, 0, 0UL);
+ }
+ if (ro->ro_rt == 0) {
+ ip6stat.ip6s_noroute++;
+ error = EHOSTUNREACH;
+ /* XXX in6_ifstat_inc(ifp, ifs6_out_discard) */
+ goto bad;
+ }
+ ia = ifatoia6(ro->ro_rt->rt_ifa);
+ ifp = ro->ro_rt->rt_ifp;
+ ro->ro_rt->rt_use++;
+ }
+
+ if ((flags & IPV6_FORWARDING) == 0)
+ in6_ifstat_inc(ifp, ifs6_out_request);
+ in6_ifstat_inc(ifp, ifs6_out_mcast);
+
+ /*
+ * Confirm that the outgoing interface supports multicast.
+ */
+ if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+ ip6stat.ip6s_noroute++;
+ in6_ifstat_inc(ifp, ifs6_out_discard);
+ error = ENETUNREACH;
+ goto bad;
+ }
+ IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);
+ if (in6m != NULL &&
+ (im6o == NULL || im6o->im6o_multicast_loop)) {
+ /*
+ * If we belong to the destination multicast group
+ * on the outgoing interface, and the caller did not
+ * forbid loopback, loop back a copy.
+ */
+ ip6_mloopback(ifp, m, dst);
+ }
+ /*
+ * Multicasts with a hoplimit of zero may be looped back,
+ * above, but must not be transmitted on a network.
+ * Also, multicasts addressed to the loopback interface
+ * are not sent -- the above call to ip6_mloopback() will
+ * loop back a copy if this host actually belongs to the
+ * destination group on the loopback interface.
+ */
+ if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK)) {
+ m_freem(m);
+ goto done;
+ }
+ }
+
+ /*
+ * Fill the outgoing inteface to tell the upper layer
+ * to increment per-interface statistics.
+ */
+ if (ifpp)
+ *ifpp = ifp;
+
+ /*
+ * Determine path MTU.
+ */
+ if (ro_pmtu != ro) {
+ /* The first hop and the final destination may differ. */
+ struct sockaddr_in6 *sin6_fin =
+ (struct sockaddr_in6 *)&ro_pmtu->ro_dst;
+ if (ro_pmtu->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
+ !IN6_ARE_ADDR_EQUAL(&sin6_fin->sin6_addr,
+ &finaldst))) {
+ RTFREE(ro_pmtu->ro_rt);
+ ro_pmtu->ro_rt = (struct rtentry *)0;
+ }
+ if (ro_pmtu->ro_rt == 0) {
+ bzero(sin6_fin, sizeof(*sin6_fin));
+ sin6_fin->sin6_family = AF_INET6;
+ sin6_fin->sin6_len = sizeof(struct sockaddr_in6);
+ sin6_fin->sin6_addr = finaldst;
+
+ rtcalloc((struct route *)ro_pmtu);
+ }
+ }
+ if (ro_pmtu->ro_rt != NULL) {
+ u_int32_t ifmtu = nd_ifinfo[ifp->if_index].linkmtu;
+
+ mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
+ if (mtu > ifmtu) {
+ /*
+ * The MTU on the route is larger than the MTU on
+ * the interface! This shouldn't happen, unless the
+ * MTU of the interface has been changed after the
+ * interface was brought up. Change the MTU in the
+ * route to match the interface MTU (as long as the
+ * field isn't locked).
+ */
+ mtu = ifmtu;
+ if ((ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU) == 0)
+ ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu; /* XXX */
+ }
+ } else {
+ mtu = nd_ifinfo[ifp->if_index].linkmtu;
+ }
+
+ /*
+ * Fake link-local scope-class addresses
+ */
+ if ((ifp->if_flags & IFF_LOOPBACK) == 0) {
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
+ ip6->ip6_src.s6_addr16[1] = 0;
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
+ ip6->ip6_dst.s6_addr16[1] = 0;
+ }
+
+#ifdef IPV6FIREWALL
+ /*
+ * Check with the firewall...
+ */
+ if (ip6_fw_chk_ptr) {
+ u_short port = 0;
+ /* If ipfw says divert, we have to just drop packet */
+ if ((*ip6_fw_chk_ptr)(&ip6, ifp, &port, &m)) {
+ m_freem(m);
+ goto done;
+ }
+ if (!m) {
+ error = EACCES;
+ goto done;
+ }
+ }
+#endif
+
+ /*
+ * If the outgoing packet contains a hop-by-hop options header,
+ * it must be examined and processed even by the source node.
+ * (RFC 2460, section 4.)
+ */
+ if (exthdrs.ip6e_hbh) {
+ struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh,
+ struct ip6_hbh *);
+ u_int32_t dummy1; /* XXX unused */
+ u_int32_t dummy2; /* XXX unused */
+
+ /*
+ * XXX: if we have to send an ICMPv6 error to the sender,
+ * we need the M_LOOP flag since icmp6_error() expects
+ * the IPv6 and the hop-by-hop options header are
+ * continuous unless the flag is set.
+ */
+ m->m_flags |= M_LOOP;
+ m->m_pkthdr.rcvif = ifp;
+ if (ip6_process_hopopts(m,
+ (u_int8_t *)(hbh + 1),
+ ((hbh->ip6h_len + 1) << 3) -
+ sizeof(struct ip6_hbh),
+ &dummy1, &dummy2) < 0) {
+ /* m was already freed at this point */
+ error = EINVAL;/* better error? */
+ goto done;
+ }
+ m->m_flags &= ~M_LOOP; /* XXX */
+ m->m_pkthdr.rcvif = NULL;
+ }
+
+ /*
+ * Send the packet to the outgoing interface.
+ * If necessary, do IPv6 fragmentation before sending.
+ */
+ tlen = m->m_pkthdr.len;
+ if (tlen <= mtu
+#ifdef notyet
+ /*
+ * On any link that cannot convey a 1280-octet packet in one piece,
+ * link-specific fragmentation and reassembly must be provided at
+ * a layer below IPv6. [RFC 2460, sec.5]
+ * Thus if the interface has ability of link-level fragmentation,
+ * we can just send the packet even if the packet size is
+ * larger than the link's MTU.
+ * XXX: IFF_FRAGMENTABLE (or such) flag has not been defined yet...
+ */
+
+ || ifp->if_flags & IFF_FRAGMENTABLE
+#endif
+ )
+ {
+#if defined(__NetBSD__) && defined(IFA_STATS)
+ if (IFA_STATS) {
+ struct in6_ifaddr *ia6;
+ ip6 = mtod(m, struct ip6_hdr *);
+ ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);
+ if (ia6) {
+ ia->ia_ifa.ifa_data.ifad_outbytes +=
+ m->m_pkthdr.len;
+ }
+ }
+#endif
+ error = nd6_output(ifp, m, dst, ro->ro_rt);
+ goto done;
+ } else if (mtu < IPV6_MMTU) {
+ /*
+ * note that path MTU is never less than IPV6_MMTU
+ * (see icmp6_input).
+ */
+ error = EMSGSIZE;
+ in6_ifstat_inc(ifp, ifs6_out_fragfail);
+ goto bad;
+ } else if (ip6->ip6_plen == 0) { /* jumbo payload cannot be fragmented */
+ error = EMSGSIZE;
+ in6_ifstat_inc(ifp, ifs6_out_fragfail);
+ goto bad;
+ } else {
+ struct mbuf **mnext, *m_frgpart;
+ struct ip6_frag *ip6f;
+ u_int32_t id = htonl(ip6_id++);
+ u_char nextproto;
+
+ /*
+ * Too large for the destination or interface;
+ * fragment if possible.
+ * Must be able to put at least 8 bytes per fragment.
+ */
+ hlen = unfragpartlen;
+ if (mtu > IPV6_MAXPACKET)
+ mtu = IPV6_MAXPACKET;
+ len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7;
+ if (len < 8) {
+ error = EMSGSIZE;
+ in6_ifstat_inc(ifp, ifs6_out_fragfail);
+ goto bad;
+ }
+
+ mnext = &m->m_nextpkt;
+
+ /*
+ * Change the next header field of the last header in the
+ * unfragmentable part.
+ */
+ if (exthdrs.ip6e_rthdr) {
+ nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *);
+ *mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT;
+ } else if (exthdrs.ip6e_dest1) {
+ nextproto = *mtod(exthdrs.ip6e_dest1, u_char *);
+ *mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT;
+ } else if (exthdrs.ip6e_hbh) {
+ nextproto = *mtod(exthdrs.ip6e_hbh, u_char *);
+ *mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT;
+ } else {
+ nextproto = ip6->ip6_nxt;
+ ip6->ip6_nxt = IPPROTO_FRAGMENT;
+ }
+
+ /*
+ * Loop through length of segment after first fragment,
+ * make new header and copy data of each part and link onto chain.
+ */
+ m0 = m;
+ for (off = hlen; off < tlen; off += len) {
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (!m) {
+ error = ENOBUFS;
+ ip6stat.ip6s_odropped++;
+ goto sendorfree;
+ }
+ m->m_flags = m0->m_flags & M_COPYFLAGS;
+ *mnext = m;
+ mnext = &m->m_nextpkt;
+ m->m_data += max_linkhdr;
+ mhip6 = mtod(m, struct ip6_hdr *);
+ *mhip6 = *ip6;
+ m->m_len = sizeof(*mhip6);
+ error = ip6_insertfraghdr(m0, m, hlen, &ip6f);
+ if (error) {
+ ip6stat.ip6s_odropped++;
+ goto sendorfree;
+ }
+ ip6f->ip6f_offlg = htons((u_short)((off - hlen) & ~7));
+ if (off + len >= tlen)
+ len = tlen - off;
+ else
+ ip6f->ip6f_offlg |= IP6F_MORE_FRAG;
+ mhip6->ip6_plen = htons((u_short)(len + hlen +
+ sizeof(*ip6f) -
+ sizeof(struct ip6_hdr)));
+ if ((m_frgpart = m_copy(m0, off, len)) == 0) {
+ error = ENOBUFS;
+ ip6stat.ip6s_odropped++;
+ goto sendorfree;
+ }
+ m_cat(m, m_frgpart);
+ m->m_pkthdr.len = len + hlen + sizeof(*ip6f);
+ m->m_pkthdr.rcvif = (struct ifnet *)0;
+ ip6f->ip6f_reserved = 0;
+ ip6f->ip6f_ident = id;
+ ip6f->ip6f_nxt = nextproto;
+ ip6stat.ip6s_ofragments++;
+ in6_ifstat_inc(ifp, ifs6_out_fragcreat);
+ }
+
+ in6_ifstat_inc(ifp, ifs6_out_fragok);
+ }
+
+ /*
+ * Remove leading garbages.
+ */
+sendorfree:
+ m = m0->m_nextpkt;
+ m0->m_nextpkt = 0;
+ m_freem(m0);
+ for (m0 = m; m; m = m0) {
+ m0 = m->m_nextpkt;
+ m->m_nextpkt = 0;
+ if (error == 0) {
+#if defined(__NetBSD__) && defined(IFA_STATS)
+ if (IFA_STATS) {
+ struct in6_ifaddr *ia6;
+ ip6 = mtod(m, struct ip6_hdr *);
+ ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);
+ if (ia6) {
+ ia->ia_ifa.ifa_data.ifad_outbytes +=
+ m->m_pkthdr.len;
+ }
+ }
+#endif
+ error = nd6_output(ifp, m, dst, ro->ro_rt);
+ } else
+ m_freem(m);
+ }
+
+ if (error == 0)
+ ip6stat.ip6s_fragmented++;
+
+done:
+ if (ro == &ip6route && ro->ro_rt) { /* brace necessary for RTFREE */
+ RTFREE(ro->ro_rt);
+ } else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) {
+ RTFREE(ro_pmtu->ro_rt);
+ }
+
+#ifdef IPSEC
+ if (sp != NULL)
+ key_freesp(sp);
+#endif /* IPSEC */
+
+ return(error);
+
+freehdrs:
+ m_freem(exthdrs.ip6e_hbh); /* m_freem will check if mbuf is 0 */
+ m_freem(exthdrs.ip6e_dest1);
+ m_freem(exthdrs.ip6e_rthdr);
+ m_freem(exthdrs.ip6e_dest2);
+ /* fall through */
+bad:
+ m_freem(m);
+ goto done;
+}
+
+static int
+ip6_copyexthdr(mp, hdr, hlen)
+ struct mbuf **mp;
+ caddr_t hdr;
+ int hlen;
+{
+ struct mbuf *m;
+
+ if (hlen > MCLBYTES)
+ return(ENOBUFS); /* XXX */
+
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (!m)
+ return(ENOBUFS);
+
+ if (hlen > MLEN) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ return(ENOBUFS);
+ }
+ }
+ m->m_len = hlen;
+ if (hdr)
+ bcopy(hdr, mtod(m, caddr_t), hlen);
+
+ *mp = m;
+ return(0);
+}
+
+/*
+ * Insert jumbo payload option.
+ */
+static int
+ip6_insert_jumboopt(exthdrs, plen)
+ struct ip6_exthdrs *exthdrs;
+ u_int32_t plen;
+{
+ struct mbuf *mopt;
+ u_char *optbuf;
+
+#define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */
+
+ /*
+ * If there is no hop-by-hop options header, allocate new one.
+ * If there is one but it doesn't have enough space to store the
+ * jumbo payload option, allocate a cluster to store the whole options.
+ * Otherwise, use it to store the options.
+ */
+ if (exthdrs->ip6e_hbh == 0) {
+ MGET(mopt, M_DONTWAIT, MT_DATA);
+ if (mopt == 0)
+ return(ENOBUFS);
+ mopt->m_len = JUMBOOPTLEN;
+ optbuf = mtod(mopt, u_char *);
+ optbuf[1] = 0; /* = ((JUMBOOPTLEN) >> 3) - 1 */
+ exthdrs->ip6e_hbh = mopt;
+ } else {
+ struct ip6_hbh *hbh;
+
+ mopt = exthdrs->ip6e_hbh;
+ if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) {
+ caddr_t oldoptp = mtod(mopt, caddr_t);
+ int oldoptlen = mopt->m_len;
+
+ if (mopt->m_flags & M_EXT)
+ return(ENOBUFS); /* XXX */
+ MCLGET(mopt, M_DONTWAIT);
+ if ((mopt->m_flags & M_EXT) == 0)
+ return(ENOBUFS);
+
+ bcopy(oldoptp, mtod(mopt, caddr_t), oldoptlen);
+ optbuf = mtod(mopt, caddr_t) + oldoptlen;
+ mopt->m_len = oldoptlen + JUMBOOPTLEN;
+ } else {
+ optbuf = mtod(mopt, u_char *) + mopt->m_len;
+ mopt->m_len += JUMBOOPTLEN;
+ }
+ optbuf[0] = IP6OPT_PADN;
+ optbuf[1] = 1;
+
+ /*
+ * Adjust the header length according to the pad and
+ * the jumbo payload option.
+ */
+ hbh = mtod(mopt, struct ip6_hbh *);
+ hbh->ip6h_len += (JUMBOOPTLEN >> 3);
+ }
+
+ /* fill in the option. */
+ optbuf[2] = IP6OPT_JUMBO;
+ optbuf[3] = 4;
+ *(u_int32_t *)&optbuf[4] = htonl(plen + JUMBOOPTLEN);
+
+ /* finally, adjust the packet header length */
+ exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN;
+
+ return(0);
+#undef JUMBOOPTLEN
+}
+
+/*
+ * Insert fragment header and copy unfragmentable header portions.
+ */
+static int
+ip6_insertfraghdr(m0, m, hlen, frghdrp)
+ struct mbuf *m0, *m;
+ int hlen;
+ struct ip6_frag **frghdrp;
+{
+ struct mbuf *n, *mlast;
+
+ if (hlen > sizeof(struct ip6_hdr)) {
+ n = m_copym(m0, sizeof(struct ip6_hdr),
+ hlen - sizeof(struct ip6_hdr), M_DONTWAIT);
+ if (n == 0)
+ return(ENOBUFS);
+ m->m_next = n;
+ } else
+ n = m;
+
+ /* Search for the last mbuf of unfragmentable part. */
+ for (mlast = n; mlast->m_next; mlast = mlast->m_next)
+ ;
+
+ if ((mlast->m_flags & M_EXT) == 0 &&
+ M_TRAILINGSPACE(mlast) < sizeof(struct ip6_frag)) {
+ /* use the trailing space of the last mbuf for the fragment hdr */
+ *frghdrp =
+ (struct ip6_frag *)(mtod(mlast, caddr_t) + mlast->m_len);
+ mlast->m_len += sizeof(struct ip6_frag);
+ m->m_pkthdr.len += sizeof(struct ip6_frag);
+ } else {
+ /* allocate a new mbuf for the fragment header */
+ struct mbuf *mfrg;
+
+ MGET(mfrg, M_DONTWAIT, MT_DATA);
+ if (mfrg == 0)
+ return(ENOBUFS);
+ mfrg->m_len = sizeof(struct ip6_frag);
+ *frghdrp = mtod(mfrg, struct ip6_frag *);
+ mlast->m_next = mfrg;
+ }
+
+ return(0);
+}
+
+/*
+ * IP6 socket option processing.
+ */
+int
+ip6_ctloutput(so, sopt)
+ struct socket *so;
+ struct sockopt *sopt;
+{
+ int privileged;
+ register struct inpcb *in6p = sotoinpcb(so);
+ int error, optval;
+ int level, op, optname;
+ int optlen;
+ struct proc *p;
+
+ if (sopt) {
+ level = sopt->sopt_level;
+ op = sopt->sopt_dir;
+ optname = sopt->sopt_name;
+ optlen = sopt->sopt_valsize;
+ p = sopt->sopt_p;
+ } else {
+ panic("ip6_ctloutput: arg soopt is NULL");
+ }
+ error = optval = 0;
+
+ privileged = (p == 0 || suser(p)) ? 0 : 1;
+
+ if (level == IPPROTO_IPV6) {
+ switch (op) {
+ case SOPT_SET:
+ switch (optname) {
+ case IPV6_PKTOPTIONS:
+ {
+ struct mbuf *m;
+
+ error = soopt_getm(sopt, &m); /* XXX */
+ if (error != NULL)
+ break;
+ error = soopt_mcopyin(sopt, m); /* XXX */
+ if (error != NULL)
+ break;
+ return (ip6_pcbopts(&in6p->in6p_outputopts,
+ m, so, sopt));
+ }
+ case IPV6_HOPOPTS:
+ case IPV6_DSTOPTS:
+ if (!privileged) {
+ error = EPERM;
+ break;
+ }
+ /* fall through */
+ case IPV6_UNICAST_HOPS:
+ case IPV6_RECVOPTS:
+ case IPV6_RECVRETOPTS:
+ case IPV6_RECVDSTADDR:
+ case IPV6_PKTINFO:
+ case IPV6_HOPLIMIT:
+ case IPV6_RTHDR:
+ case IPV6_CHECKSUM:
+ case IPV6_FAITH:
+ case IPV6_BINDV6ONLY:
+ if (optlen != sizeof(int))
+ error = EINVAL;
+ else {
+ error = sooptcopyin(sopt, &optval,
+ sizeof optval, sizeof optval);
+ if (error)
+ break;
+ switch (optname) {
+
+ case IPV6_UNICAST_HOPS:
+ if (optval < -1 || optval >= 256)
+ error = EINVAL;
+ else {
+ /* -1 = kernel default */
+ in6p->in6p_hops = optval;
+ if ((in6p->in6p_vflag &
+ INP_IPV4) != 0)
+ in6p->inp_ip_ttl = optval;
+ }
+ break;
+#define OPTSET(bit) \
+ if (optval) \
+ in6p->in6p_flags |= bit; \
+ else \
+ in6p->in6p_flags &= ~bit;
+
+ case IPV6_RECVOPTS:
+ OPTSET(IN6P_RECVOPTS);
+ break;
+
+ case IPV6_RECVRETOPTS:
+ OPTSET(IN6P_RECVRETOPTS);
+ break;
+
+ case IPV6_RECVDSTADDR:
+ OPTSET(IN6P_RECVDSTADDR);
+ break;
+
+ case IPV6_PKTINFO:
+ OPTSET(IN6P_PKTINFO);
+ break;
+
+ case IPV6_HOPLIMIT:
+ OPTSET(IN6P_HOPLIMIT);
+ break;
+
+ case IPV6_HOPOPTS:
+ OPTSET(IN6P_HOPOPTS);
+ break;
+
+ case IPV6_DSTOPTS:
+ OPTSET(IN6P_DSTOPTS);
+ break;
+
+ case IPV6_RTHDR:
+ OPTSET(IN6P_RTHDR);
+ break;
+
+ case IPV6_CHECKSUM:
+ in6p->in6p_cksum = optval;
+ break;
+
+ case IPV6_FAITH:
+ OPTSET(IN6P_FAITH);
+ break;
+
+ case IPV6_BINDV6ONLY:
+ OPTSET(IN6P_BINDV6ONLY);
+ break;
+ }
+ }
+ break;
+#undef OPTSET
+
+ case IPV6_MULTICAST_IF:
+ case IPV6_MULTICAST_HOPS:
+ case IPV6_MULTICAST_LOOP:
+ case IPV6_JOIN_GROUP:
+ case IPV6_LEAVE_GROUP:
+ {
+ struct mbuf *m;
+ if (sopt->sopt_valsize > MLEN) {
+ error = EMSGSIZE;
+ break;
+ }
+ /* XXX */
+ MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_HEADER);
+ if (m == 0) {
+ error = ENOBUFS;
+ break;
+ }
+ m->m_len = sopt->sopt_valsize;
+ error = sooptcopyin(sopt, mtod(m, char *),
+ m->m_len, m->m_len);
+ error = ip6_setmoptions(sopt->sopt_name,
+ &in6p->in6p_moptions,
+ m);
+ (void)m_free(m);
+ }
+ break;
+
+ case IPV6_PORTRANGE:
+ error = sooptcopyin(sopt, &optval, sizeof optval,
+ sizeof optval);
+ if (error)
+ break;
+
+ switch (optval) {
+ case IPV6_PORTRANGE_DEFAULT:
+ in6p->in6p_flags &= ~(IN6P_LOWPORT);
+ in6p->in6p_flags &= ~(IN6P_HIGHPORT);
+ break;
+
+ case IPV6_PORTRANGE_HIGH:
+ in6p->in6p_flags &= ~(IN6P_LOWPORT);
+ in6p->in6p_flags |= IN6P_HIGHPORT;
+ break;
+
+ case IPV6_PORTRANGE_LOW:
+ in6p->in6p_flags &= ~(IN6P_HIGHPORT);
+ in6p->in6p_flags |= IN6P_LOWPORT;
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+#ifdef IPSEC
+ case IPV6_IPSEC_POLICY:
+ {
+ caddr_t req = NULL;
+ struct mbuf *m;
+
+ if (error = soopt_getm(sopt, &m)) /* XXX */
+ break;
+ if (error = soopt_mcopyin(sopt, m)) /* XXX */
+ break;
+ if (m != 0)
+ req = mtod(m, caddr_t);
+ error = ipsec6_set_policy(in6p, optname, req,
+ privileged);
+ m_freem(m);
+ }
+ break;
+#endif /* IPSEC */
+
+#ifdef IPV6FIREWALL
+ case IPV6_FW_ADD:
+ case IPV6_FW_DEL:
+ case IPV6_FW_FLUSH:
+ case IPV6_FW_ZERO:
+ {
+ struct mbuf *m;
+ struct mbuf **mp = &m;
+
+ if (ip6_fw_ctl_ptr == NULL)
+ return EINVAL;
+ if (error = soopt_getm(sopt, &m)) /* XXX */
+ break;
+ if (error = soopt_mcopyin(sopt, m)) /* XXX */
+ break;
+ error = (*ip6_fw_ctl_ptr)(optname, mp);
+ m = *mp;
+ }
+ break;
+#endif
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+
+ case SOPT_GET:
+ switch (optname) {
+
+ case IPV6_OPTIONS:
+ case IPV6_RETOPTS:
+ error = ENOPROTOOPT;
+ break;
+
+ case IPV6_PKTOPTIONS:
+ if (in6p->in6p_options) {
+ error = soopt_mcopyout(sopt,
+ in6p->in6p_options);
+ } else
+ sopt->sopt_valsize = 0;
+ break;
+
+ case IPV6_HOPOPTS:
+ case IPV6_DSTOPTS:
+ if (!privileged) {
+ error = EPERM;
+ break;
+ }
+ /* fall through */
+ case IPV6_UNICAST_HOPS:
+ case IPV6_RECVOPTS:
+ case IPV6_RECVRETOPTS:
+ case IPV6_RECVDSTADDR:
+ case IPV6_PKTINFO:
+ case IPV6_HOPLIMIT:
+ case IPV6_RTHDR:
+ case IPV6_CHECKSUM:
+ case IPV6_FAITH:
+ case IPV6_BINDV6ONLY:
+ switch (optname) {
+
+ case IPV6_UNICAST_HOPS:
+ optval = in6p->in6p_hops;
+ break;
+
+#define OPTBIT(bit) (in6p->in6p_flags & bit ? 1 : 0)
+
+ case IPV6_RECVOPTS:
+ optval = OPTBIT(IN6P_RECVOPTS);
+ break;
+
+ case IPV6_RECVRETOPTS:
+ optval = OPTBIT(IN6P_RECVRETOPTS);
+ break;
+
+ case IPV6_RECVDSTADDR:
+ optval = OPTBIT(IN6P_RECVDSTADDR);
+ break;
+
+ case IPV6_PKTINFO:
+ optval = OPTBIT(IN6P_PKTINFO);
+ break;
+
+ case IPV6_HOPLIMIT:
+ optval = OPTBIT(IN6P_HOPLIMIT);
+ break;
+
+ case IPV6_HOPOPTS:
+ optval = OPTBIT(IN6P_HOPOPTS);
+ break;
+
+ case IPV6_DSTOPTS:
+ optval = OPTBIT(IN6P_DSTOPTS);
+ break;
+
+ case IPV6_RTHDR:
+ optval = OPTBIT(IN6P_RTHDR);
+ break;
+
+ case IPV6_CHECKSUM:
+ optval = in6p->in6p_cksum;
+ break;
+
+ case IPV6_FAITH:
+ optval = OPTBIT(IN6P_FAITH);
+ break;
+
+ case IPV6_BINDV6ONLY:
+ optval = OPTBIT(IN6P_BINDV6ONLY);
+ break;
+
+ case IPV6_PORTRANGE:
+ {
+ int flags;
+
+ flags = in6p->in6p_flags;
+ if (flags & IN6P_HIGHPORT)
+ optval = IPV6_PORTRANGE_HIGH;
+ else if (flags & IN6P_LOWPORT)
+ optval = IPV6_PORTRANGE_LOW;
+ else
+ optval = 0;
+ break;
+ }
+ }
+ error = sooptcopyout(sopt, &optval,
+ sizeof optval);
+ break;
+
+ case IPV6_MULTICAST_IF:
+ case IPV6_MULTICAST_HOPS:
+ case IPV6_MULTICAST_LOOP:
+ case IPV6_JOIN_GROUP:
+ case IPV6_LEAVE_GROUP:
+ {
+ struct mbuf *m;
+ error = ip6_getmoptions(sopt->sopt_name,
+ in6p->in6p_moptions, &m);
+ if (error == 0)
+ error = sooptcopyout(sopt,
+ mtod(m, char *), m->m_len);
+ m_freem(m);
+ }
+ break;
+
+#ifdef IPSEC
+ case IPV6_IPSEC_POLICY:
+ {
+ caddr_t req = NULL;
+ int len = 0;
+ struct mbuf *m;
+ struct mbuf **mp = &m;
+
+ if (m != 0) {
+ req = mtod(m, caddr_t);
+ len = m->m_len;
+ }
+ error = ipsec6_get_policy(in6p, req, mp);
+ if (error == 0)
+ error = soopt_mcopyout(sopt, m); /*XXX*/
+ m_freem(m);
+ break;
+ }
+#endif /* IPSEC */
+
+#ifdef IPV6FIREWALL
+ case IPV6_FW_GET:
+ {
+ struct mbuf *m;
+ struct mbuf **mp = &m;
+
+ if (ip6_fw_ctl_ptr == NULL)
+ {
+ return EINVAL;
+ }
+ error = (*ip6_fw_ctl_ptr)(optname, mp);
+ if (error == 0)
+ error = soopt_mcopyout(sopt, m); /* XXX */
+ if (m)
+ m_freem(m);
+ }
+ break;
+#endif
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+ }
+ } else {
+ error = EINVAL;
+ }
+ return(error);
+}
+
+/*
+ * Set up IP6 options in pcb for insertion in output packets.
+ * Store in mbuf with pointer in pcbopt, adding pseudo-option
+ * with destination address if source routed.
+ */
+static int
+ip6_pcbopts(pktopt, m, so, sopt)
+ struct ip6_pktopts **pktopt;
+ register struct mbuf *m;
+ struct socket *so;
+ struct sockopt *sopt;
+{
+ register struct ip6_pktopts *opt = *pktopt;
+ int error = 0;
+ struct proc *p = sopt->sopt_p;
+ int priv = 0;
+
+ /* turn off any old options. */
+ if (opt) {
+ if (opt->ip6po_m)
+ (void)m_free(opt->ip6po_m);
+ } else
+ opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK);
+ *pktopt = 0;
+
+ if (!m || m->m_len == 0) {
+ /*
+ * Only turning off any previous options.
+ */
+ if (opt)
+ free(opt, M_IP6OPT);
+ if (m)
+ (void)m_free(m);
+ return(0);
+ }
+
+ /* set options specified by user. */
+ if (p && !suser(p))
+ priv = 1;
+ if ((error = ip6_setpktoptions(m, opt, priv)) != 0) {
+ (void)m_free(m);
+ return(error);
+ }
+ *pktopt = opt;
+ return(0);
+}
+
+/*
+ * Set the IP6 multicast options in response to user setsockopt().
+ */
+static int
+ip6_setmoptions(optname, im6op, m)
+ int optname;
+ struct ip6_moptions **im6op;
+ struct mbuf *m;
+{
+ int error = 0;
+ u_int loop, ifindex;
+ struct ipv6_mreq *mreq;
+ struct ifnet *ifp;
+ struct ip6_moptions *im6o = *im6op;
+ struct route_in6 ro;
+ struct sockaddr_in6 *dst;
+ struct in6_multi_mship *imm;
+ struct proc *p = curproc; /* XXX */
+
+ if (im6o == NULL) {
+ /*
+ * No multicast option buffer attached to the pcb;
+ * allocate one and initialize to default values.
+ */
+ im6o = (struct ip6_moptions *)
+ malloc(sizeof(*im6o), M_IPMOPTS, M_WAITOK);
+
+ if (im6o == NULL)
+ return(ENOBUFS);
+ *im6op = im6o;
+ im6o->im6o_multicast_ifp = NULL;
+ im6o->im6o_multicast_hlim = ip6_defmcasthlim;
+ im6o->im6o_multicast_loop = IPV6_DEFAULT_MULTICAST_LOOP;
+ LIST_INIT(&im6o->im6o_memberships);
+ }
+
+ switch (optname) {
+
+ case IPV6_MULTICAST_IF:
+ /*
+ * Select the interface for outgoing multicast packets.
+ */
+ if (m == NULL || m->m_len != sizeof(u_int)) {
+ error = EINVAL;
+ break;
+ }
+ ifindex = *(mtod(m, u_int *));
+ if (ifindex < 0 || if_index < ifindex) {
+ error = ENXIO; /* XXX EINVAL? */
+ break;
+ }
+ ifp = ifindex2ifnet[ifindex];
+ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ im6o->im6o_multicast_ifp = ifp;
+ break;
+
+ case IPV6_MULTICAST_HOPS:
+ {
+ /*
+ * Set the IP6 hoplimit for outgoing multicast packets.
+ */
+ int optval;
+ if (m == NULL || m->m_len != sizeof(int)) {
+ error = EINVAL;
+ break;
+ }
+ optval = *(mtod(m, u_int *));
+ if (optval < -1 || optval >= 256)
+ error = EINVAL;
+ else if (optval == -1)
+ im6o->im6o_multicast_hlim = ip6_defmcasthlim;
+ else
+ im6o->im6o_multicast_hlim = optval;
+ break;
+ }
+
+ case IPV6_MULTICAST_LOOP:
+ /*
+ * Set the loopback flag for outgoing multicast packets.
+ * Must be zero or one.
+ */
+ if (m == NULL || m->m_len != sizeof(u_int) ||
+ (loop = *(mtod(m, u_int *))) > 1) {
+ error = EINVAL;
+ break;
+ }
+ im6o->im6o_multicast_loop = loop;
+ break;
+
+ case IPV6_JOIN_GROUP:
+ /*
+ * Add a multicast group membership.
+ * Group must be a valid IP6 multicast address.
+ */
+ if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
+ error = EINVAL;
+ break;
+ }
+ mreq = mtod(m, struct ipv6_mreq *);
+ if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
+ /*
+ * We use the unspecified address to specify to accept
+ * all multicast addresses. Only super user is allowed
+ * to do this.
+ */
+ if (suser(p)) {
+ error = EACCES;
+ break;
+ }
+ } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
+ error = EINVAL;
+ break;
+ }
+
+ /*
+ * If the interface is specified, validate it.
+ */
+ if (mreq->ipv6mr_interface < 0
+ || if_index < mreq->ipv6mr_interface) {
+ error = ENXIO; /* XXX EINVAL? */
+ break;
+ }
+ /*
+ * If no interface was explicitly specified, choose an
+ * appropriate one according to the given multicast address.
+ */
+ if (mreq->ipv6mr_interface == 0) {
+ /*
+ * If the multicast address is in node-local scope,
+ * the interface should be a loopback interface.
+ * Otherwise, look up the routing table for the
+ * address, and choose the outgoing interface.
+ * XXX: is it a good approach?
+ */
+ if (IN6_IS_ADDR_MC_NODELOCAL(&mreq->ipv6mr_multiaddr)) {
+ ifp = &loif[0];
+ } else {
+ ro.ro_rt = NULL;
+ dst = (struct sockaddr_in6 *)&ro.ro_dst;
+ bzero(dst, sizeof(*dst));
+ dst->sin6_len = sizeof(struct sockaddr_in6);
+ dst->sin6_family = AF_INET6;
+ dst->sin6_addr = mreq->ipv6mr_multiaddr;
+ rtalloc((struct route *)&ro);
+ if (ro.ro_rt == NULL) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ ifp = ro.ro_rt->rt_ifp;
+ rtfree(ro.ro_rt);
+ }
+ } else
+ ifp = ifindex2ifnet[mreq->ipv6mr_interface];
+
+ /*
+ * See if we found an interface, and confirm that it
+ * supports multicast
+ */
+ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ /*
+ * Put interface index into the multicast address,
+ * if the address has link-local scope.
+ */
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
+ mreq->ipv6mr_multiaddr.s6_addr16[1]
+ = htons(mreq->ipv6mr_interface);
+ }
+ /*
+ * See if the membership already exists.
+ */
+ for (imm = im6o->im6o_memberships.lh_first;
+ imm != NULL; imm = imm->i6mm_chain.le_next)
+ if (imm->i6mm_maddr->in6m_ifp == ifp &&
+ IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
+ &mreq->ipv6mr_multiaddr))
+ break;
+ if (imm != NULL) {
+ error = EADDRINUSE;
+ break;
+ }
+ /*
+ * Everything looks good; add a new record to the multicast
+ * address list for the given interface.
+ */
+ imm = malloc(sizeof(*imm), M_IPMADDR, M_WAITOK);
+ if (imm == NULL) {
+ error = ENOBUFS;
+ break;
+ }
+ if ((imm->i6mm_maddr =
+ in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error)) == NULL) {
+ free(imm, M_IPMADDR);
+ break;
+ }
+ LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
+ break;
+
+ case IPV6_LEAVE_GROUP:
+ /*
+ * Drop a multicast group membership.
+ * Group must be a valid IP6 multicast address.
+ */
+ if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
+ error = EINVAL;
+ break;
+ }
+ mreq = mtod(m, struct ipv6_mreq *);
+ if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
+ if (suser(p)) {
+ error = EACCES;
+ break;
+ }
+ } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
+ error = EINVAL;
+ break;
+ }
+ /*
+ * If an interface address was specified, get a pointer
+ * to its ifnet structure.
+ */
+ if (mreq->ipv6mr_interface < 0
+ || if_index < mreq->ipv6mr_interface) {
+ error = ENXIO; /* XXX EINVAL? */
+ break;
+ }
+ ifp = ifindex2ifnet[mreq->ipv6mr_interface];
+ /*
+ * Put interface index into the multicast address,
+ * if the address has link-local scope.
+ */
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
+ mreq->ipv6mr_multiaddr.s6_addr16[1]
+ = htons(mreq->ipv6mr_interface);
+ }
+ /*
+ * Find the membership in the membership list.
+ */
+ for (imm = im6o->im6o_memberships.lh_first;
+ imm != NULL; imm = imm->i6mm_chain.le_next) {
+ if ((ifp == NULL ||
+ imm->i6mm_maddr->in6m_ifp == ifp) &&
+ IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
+ &mreq->ipv6mr_multiaddr))
+ break;
+ }
+ if (imm == NULL) {
+ /* Unable to resolve interface */
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ /*
+ * Give up the multicast address record to which the
+ * membership points.
+ */
+ LIST_REMOVE(imm, i6mm_chain);
+ in6_delmulti(imm->i6mm_maddr);
+ free(imm, M_IPMADDR);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ /*
+ * If all options have default values, no need to keep the mbuf.
+ */
+ if (im6o->im6o_multicast_ifp == NULL &&
+ im6o->im6o_multicast_hlim == ip6_defmcasthlim &&
+ im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP &&
+ im6o->im6o_memberships.lh_first == NULL) {
+ free(*im6op, M_IPMOPTS);
+ *im6op = NULL;
+ }
+
+ return(error);
+}
+
+/*
+ * Return the IP6 multicast options in response to user getsockopt().
+ */
+static int
+ip6_getmoptions(optname, im6o, mp)
+ int optname;
+ register struct ip6_moptions *im6o;
+ register struct mbuf **mp;
+{
+ u_int *hlim, *loop, *ifindex;
+
+ *mp = m_get(M_WAIT, MT_HEADER); /*XXX*/
+
+ switch (optname) {
+
+ case IPV6_MULTICAST_IF:
+ ifindex = mtod(*mp, u_int *);
+ (*mp)->m_len = sizeof(u_int);
+ if (im6o == NULL || im6o->im6o_multicast_ifp == NULL)
+ *ifindex = 0;
+ else
+ *ifindex = im6o->im6o_multicast_ifp->if_index;
+ return(0);
+
+ case IPV6_MULTICAST_HOPS:
+ hlim = mtod(*mp, u_int *);
+ (*mp)->m_len = sizeof(u_int);
+ if (im6o == NULL)
+ *hlim = ip6_defmcasthlim;
+ else
+ *hlim = im6o->im6o_multicast_hlim;
+ return(0);
+
+ case IPV6_MULTICAST_LOOP:
+ loop = mtod(*mp, u_int *);
+ (*mp)->m_len = sizeof(u_int);
+ if (im6o == NULL)
+ *loop = ip6_defmcasthlim;
+ else
+ *loop = im6o->im6o_multicast_loop;
+ return(0);
+
+ default:
+ return(EOPNOTSUPP);
+ }
+}
+
+/*
+ * Discard the IP6 multicast options.
+ */
+void
+ip6_freemoptions(im6o)
+ register struct ip6_moptions *im6o;
+{
+ struct in6_multi_mship *imm;
+
+ if (im6o == NULL)
+ return;
+
+ while ((imm = im6o->im6o_memberships.lh_first) != NULL) {
+ LIST_REMOVE(imm, i6mm_chain);
+ if (imm->i6mm_maddr)
+ in6_delmulti(imm->i6mm_maddr);
+ free(imm, M_IPMADDR);
+ }
+ free(im6o, M_IPMOPTS);
+}
+
+/*
+ * Set IPv6 outgoing packet options based on advanced API.
+ */
+int
+ip6_setpktoptions(control, opt, priv)
+ struct mbuf *control;
+ struct ip6_pktopts *opt;
+ int priv;
+{
+ register struct cmsghdr *cm = 0;
+
+ if (control == 0 || opt == 0)
+ return(EINVAL);
+
+ bzero(opt, sizeof(*opt));
+ opt->ip6po_hlim = -1; /* -1 means to use default hop limit */
+
+ /*
+ * XXX: Currently, we assume all the optional information is stored
+ * in a single mbuf.
+ */
+ if (control->m_next)
+ return(EINVAL);
+
+ opt->ip6po_m = control;
+
+ for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len),
+ control->m_len -= CMSG_ALIGN(cm->cmsg_len)) {
+ cm = mtod(control, struct cmsghdr *);
+ if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len)
+ return(EINVAL);
+ if (cm->cmsg_level != IPPROTO_IPV6)
+ continue;
+
+ switch(cm->cmsg_type) {
+ case IPV6_PKTINFO:
+ if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo)))
+ return(EINVAL);
+ opt->ip6po_pktinfo = (struct in6_pktinfo *)CMSG_DATA(cm);
+ if (opt->ip6po_pktinfo->ipi6_ifindex &&
+ IN6_IS_ADDR_LINKLOCAL(&opt->ip6po_pktinfo->ipi6_addr))
+ opt->ip6po_pktinfo->ipi6_addr.s6_addr16[1] =
+ htons(opt->ip6po_pktinfo->ipi6_ifindex);
+
+ if (opt->ip6po_pktinfo->ipi6_ifindex > if_index
+ || opt->ip6po_pktinfo->ipi6_ifindex < 0) {
+ return(ENXIO);
+ }
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) {
+ struct ifaddr *ia;
+ struct sockaddr_in6 sin6;
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr =
+ opt->ip6po_pktinfo->ipi6_addr;
+ ia = ifa_ifwithaddr(sin6tosa(&sin6));
+ if (ia == NULL ||
+ (opt->ip6po_pktinfo->ipi6_ifindex &&
+ (ia->ifa_ifp->if_index !=
+ opt->ip6po_pktinfo->ipi6_ifindex))) {
+ return(EADDRNOTAVAIL);
+ }
+ /*
+ * Check if the requested source address is
+ * indeed a unicast address assigned to the
+ * node.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&opt->ip6po_pktinfo->ipi6_addr))
+ return(EADDRNOTAVAIL);
+ }
+ break;
+
+ case IPV6_HOPLIMIT:
+ if (cm->cmsg_len != CMSG_LEN(sizeof(int)))
+ return(EINVAL);
+
+ opt->ip6po_hlim = *(int *)CMSG_DATA(cm);
+ if (opt->ip6po_hlim < -1 || opt->ip6po_hlim > 255)
+ return(EINVAL);
+ break;
+
+ case IPV6_NEXTHOP:
+ if (!priv)
+ return(EPERM);
+ if (cm->cmsg_len < sizeof(u_char) ||
+ cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm)))
+ return(EINVAL);
+
+ opt->ip6po_nexthop = (struct sockaddr *)CMSG_DATA(cm);
+
+ break;
+
+ case IPV6_HOPOPTS:
+ if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_hbh)))
+ return(EINVAL);
+ opt->ip6po_hbh = (struct ip6_hbh *)CMSG_DATA(cm);
+ if (cm->cmsg_len !=
+ CMSG_LEN((opt->ip6po_hbh->ip6h_len + 1) << 3))
+ return(EINVAL);
+ break;
+
+ case IPV6_DSTOPTS:
+ if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest)))
+ return(EINVAL);
+
+ /*
+ * If there is no routing header yet, the destination
+ * options header should be put on the 1st part.
+ * Otherwise, the header should be on the 2nd part.
+ * (See RFC 2460, section 4.1)
+ */
+ if (opt->ip6po_rthdr == NULL) {
+ opt->ip6po_dest1 =
+ (struct ip6_dest *)CMSG_DATA(cm);
+ if (cm->cmsg_len !=
+ CMSG_LEN((opt->ip6po_dest1->ip6d_len + 1)
+ << 3))
+ return(EINVAL);
+ } else {
+ opt->ip6po_dest2 =
+ (struct ip6_dest *)CMSG_DATA(cm);
+ if (cm->cmsg_len !=
+ CMSG_LEN((opt->ip6po_dest2->ip6d_len + 1)
+ << 3))
+ return(EINVAL);
+ }
+ break;
+
+ case IPV6_RTHDR:
+ if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr)))
+ return(EINVAL);
+ opt->ip6po_rthdr = (struct ip6_rthdr *)CMSG_DATA(cm);
+ if (cm->cmsg_len !=
+ CMSG_LEN((opt->ip6po_rthdr->ip6r_len + 1) << 3))
+ return(EINVAL);
+ switch(opt->ip6po_rthdr->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ if (opt->ip6po_rthdr->ip6r_segleft == 0)
+ return(EINVAL);
+ break;
+ default:
+ return(EINVAL);
+ }
+ break;
+
+ default:
+ return(ENOPROTOOPT);
+ }
+ }
+
+ return(0);
+}
+
+/*
+ * Routine called from ip6_output() to loop back a copy of an IP6 multicast
+ * packet to the input queue of a specified interface. Note that this
+ * calls the output routine of the loopback "driver", but with an interface
+ * pointer that might NOT be &loif -- easier than replicating that code here.
+ */
+void
+ip6_mloopback(ifp, m, dst)
+ struct ifnet *ifp;
+ register struct mbuf *m;
+ register struct sockaddr_in6 *dst;
+{
+ struct mbuf *copym;
+
+ copym = m_copy(m, 0, M_COPYALL);
+ if (copym != NULL) {
+ (void)if_simloop(ifp, copym, (struct sockaddr *)dst, NULL);
+ }
+}
+
+/*
+ * Chop IPv6 header off from the payload.
+ */
+static int
+ip6_splithdr(m, exthdrs)
+ struct mbuf *m;
+ struct ip6_exthdrs *exthdrs;
+{
+ struct mbuf *mh;
+ struct ip6_hdr *ip6;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ if (m->m_len > sizeof(*ip6)) {
+ MGETHDR(mh, M_DONTWAIT, MT_HEADER);
+ if (mh == 0) {
+ m_freem(m);
+ return ENOBUFS;
+ }
+ M_COPY_PKTHDR(mh, m);
+ MH_ALIGN(mh, sizeof(*ip6));
+ m->m_flags &= ~M_PKTHDR;
+ m->m_len -= sizeof(*ip6);
+ m->m_data += sizeof(*ip6);
+ mh->m_next = m;
+ m = mh;
+ m->m_len = sizeof(*ip6);
+ bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(*ip6));
+ }
+ exthdrs->ip6e_ip6 = m;
+ return 0;
+}
+
+/*
+ * Compute IPv6 extension header length.
+ */
+int
+ip6_optlen(in6p)
+ struct in6pcb *in6p;
+{
+ int len;
+
+ if (!in6p->in6p_outputopts)
+ return 0;
+
+ len = 0;
+#define elen(x) \
+ (((struct ip6_ext *)(x)) ? (((struct ip6_ext *)(x))->ip6e_len + 1) << 3 : 0)
+
+ len += elen(in6p->in6p_outputopts->ip6po_hbh);
+ len += elen(in6p->in6p_outputopts->ip6po_dest1);
+ len += elen(in6p->in6p_outputopts->ip6po_rthdr);
+ len += elen(in6p->in6p_outputopts->ip6po_dest2);
+ return len;
+#undef elen
+}
+
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
new file mode 100644
index 0000000..21df4f7
--- /dev/null
+++ b/sys/netinet6/ip6_var.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _NETINET6_IP6_VAR_H_
+#define _NETINET6_IP6_VAR_H_
+
+/*
+ * IP6 reassembly queue structure. Each fragment
+ * being reassembled is attached to one of these structures.
+ */
+struct ip6q {
+ u_long ip6q_head;
+ u_short ip6q_len;
+ u_char ip6q_nxt;
+ u_char ip6q_hlim;
+ struct ip6asfrag *ip6q_down;
+ struct ip6asfrag *ip6q_up;
+ u_long ip6q_ident;
+ u_char ip6q_arrive;
+ u_char ip6q_ttl;
+ struct in6_addr ip6q_src, ip6q_dst;
+ struct ip6q *ip6q_next;
+ struct ip6q *ip6q_prev;
+ int ip6q_unfrglen;
+};
+
+struct ip6asfrag {
+ u_long ip6af_head;
+ u_short ip6af_len;
+ u_char ip6af_nxt;
+ u_char ip6af_hlim;
+ /* must not override the above members during reassembling */
+ struct ip6asfrag *ip6af_down;
+ struct ip6asfrag *ip6af_up;
+ u_short ip6af_mff;
+ u_short ip6af_off;
+ struct mbuf *ip6af_m;
+ u_long ip6af_offset; /* offset where next header starts */
+ u_short ip6af_frglen; /* fragmentable part length */
+ u_char ip6af_x1[10];
+};
+
+#define IP6_REASS_MBUF(ip6af) (*(struct mbuf **)&((ip6af)->ip6af_m))
+
+struct ip6_moptions {
+ struct ifnet *im6o_multicast_ifp; /* ifp for outgoing multicasts */
+ u_char im6o_multicast_hlim; /* hoplimit for outgoing multicasts */
+ u_char im6o_multicast_loop; /* 1 >= hear sends if a member */
+ LIST_HEAD(, in6_multi_mship) im6o_memberships;
+};
+
+/*
+ * Control options for outgoing packets
+ */
+
+/* Routing header related info */
+struct ip6po_rhinfo {
+ struct ip6_rthdr *ip6po_rhi_rthdr; /* Routing header */
+ struct route_in6 ip6po_rhi_route; /* Route to the 1st hop */
+};
+#define ip6po_rthdr ip6po_rhinfo.ip6po_rhi_rthdr
+#define ip6po_route ip6po_rhinfo.ip6po_rhi_route
+
+struct ip6_pktopts {
+ struct mbuf *ip6po_m; /* Pointer to mbuf storing the data */
+ int ip6po_hlim; /* Hoplimit for outgoing packets */
+ struct in6_pktinfo *ip6po_pktinfo; /* Outgoing IF/address information */
+ struct sockaddr *ip6po_nexthop; /* Next-hop address */
+ struct ip6_hbh *ip6po_hbh; /* Hop-by-Hop options header */
+ struct ip6_dest *ip6po_dest1; /* Destination options header(1st part) */
+ struct ip6po_rhinfo ip6po_rhinfo; /* Routing header related info. */
+ struct ip6_dest *ip6po_dest2; /* Destination options header(2nd part) */
+};
+
+struct ip6stat {
+ u_long ip6s_total; /* total packets received */
+ u_long ip6s_tooshort; /* packet too short */
+ u_long ip6s_toosmall; /* not enough data */
+ u_long ip6s_fragments; /* fragments received */
+ u_long ip6s_fragdropped; /* frags dropped(dups, out of space) */
+ u_long ip6s_fragtimeout; /* fragments timed out */
+ u_long ip6s_fragoverflow; /* fragments that exceeded limit */
+ u_long ip6s_forward; /* packets forwarded */
+ u_long ip6s_cantforward; /* packets rcvd for unreachable dest */
+ u_long ip6s_redirectsent; /* packets forwarded on same net */
+ u_long ip6s_delivered; /* datagrams delivered to upper level*/
+ u_long ip6s_localout; /* total ip packets generated here */
+ u_long ip6s_odropped; /* lost packets due to nobufs, etc. */
+ u_long ip6s_reassembled; /* total packets reassembled ok */
+ u_long ip6s_fragmented; /* datagrams sucessfully fragmented */
+ u_long ip6s_ofragments; /* output fragments created */
+ u_long ip6s_cantfrag; /* don't fragment flag was set, etc. */
+ u_long ip6s_badoptions; /* error in option processing */
+ u_long ip6s_noroute; /* packets discarded due to no route */
+ u_long ip6s_badvers; /* ip6 version != 6 */
+ u_long ip6s_rawout; /* total raw ip packets generated */
+ u_long ip6s_badscope; /* scope error */
+ u_long ip6s_notmember; /* don't join this multicast group */
+ u_long ip6s_nxthist[256]; /* next header history */
+ u_long ip6s_m1; /* one mbuf */
+ u_long ip6s_m2m[32]; /* two or more mbuf */
+ u_long ip6s_mext1; /* one ext mbuf */
+ u_long ip6s_mext2m; /* two or more ext mbuf */
+ u_long ip6s_exthdrtoolong; /* ext hdr are not continuous */
+ u_long ip6s_nogif; /* no match gif found */
+ u_long ip6s_toomanyhdr; /* discarded due to too many headers */
+};
+
+#ifdef _KERNEL
+/* flags passed to ip6_output as last parameter */
+#define IPV6_DADOUTPUT 0x01 /* DAD */
+#define IPV6_FORWARDING 0x02 /* most of IPv6 header exists */
+
+extern struct ip6stat ip6stat; /* statistics */
+extern u_int32_t ip6_id; /* fragment identifier */
+extern int ip6_defhlim; /* default hop limit */
+extern int ip6_defmcasthlim; /* default multicast hop limit */
+extern int ip6_forwarding; /* act as router? */
+extern int ip6_forward_srcrt; /* forward src-routed? */
+extern int ip6_gif_hlim; /* Hop limit for gif encap packet */
+extern int ip6_use_deprecated; /* allow deprecated addr as source */
+extern int ip6_rr_prune; /* router renumbering prefix
+ * walk list every 5 sec. */
+extern int ip6_mapped_addr_on;
+
+extern struct socket *ip6_mrouter; /* multicast routing daemon */
+extern int ip6_sendredirects; /* send IP redirects when forwarding? */
+extern int ip6_maxfragpackets; /* Maximum packets in reassembly queue */
+extern int ip6_sourcecheck; /* Verify source interface */
+extern int ip6_sourcecheck_interval; /* Interval between log messages */
+extern int ip6_accept_rtadv; /* Acts as a host not a router */
+extern int ip6_keepfaith; /* Firewall Aided Internet Translator */
+extern int ip6_log_interval;
+extern time_t ip6_log_time;
+extern int ip6_hdrnestlimit; /* upper limit of # of extension headers */
+extern int ip6_dad_count; /* DupAddrDetectionTransmits */
+
+extern u_int32_t ip6_flow_seq;
+extern int ip6_auto_flowlabel;
+
+extern struct pr_usrreqs rip6_usrreqs;
+struct sockopt;
+struct inpcb;
+
+int icmp6_ctloutput __P((struct socket *, struct sockopt *sopt));
+
+void ip6_init __P((void));
+void ip6intr __P((void));
+void ip6_input __P((struct mbuf *));
+void ip6_freemoptions __P((struct ip6_moptions *));
+int ip6_unknown_opt __P((u_int8_t *, struct mbuf *, int));
+char * ip6_get_prevhdr __P((struct mbuf *, int));
+int ip6_mforward __P((struct ip6_hdr *, struct ifnet *, struct mbuf *));
+int ip6_process_hopopts __P((struct mbuf *, u_int8_t *, int, u_int32_t *,
+ u_int32_t *));
+void ip6_savecontrol __P((struct inpcb *, struct mbuf **, struct ip6_hdr *,
+ struct mbuf *));
+int ip6_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
+
+void ip6_forward __P((struct mbuf *, int));
+
+void ip6_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in6 *));
+int ip6_output __P((struct mbuf *, struct ip6_pktopts *,
+ struct route_in6 *, int,
+ struct ip6_moptions *, struct ifnet **));
+int ip6_ctloutput __P((struct socket *, struct sockopt *sopt));
+int ip6_setpktoptions __P((struct mbuf *, struct ip6_pktopts *, int));
+int ip6_optlen __P((struct inpcb *));
+
+int route6_input __P((struct mbuf **, int *, int));
+
+void frag6_init __P((void));
+int frag6_input __P((struct mbuf **, int *, int));
+void frag6_slowtimo __P((void));
+void frag6_drain __P((void));
+
+void rip6_init __P((void));
+int rip6_input __P((struct mbuf **mp, int *offp, int proto));
+int rip6_ctloutput __P((struct socket *so, struct sockopt *sopt));
+int rip6_output __P((struct mbuf *, ...));
+int rip6_usrreq __P((struct socket *,
+ int, struct mbuf *, struct mbuf *, struct mbuf *, struct proc *));
+
+int dest6_input __P((struct mbuf **, int *, int));
+int none_input __P((struct mbuf **, int *, int));
+#endif /* _KERNEL */
+
+#endif /* !_NETINET6_IP6_VAR_H_ */
diff --git a/sys/netinet6/ip6protosw.h b/sys/netinet6/ip6protosw.h
new file mode 100644
index 0000000..bf3e4dc
--- /dev/null
+++ b/sys/netinet6/ip6protosw.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* BSDI protosw.h,v 2.3 1996/10/11 16:02:40 pjd Exp */
+
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)protosw.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _NETINET6_IP6PROTOSW_H_
+#define _NETINET6_IP6PROTOSW_H_
+
+/*
+ * Protocol switch table for IPv6.
+ * All other definitions should refer to sys/protosw.h
+ */
+
+struct mbuf;
+struct sockaddr;
+struct socket;
+struct domain;
+struct proc;
+struct ip6_hdr;
+struct pr_usrreqs;
+
+/*
+ * argument type for the last arg of pr_ctlinput().
+ * should be consulted only with AF_INET6 family.
+ */
+struct ip6ctlparam {
+ struct mbuf *ip6c_m; /* start of mbuf chain */
+ struct ip6_hdr *ip6c_ip6; /* ip6 header of target packet */
+ int ip6c_off; /* offset of the target proto header */
+};
+
+struct ip6protosw {
+ int pr_type; /* socket type used for */
+ struct domain *pr_domain; /* domain protocol a member of */
+ short pr_protocol; /* protocol number */
+ short pr_flags; /* see below */
+
+/* protocol-protocol hooks */
+ int (*pr_input) /* input to protocol (from below) */
+ __P((struct mbuf **, int *, int));
+ int (*pr_output) /* output to protocol (from above) */
+ __P((struct mbuf *, ...));
+ void (*pr_ctlinput) /* control input (from below) */
+ __P((int, struct sockaddr *, void *));
+ int (*pr_ctloutput) /* control output (from above) */
+ __P((struct socket *, struct sockopt *));
+
+/* user-protocol hook */
+ int (*pr_usrreq) /* user request: see list below */
+ __P((struct socket *, int, struct mbuf *,
+ struct mbuf *, struct mbuf *, struct proc *));
+
+/* utility hooks */
+ void (*pr_init) /* initialization hook */
+ __P((void));
+
+ void (*pr_fasttimo) /* fast timeout (200ms) */
+ __P((void));
+ void (*pr_slowtimo) /* slow timeout (500ms) */
+ __P((void));
+ void (*pr_drain) /* flush any excess space possible */
+ __P((void));
+ struct pr_usrreqs *pr_usrreqs; /* supersedes pr_usrreq() */
+};
+
+#endif /* !_NETINET6_IP6PROTOSW_H_ */
diff --git a/sys/netinet6/ipsec.h b/sys/netinet6/ipsec.h
new file mode 100644
index 0000000..6fdcba1
--- /dev/null
+++ b/sys/netinet6/ipsec.h
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * IPsec controller part.
+ */
+
+#ifndef _NETINET6_IPSEC_H_
+#define _NETINET6_IPSEC_H_
+
+#include <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+
+#ifdef KERNEL
+
+/*
+ * Security Policy Index
+ * NOTE: Encure to be same address family and upper layer protocol.
+ * NOTE: ul_proto, port number, uid, gid:
+ * ANY: reserved for waldcard.
+ * 0 to (~0 - 1): is one of the number of each value.
+ */
+struct secpolicyindex {
+ u_int8_t dir; /* direction of packet flow, see blow */
+ struct sockaddr_storage src; /* IP src address for SP */
+ struct sockaddr_storage dst; /* IP dst address for SP */
+ u_int8_t prefs; /* prefix length in bits for src */
+ u_int8_t prefd; /* prefix length in bits for dst */
+ u_int16_t ul_proto; /* upper layer Protocol */
+};
+
+/* Security Policy Data Base */
+struct secpolicy {
+ LIST_ENTRY(secpolicy) chain;
+
+ int refcnt; /* reference count */
+ struct secpolicyindex spidx; /* selector */
+ u_int state; /* 0: dead, others: alive */
+#define IPSEC_SPSTATE_DEAD 0
+#define IPSEC_SPSTATE_ALIVE 1
+
+ u_int policy; /* DISCARD, NONE or IPSEC, see keyv2.h */
+ struct ipsecrequest *req;
+ /* pointer to the ipsec request tree, */
+ /* if policy == IPSEC else this value == NULL.*/
+};
+
+/* Request for IPsec */
+struct ipsecrequest {
+ struct ipsecrequest *next;
+ /* pointer to next structure */
+ /* If NULL, it means the end of chain. */
+ struct secasindex saidx;
+ u_int level; /* IPsec level defined below. */
+
+ struct secasvar *sav; /* place holder of SA for use */
+ struct secpolicy *sp; /* back pointer to SP */
+};
+
+/* security policy in PCB */
+struct inpcbpolicy {
+ struct secpolicy *sp_in;
+ struct secpolicy *sp_out;
+ int priv; /* privileged socket ? */
+};
+#endif /*KERNEL*/
+
+#define IPSEC_PORT_ANY 65535
+#define IPSEC_ULPROTO_ANY 255
+#define IPSEC_PROTO_ANY 65535
+
+/* mode of security protocol */
+/* NOTE: DON'T use IPSEC_MODE_ANY at SPD. It's only use in SAD */
+#define IPSEC_MODE_ANY 0 /* i.e. wildcard. */
+#define IPSEC_MODE_TRANSPORT 1
+#define IPSEC_MODE_TUNNEL 2
+
+/*
+ * Direction of security policy.
+ * NOTE: Since INVALID is used just as flag.
+ * The other are used for loop counter too.
+ */
+#define IPSEC_DIR_ANY 0
+#define IPSEC_DIR_INBOUND 1
+#define IPSEC_DIR_OUTBOUND 2
+#define IPSEC_DIR_MAX 3
+#define IPSEC_DIR_INVALID 4
+
+/* Policy level */
+/*
+ * IPSEC, ENTRUST and BYPASS are allowd for setsockopt() in PCB,
+ * DISCARD, IPSEC and NONE are allowd for setkey() in SPD.
+ * DISCARD and NONE are allowd for system default.
+ */
+#define IPSEC_POLICY_DISCARD 0 /* discarding packet */
+#define IPSEC_POLICY_NONE 1 /* through IPsec engine */
+#define IPSEC_POLICY_IPSEC 2 /* do IPsec */
+#define IPSEC_POLICY_ENTRUST 3 /* consulting SPD if present. */
+#define IPSEC_POLICY_BYPASS 4 /* only for privileged socket. */
+
+/* Security protocol level */
+#define IPSEC_LEVEL_DEFAULT 0 /* reference to system default */
+#define IPSEC_LEVEL_USE 1 /* use SA if present. */
+#define IPSEC_LEVEL_REQUIRE 2 /* require SA. */
+#define IPSEC_LEVEL_UNIQUE 3 /* unique SA. */
+
+#define IPSEC_REPLAYWSIZE 32
+
+/* statistics for ipsec processing */
+struct ipsecstat {
+ u_long in_success; /* succeeded inbound process */
+ u_long in_polvio; /* security policy violation for inbound process */
+ u_long in_nosa; /* inbound SA is unavailable */
+ u_long in_inval; /* inbound processing failed due to EINVAL */
+ u_long in_badspi; /* failed getting a SPI */
+ u_long in_ahreplay; /* AH replay check failed */
+ u_long in_espreplay; /* ESP replay check failed */
+ u_long in_ahauthsucc; /* AH authentication success */
+ u_long in_ahauthfail; /* AH authentication failure */
+ u_long in_espauthsucc; /* ESP authentication success */
+ u_long in_espauthfail; /* ESP authentication failure */
+ u_long in_esphist[SADB_EALG_MAX];
+ u_long in_ahhist[SADB_AALG_MAX];
+ u_long out_success; /* succeeded outbound process */
+ u_long out_polvio; /* security policy violation for outbound process */
+ u_long out_nosa; /* outbound SA is unavailable */
+ u_long out_inval; /* outbound process failed due to EINVAL */
+ u_long out_noroute; /* there is no route */
+ u_long out_esphist[SADB_EALG_MAX];
+ u_long out_ahhist[SADB_AALG_MAX];
+};
+
+/*
+ * Definitions for IPsec & Key sysctl operations.
+ */
+/*
+ * Names for IPsec & Key sysctl objects
+ */
+#define IPSECCTL_STATS 1 /* stats */
+#define IPSECCTL_DEF_POLICY 2
+#define IPSECCTL_DEF_ESP_TRANSLEV 3 /* int; ESP transport mode */
+#define IPSECCTL_DEF_ESP_NETLEV 4 /* int; ESP tunnel mode */
+#define IPSECCTL_DEF_AH_TRANSLEV 5 /* int; AH transport mode */
+#define IPSECCTL_DEF_AH_NETLEV 6 /* int; AH tunnel mode */
+#define IPSECCTL_INBOUND_CALL_IKE 7
+#define IPSECCTL_AH_CLEARTOS 8
+#define IPSECCTL_AH_OFFSETMASK 9
+#define IPSECCTL_DFBIT 10
+#define IPSECCTL_ECN 11
+#define IPSECCTL_MAXID 12
+
+#define IPSECCTL_NAMES { \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "def_policy", CTLTYPE_INT }, \
+ { "esp_trans_deflev", CTLTYPE_INT }, \
+ { "esp_net_deflev", CTLTYPE_INT }, \
+ { "ah_trans_deflev", CTLTYPE_INT }, \
+ { "ah_net_deflev", CTLTYPE_INT }, \
+ { "inbound_call_ike", CTLTYPE_INT }, \
+ { "ah_cleartos", CTLTYPE_INT }, \
+ { "ah_offsetmask", CTLTYPE_INT }, \
+ { "dfbit", CTLTYPE_INT }, \
+ { "ecn", CTLTYPE_INT }, \
+}
+
+#define IPSEC6CTL_NAMES { \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "def_policy", CTLTYPE_INT }, \
+ { "esp_trans_deflev", CTLTYPE_INT }, \
+ { "esp_net_deflev", CTLTYPE_INT }, \
+ { "ah_trans_deflev", CTLTYPE_INT }, \
+ { "ah_net_deflev", CTLTYPE_INT }, \
+ { "inbound_call_ike", CTLTYPE_INT }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "ecn", CTLTYPE_INT }, \
+}
+
+#define IPSECCTL_VARS { \
+ 0, \
+ 0, \
+ &ip4_def_policy.policy, \
+ &ip4_esp_trans_deflev, \
+ &ip4_esp_net_deflev, \
+ &ip4_ah_trans_deflev, \
+ &ip4_ah_net_deflev, \
+ &ip4_inbound_call_ike, \
+ &ip4_ah_cleartos, \
+ &ip4_ah_offsetmask, \
+ &ip4_ipsec_dfbit, \
+ &ip4_ipsec_ecn, \
+}
+
+#define IPSEC6CTL_VARS { \
+ 0, \
+ 0, \
+ &ip6_def_policy.policy, \
+ &ip6_esp_trans_deflev, \
+ &ip6_esp_net_deflev, \
+ &ip6_ah_trans_deflev, \
+ &ip6_ah_net_deflev, \
+ &ip6_inbound_call_ike, \
+ 0, \
+ 0, \
+ 0, \
+ &ip6_ipsec_ecn, \
+}
+
+#ifdef KERNEL
+struct ipsec_output_state {
+ struct mbuf *m;
+ struct route *ro;
+ struct sockaddr *dst;
+};
+
+extern struct ipsecstat ipsecstat;
+extern struct secpolicy ip4_def_policy;
+extern int ip4_esp_trans_deflev;
+extern int ip4_esp_net_deflev;
+extern int ip4_ah_trans_deflev;
+extern int ip4_ah_net_deflev;
+extern int ip4_inbound_call_ike;
+extern int ip4_ah_cleartos;
+extern int ip4_ah_offsetmask;
+extern int ip4_ipsec_dfbit;
+extern int ip4_ipsec_ecn;
+
+extern struct secpolicy *ipsec4_getpolicybysock
+ __P((struct mbuf *, u_int, struct socket *, int *));
+extern struct secpolicy *ipsec4_getpolicybyaddr
+ __P((struct mbuf *, u_int, int, int *));
+
+struct inpcb;
+
+extern int ipsec_init_policy __P((struct socket *so, struct inpcbpolicy **));
+extern int ipsec_copy_policy
+ __P((struct inpcbpolicy *, struct inpcbpolicy *));
+extern u_int ipsec_get_reqlevel __P((struct ipsecrequest *));
+
+extern int ipsec4_set_policy __P((struct inpcb *inp, int optname,
+ caddr_t request, int priv));
+extern int ipsec4_get_policy
+ __P((struct inpcb *inpcb, caddr_t request, struct mbuf **mp));
+extern int ipsec4_delete_pcbpolicy __P((struct inpcb *));
+extern int ipsec4_in_reject_so __P((struct mbuf *, struct socket *));
+extern int ipsec4_in_reject __P((struct mbuf *, struct inpcb *));
+
+struct secas;
+struct tcpcb;
+struct tcp6cb;
+extern int ipsec_chkreplay __P((u_int32_t, struct secasvar *));
+extern int ipsec_updatereplay __P((u_int32_t, struct secasvar *));
+
+extern size_t ipsec4_hdrsiz __P((struct mbuf *, u_int, struct inpcb *));
+extern size_t ipsec_hdrsiz_tcp __P((struct tcpcb *, int));
+
+struct ip;
+
+extern const char *ipsec4_logpacketstr __P((struct ip *, u_int32_t));
+extern const char *ipsec_logsastr __P((struct secasvar *));
+
+extern void ipsec_dumpmbuf __P((struct mbuf *));
+
+extern int ipsec4_output __P((struct ipsec_output_state *, struct secpolicy *,
+ int));
+
+extern int ipsec4_tunnel_validate __P((struct ip *, u_int,
+ struct secasvar *));
+
+extern struct mbuf *ipsec_copypkt __P((struct mbuf *));
+
+#endif /*KERNEL*/
+
+#ifndef KERNEL
+extern caddr_t ipsec_set_policy __P((char *policy, int buflen));
+extern int ipsec_get_policylen __P((caddr_t buf));
+extern char *ipsec_dump_policy __P((caddr_t buf, char *delimiter));
+
+extern char *ipsec_strerror __P((void));
+#endif /*!KERNEL*/
+
+#endif /*_NETINET6_IPSEC_H_*/
+
diff --git a/sys/netinet6/ipsec6.h b/sys/netinet6/ipsec6.h
new file mode 100644
index 0000000..61896b3
--- /dev/null
+++ b/sys/netinet6/ipsec6.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * IPsec controller part, only IPv6 related
+ */
+
+#ifndef _NETINET6_IPSEC6_H_
+#define _NETINET6_IPSEC6_H_
+
+#ifdef KERNEL
+
+extern struct ipsecstat ipsec6stat;
+extern struct secpolicy ip6_def_policy;
+extern int ip6_esp_trans_deflev;
+extern int ip6_esp_net_deflev;
+extern int ip6_ah_trans_deflev;
+extern int ip6_ah_net_deflev;
+extern int ip6_inbound_call_ike;
+extern int ip6_ipsec_ecn;
+
+extern struct secpolicy *ipsec6_getpolicybysock
+ __P((struct mbuf *, u_int, struct socket *, int *));
+extern struct secpolicy *ipsec6_getpolicybyaddr
+ __P((struct mbuf *, u_int, int, int *));
+
+extern int ipsec6_in_reject_so __P((struct mbuf *, struct socket *));
+extern int ipsec6_delete_pcbpolicy __P((struct inpcb *));
+extern int ipsec6_set_policy __P((struct inpcb *inp, int optname,
+ caddr_t request, int priv));
+extern int ipsec6_get_policy
+ __P((struct inpcb *inp, caddr_t request, struct mbuf **mp));
+extern int ipsec6_in_reject __P((struct mbuf *, struct inpcb *));
+
+extern size_t ipsec6_hdrsiz __P((struct mbuf *, u_int, struct inpcb *));
+
+struct ip6_hdr;
+
+extern const char *ipsec6_logpacketstr __P((struct ip6_hdr *, u_int32_t));
+extern int ipsec6_output_trans __P((struct ipsec_output_state *, u_char *,
+ struct mbuf *, struct secpolicy *,
+ int, int *));
+extern int ipsec6_output_tunnel __P((struct ipsec_output_state *,
+ struct secpolicy *, int));
+extern int ipsec6_tunnel_validate __P((struct ip6_hdr *, u_int,
+ struct secasvar *));
+
+#endif /*KERNEL*/
+#endif /* _NETINET6_IPSEC6_H_ */
diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c
new file mode 100644
index 0000000..8c5824a
--- /dev/null
+++ b/sys/netinet6/mld6.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1988 Stephen Deering.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)igmp.c 8.1 (Berkeley) 7/19/93
+ */
+
+#include "opt_inet.h"
+#include "opt_ipsec.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/in6.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+#include <netinet6/mld6_var.h>
+
+#include <net/net_osdep.h>
+
+/*
+ * Protocol constants
+ */
+
+/* denotes that the MLD max response delay field specifies time in milliseconds */
+#define MLD6_TIMER_SCALE 1000
+/*
+ * time between repetitions of a node's initial report of interest in a
+ * multicast address(in seconds)
+ */
+#define MLD6_UNSOLICITED_REPORT_INTERVAL 10
+
+static struct ip6_pktopts ip6_opts;
+static int mld6_timers_are_running;
+/* XXX: These are necessary for KAME's link-local hack */
+static struct in6_addr mld6_all_nodes_linklocal =
+ IN6ADDR_LINKLOCAL_ALLNODES_INIT;
+static struct in6_addr mld6_all_routers_linklocal =
+ IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
+
+static void mld6_sendpkt __P((struct in6_multi *, int,
+ const struct in6_addr *));
+
+void
+mld6_init()
+{
+ static u_int8_t hbh_buf[8];
+ struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
+ u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
+
+ mld6_timers_are_running = 0;
+
+ /* ip6h_nxt will be fill in later */
+ hbh->ip6h_len = 0; /* (8 >> 3) - 1*/
+
+ /* XXX: grotty hard coding... */
+ hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */
+ hbh_buf[3] = 0;
+ hbh_buf[4] = IP6OPT_RTALERT;
+ hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
+ bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t));
+
+ ip6_opts.ip6po_hbh = hbh;
+ /* We will specify the hoplimit by a multicast option. */
+ ip6_opts.ip6po_hlim = -1;
+}
+
+void
+mld6_start_listening(in6m)
+ struct in6_multi *in6m;
+{
+ int s = splnet();
+
+ /*
+ * (draft-ietf-ipngwg-mld, page 10)
+ * The node never sends a Report or Done for the link-scope all-nodes
+ * address.
+ * MLD messages are never sent for multicast addresses whose scope is 0
+ * (reserved) or 1 (node-local).
+ */
+ mld6_all_nodes_linklocal.s6_addr16[1] =
+ htons(in6m->in6m_ifp->if_index); /* XXX */
+ if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal) ||
+ IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) {
+ in6m->in6m_timer = 0;
+ in6m->in6m_state = MLD6_OTHERLISTENER;
+ } else {
+ mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, NULL);
+ in6m->in6m_timer = MLD6_RANDOM_DELAY(
+ MLD6_UNSOLICITED_REPORT_INTERVAL * PR_FASTHZ);
+ in6m->in6m_state = MLD6_IREPORTEDLAST;
+ mld6_timers_are_running = 1;
+ }
+ splx(s);
+}
+
+void
+mld6_stop_listening(in6m)
+ struct in6_multi *in6m;
+{
+ mld6_all_nodes_linklocal.s6_addr16[1] =
+ htons(in6m->in6m_ifp->if_index); /* XXX */
+ mld6_all_routers_linklocal.s6_addr16[1] =
+ htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */
+
+ if (in6m->in6m_state == MLD6_IREPORTEDLAST &&
+ (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal)) &&
+ IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > IPV6_ADDR_SCOPE_NODELOCAL)
+ mld6_sendpkt(in6m, MLD6_LISTENER_DONE,
+ &mld6_all_routers_linklocal);
+}
+
+void
+mld6_input(m, off)
+ struct mbuf *m;
+ int off;
+{
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct mld6_hdr *mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off);
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct in6_multi *in6m;
+ struct in6_ifaddr *ia;
+ struct ifmultiaddr *ifma;
+ int timer; /* timer value in the MLD query header */
+
+ /* source address validation */
+ if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
+ log(LOG_ERR,
+ "mld6_input: src %s is not link-local\n",
+ ip6_sprintf(&ip6->ip6_src));
+ /*
+ * spec(draft-ietf-ipngwg-mld) does not explicitly
+ * specify to discard the packet from a non link-local
+ * source address. But we believe it's expected to do so.
+ */
+ return;
+ }
+
+ /*
+ * In the MLD6 specification, there are 3 states and a flag.
+ *
+ * In Non-Listener state, we simply don't have a membership record.
+ * In Delaying Listener state, our timer is running (in6m->in6m_timer)
+ * In Idle Listener state, our timer is not running (in6m->in6m_timer==0)
+ *
+ * The flag is in6m->in6m_state, it is set to MLD6_OTHERLISTENER if
+ * we have heard a report from another member, or MLD6_IREPORTEDLAST
+ * if we sent the last report.
+ */
+ switch(mldh->mld6_type) {
+ case MLD6_LISTENER_QUERY:
+ if (ifp->if_flags & IFF_LOOPBACK)
+ break;
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld6_addr) &&
+ !IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr))
+ break; /* print error or log stat? */
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr))
+ mldh->mld6_addr.s6_addr16[1] =
+ htons(ifp->if_index); /* XXX */
+
+ /*
+ * - Start the timers in all of our membership records
+ * that the query applies to for the interface on
+ * which the query arrived excl. those that belong
+ * to the "all-nodes" group (ff02::1).
+ * - Restart any timer that is already running but has
+ * A value longer than the requested timeout.
+ * - Use the value specified in the query message as
+ * the maximum timeout.
+ */
+ IFP_TO_IA6(ifp, ia);
+ if (ia == NULL)
+ break;
+
+ /*
+ * XXX: System timer resolution is too low to handle Max
+ * Response Delay, so set 1 to the internal timer even if
+ * the calculated value equals to zero when Max Response
+ * Delay is positive.
+ */
+ timer = ntohs(mldh->mld6_maxdelay)*PR_FASTHZ/MLD6_TIMER_SCALE;
+ if (timer == 0 && mldh->mld6_maxdelay)
+ timer = 1;
+ mld6_all_nodes_linklocal.s6_addr16[1] =
+ htons(ifp->if_index); /* XXX */
+
+ LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_INET6)
+ continue;
+ in6m = (struct in6_multi *)ifma->ifma_protospec;
+ if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr,
+ &mld6_all_nodes_linklocal) ||
+ IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
+ IPV6_ADDR_SCOPE_LINKLOCAL)
+ continue;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld6_addr) ||
+ IN6_ARE_ADDR_EQUAL(&mldh->mld6_addr,
+ &in6m->in6m_addr))
+ {
+ if (timer == 0) {
+ /* send a report immediately */
+ mld6_sendpkt(in6m, MLD6_LISTENER_REPORT,
+ NULL);
+ in6m->in6m_timer = 0; /* reset timer */
+ in6m->in6m_state = MLD6_IREPORTEDLAST;
+ } else if (in6m->in6m_timer == 0 || /*idle state*/
+ in6m->in6m_timer > timer) {
+ in6m->in6m_timer =
+ MLD6_RANDOM_DELAY(timer);
+ mld6_timers_are_running = 1;
+ }
+ }
+ }
+
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr))
+ mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */
+ break;
+ case MLD6_LISTENER_REPORT:
+ /*
+ * For fast leave to work, we have to know that we are the
+ * last person to send a report for this group. Reports
+ * can potentially get looped back if we are a multicast
+ * router, so discard reports sourced by me.
+ * Note that it is impossible to check IFF_LOOPBACK flag of
+ * ifp for this purpose, since ip6_mloopback pass the physical
+ * interface to looutput.
+ */
+ if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
+ break;
+
+ if (!IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr))
+ break;
+
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr))
+ mldh->mld6_addr.s6_addr16[1] =
+ htons(ifp->if_index); /* XXX */
+ /*
+ * If we belong to the group being reported, stop
+ * our timer for that group.
+ */
+ IN6_LOOKUP_MULTI(mldh->mld6_addr, ifp, in6m);
+ if (in6m) {
+ in6m->in6m_timer = 0; /* transit to idle state */
+ in6m->in6m_state = MLD6_OTHERLISTENER; /* clear flag */
+ }
+
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr))
+ mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */
+ break;
+ default: /* this is impossible */
+ log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld6_type);
+ break;
+ }
+}
+
+void
+mld6_fasttimeo()
+{
+ register struct in6_multi *in6m;
+ struct in6_multistep step;
+ int s;
+
+ /*
+ * Quick check to see if any work needs to be done, in order
+ * to minimize the overhead of fasttimo processing.
+ */
+ if (!mld6_timers_are_running)
+ return;
+
+ s = splnet();
+ mld6_timers_are_running = 0;
+ IN6_FIRST_MULTI(step, in6m);
+ while (in6m != NULL) {
+ if (in6m->in6m_timer == 0) {
+ /* do nothing */
+ } else if (--in6m->in6m_timer == 0) {
+ mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, NULL);
+ in6m->in6m_state = MLD6_IREPORTEDLAST;
+ } else {
+ mld6_timers_are_running = 1;
+ }
+ IN6_NEXT_MULTI(step, in6m);
+ }
+ splx(s);
+}
+
+static void
+mld6_sendpkt(in6m, type, dst)
+ struct in6_multi *in6m;
+ int type;
+ const struct in6_addr *dst;
+{
+ struct mbuf *mh, *md;
+ struct mld6_hdr *mldh;
+ struct ip6_hdr *ip6;
+ struct ip6_moptions im6o;
+ struct in6_ifaddr *ia;
+ struct ifnet *ifp = in6m->in6m_ifp;
+ struct ifnet *outif = NULL;
+
+ /*
+ * At first, find a link local address on the outgoing interface
+ * to use as the source address of the MLD packet.
+ */
+ if ((ia = in6ifa_ifpforlinklocal(ifp)) == NULL)
+ return;
+
+ /*
+ * Allocate mbufs to store ip6 header and MLD header.
+ * We allocate 2 mbufs and make chain in advance because
+ * it is more convenient when inserting the hop-by-hop option later.
+ */
+ MGETHDR(mh, M_DONTWAIT, MT_HEADER);
+ if (mh == NULL)
+ return;
+ MGET(md, M_DONTWAIT, MT_DATA);
+ if (md == NULL) {
+ m_free(mh);
+ return;
+ }
+ mh->m_next = md;
+
+#ifdef IPSEC
+ mh->m_pkthdr.rcvif = NULL;
+#endif
+ mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld6_hdr);
+ mh->m_len = sizeof(struct ip6_hdr);
+ MH_ALIGN(mh, sizeof(struct ip6_hdr));
+
+ /* fill in the ip6 header */
+ ip6 = mtod(mh, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc = IPV6_VERSION;
+ /* ip6_plen will be set later */
+ ip6->ip6_nxt = IPPROTO_ICMPV6;
+ /* ip6_hlim will be set by im6o.im6o_multicast_hlim */
+ ip6->ip6_src = ia->ia_addr.sin6_addr;
+ ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
+
+ /* fill in the MLD header */
+ md->m_len = sizeof(struct mld6_hdr);
+ mldh = mtod(md, struct mld6_hdr *);
+ mldh->mld6_type = type;
+ mldh->mld6_code = 0;
+ mldh->mld6_cksum = 0;
+ /* XXX: we assume the function will not be called for query messages */
+ mldh->mld6_maxdelay = 0;
+ mldh->mld6_reserved = 0;
+ mldh->mld6_addr = in6m->in6m_addr;
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr))
+ mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */
+ mldh->mld6_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
+ sizeof(struct mld6_hdr));
+
+ /* construct multicast option */
+ bzero(&im6o, sizeof(im6o));
+ im6o.im6o_multicast_ifp = ifp;
+ im6o.im6o_multicast_hlim = 1;
+
+ /*
+ * Request loopback of the report if we are acting as a multicast
+ * router, so that the process-level routing daemon can hear it.
+ */
+ im6o.im6o_multicast_loop = 0;
+
+ /* increment output statictics */
+ icmp6stat.icp6s_outhist[type]++;
+
+ ip6_output(mh, &ip6_opts, NULL, 0, &im6o, &outif);
+ if (outif) {
+ icmp6_ifstat_inc(outif, ifs6_out_msg);
+ switch(type) {
+ case MLD6_LISTENER_QUERY:
+ icmp6_ifstat_inc(outif, ifs6_out_mldquery);
+ break;
+ case MLD6_LISTENER_REPORT:
+ icmp6_ifstat_inc(outif, ifs6_out_mldreport);
+ break;
+ case MLD6_LISTENER_DONE:
+ icmp6_ifstat_inc(outif, ifs6_out_mlddone);
+ break;
+ }
+ }
+}
diff --git a/sys/netinet6/mld6_var.h b/sys/netinet6/mld6_var.h
new file mode 100644
index 0000000..ab50c59
--- /dev/null
+++ b/sys/netinet6/mld6_var.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETINET6_MLD6_VAR_H_
+#define _NETINET6_MLD6_VAR_H_
+
+#ifdef _KERNEL
+
+#define MLD6_RANDOM_DELAY(X) (random() % (X) + 1)
+
+/*
+ * States for MLD stop-listening processing
+ */
+#define MLD6_OTHERLISTENER 0
+#define MLD6_IREPORTEDLAST 1
+
+void mld6_init __P((void));
+void mld6_input __P((struct mbuf *, int));
+void mld6_start_listening __P((struct in6_multi *));
+void mld6_stop_listening __P((struct in6_multi *));
+void mld6_fasttimeo __P((void));
+#endif /* _KERNEL */
+
+#endif /* _NETINET6_MLD6_VAR_H_ */
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
new file mode 100644
index 0000000..87eab9d
--- /dev/null
+++ b/sys/netinet6/nd6.c
@@ -0,0 +1,1531 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * XXX
+ * KAME 970409 note:
+ * BSD/OS version heavily modifies this code, related to llinfo.
+ * Since we don't have BSD/OS version of net/route.c in our hand,
+ * I left the code mostly as it was in 970310. -- itojun
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/if_atm.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/if_fddi.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet6/in6_prefix.h>
+#include <netinet6/icmp6.h>
+
+#include "loop.h"
+
+#include <net/net_osdep.h>
+
+#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */
+#define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */
+
+#define SIN6(s) ((struct sockaddr_in6 *)s)
+#define SDL(s) ((struct sockaddr_dl *)s)
+
+/* timer values */
+int nd6_prune = 1; /* walk list every 1 seconds */
+int nd6_delay = 5; /* delay first probe time 5 second */
+int nd6_umaxtries = 3; /* maximum unicast query */
+int nd6_mmaxtries = 3; /* maximum multicast query */
+int nd6_useloopback = 1; /* use loopback interface for local traffic */
+int nd6_proxyall = 0; /* enable Proxy Neighbor Advertisement */
+
+/* for debugging? */
+static int nd6_inuse, nd6_allocated;
+
+struct llinfo_nd6 llinfo_nd6 = {&llinfo_nd6, &llinfo_nd6};
+struct nd_ifinfo *nd_ifinfo = NULL;
+struct nd_drhead nd_defrouter = { 0 };
+struct nd_prhead nd_prefix = { 0 };
+
+int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL;
+static struct sockaddr_in6 all1_sa;
+
+static void nd6_slowtimo __P((void *));
+
+void
+nd6_init()
+{
+ static int nd6_init_done = 0;
+ int i;
+
+ if (nd6_init_done) {
+ log(LOG_NOTICE, "nd6_init called more than once(ignored)\n");
+ return;
+ }
+
+ all1_sa.sin6_family = AF_INET6;
+ all1_sa.sin6_len = sizeof(struct sockaddr_in6);
+ for (i = 0; i < sizeof(all1_sa.sin6_addr); i++)
+ all1_sa.sin6_addr.s6_addr[i] = 0xff;
+
+ nd6_init_done = 1;
+
+ /* start timer */
+ timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz);
+}
+
+void
+nd6_ifattach(ifp)
+ struct ifnet *ifp;
+{
+ static size_t if_indexlim = 8;
+
+ /*
+ * We have some arrays that should be indexed by if_index.
+ * since if_index will grow dynamically, they should grow too.
+ */
+ if (nd_ifinfo == NULL || if_index >= if_indexlim) {
+ size_t n;
+ caddr_t q;
+
+ while (if_index >= if_indexlim)
+ if_indexlim <<= 1;
+
+ /* grow nd_ifinfo */
+ n = if_indexlim * sizeof(struct nd_ifinfo);
+ q = (caddr_t)malloc(n, M_IP6NDP, M_WAITOK);
+ bzero(q, n);
+ if (nd_ifinfo) {
+ bcopy((caddr_t)nd_ifinfo, q, n/2);
+ free((caddr_t)nd_ifinfo, M_IP6NDP);
+ }
+ nd_ifinfo = (struct nd_ifinfo *)q;
+ }
+
+#define ND nd_ifinfo[ifp->if_index]
+ ND.linkmtu = ifindex2ifnet[ifp->if_index]->if_mtu;
+ ND.chlim = IPV6_DEFHLIM;
+ ND.basereachable = REACHABLE_TIME;
+ ND.reachable = ND_COMPUTE_RTIME(ND.basereachable);
+ ND.retrans = RETRANS_TIMER;
+ ND.receivedra = 0;
+ nd6_setmtu(ifp);
+#undef ND
+}
+
+/*
+ * Reset ND level link MTU. This function is called when the physical MTU
+ * changes, which means we might have to adjust the ND level MTU.
+ */
+void
+nd6_setmtu(ifp)
+ struct ifnet *ifp;
+{
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+ struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index];
+ u_long oldmaxmtu = ndi->maxmtu;
+ u_long oldlinkmtu = ndi->linkmtu;
+
+ switch(ifp->if_type) {
+ case IFT_ARCNET: /* XXX MTU handling needs more work */
+ ndi->maxmtu = MIN(60480, ifp->if_mtu);
+ break;
+ case IFT_ETHER:
+ ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu);
+ break;
+ case IFT_FDDI:
+ ndi->maxmtu = MIN(FDDIIPMTU, ifp->if_mtu);
+ break;
+ case IFT_ATM:
+ ndi->maxmtu = MIN(ATMMTU, ifp->if_mtu);
+ break;
+ default:
+ ndi->maxmtu = ifp->if_mtu;
+ break;
+ }
+
+ if (oldmaxmtu != ndi->maxmtu) {
+ /*
+ * If the ND level MTU is not set yet, or if the maxmtu
+ * is reset to a smaller value than the ND level MTU,
+ * also reset the ND level MTU.
+ */
+ if (ndi->linkmtu == 0 ||
+ ndi->maxmtu < ndi->linkmtu) {
+ ndi->linkmtu = ndi->maxmtu;
+ /* also adjust in6_maxmtu if necessary. */
+ if (oldlinkmtu == 0) {
+ /*
+ * XXX: the case analysis is grotty, but
+ * it is not efficient to call in6_setmaxmtu()
+ * here when we are during the initialization
+ * procedure.
+ */
+ if (in6_maxmtu < ndi->linkmtu)
+ in6_maxmtu = ndi->linkmtu;
+ } else
+ in6_setmaxmtu();
+ }
+ }
+#undef MIN
+}
+
+void
+nd6_option_init(opt, icmp6len, ndopts)
+ void *opt;
+ int icmp6len;
+ union nd_opts *ndopts;
+{
+ bzero(ndopts, sizeof(*ndopts));
+ ndopts->nd_opts_search = (struct nd_opt_hdr *)opt;
+ ndopts->nd_opts_last
+ = (struct nd_opt_hdr *)(((u_char *)opt) + icmp6len);
+
+ if (icmp6len == 0) {
+ ndopts->nd_opts_done = 1;
+ ndopts->nd_opts_search = NULL;
+ }
+}
+
+/*
+ * Take one ND option.
+ */
+struct nd_opt_hdr *
+nd6_option(ndopts)
+ union nd_opts *ndopts;
+{
+ struct nd_opt_hdr *nd_opt;
+ int olen;
+
+ if (!ndopts)
+ panic("ndopts == NULL in nd6_option\n");
+ if (!ndopts->nd_opts_last)
+ panic("uninitialized ndopts in nd6_option\n");
+ if (!ndopts->nd_opts_search)
+ return NULL;
+ if (ndopts->nd_opts_done)
+ return NULL;
+
+ nd_opt = ndopts->nd_opts_search;
+
+ olen = nd_opt->nd_opt_len << 3;
+ if (olen == 0) {
+ /*
+ * Message validation requires that all included
+ * options have a length that is greater than zero.
+ */
+ bzero(ndopts, sizeof(*ndopts));
+ return NULL;
+ }
+
+ ndopts->nd_opts_search = (struct nd_opt_hdr *)((caddr_t)nd_opt + olen);
+ if (!(ndopts->nd_opts_search < ndopts->nd_opts_last)) {
+ ndopts->nd_opts_done = 1;
+ ndopts->nd_opts_search = NULL;
+ }
+ return nd_opt;
+}
+
+/*
+ * Parse multiple ND options.
+ * This function is much easier to use, for ND routines that do not need
+ * multiple options of the same type.
+ */
+int
+nd6_options(ndopts)
+ union nd_opts *ndopts;
+{
+ struct nd_opt_hdr *nd_opt;
+ int i = 0;
+
+ if (!ndopts)
+ panic("ndopts == NULL in nd6_options\n");
+ if (!ndopts->nd_opts_last)
+ panic("uninitialized ndopts in nd6_options\n");
+ if (!ndopts->nd_opts_search)
+ return 0;
+
+ while (1) {
+ nd_opt = nd6_option(ndopts);
+ if (!nd_opt && !ndopts->nd_opts_last) {
+ /*
+ * Message validation requires that all included
+ * options have a length that is greater than zero.
+ */
+ bzero(ndopts, sizeof(*ndopts));
+ return -1;
+ }
+
+ if (!nd_opt)
+ goto skip1;
+
+ switch (nd_opt->nd_opt_type) {
+ case ND_OPT_SOURCE_LINKADDR:
+ case ND_OPT_TARGET_LINKADDR:
+ case ND_OPT_MTU:
+ case ND_OPT_REDIRECTED_HEADER:
+ if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
+ printf("duplicated ND6 option found "
+ "(type=%d)\n", nd_opt->nd_opt_type);
+ /* XXX bark? */
+ } else {
+ ndopts->nd_opt_array[nd_opt->nd_opt_type]
+ = nd_opt;
+ }
+ break;
+ case ND_OPT_PREFIX_INFORMATION:
+ if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) {
+ ndopts->nd_opt_array[nd_opt->nd_opt_type]
+ = nd_opt;
+ }
+ ndopts->nd_opts_pi_end =
+ (struct nd_opt_prefix_info *)nd_opt;
+ break;
+ default:
+ /*
+ * Unknown options must be silently ignored,
+ * to accomodate future extension to the protocol.
+ */
+ log(LOG_INFO,
+ "nd6_options: unsupported option %d - "
+ "option ignored\n", nd_opt->nd_opt_type);
+ }
+
+skip1:
+ i++;
+ if (i > 10) {
+ printf("too many loop in nd opt\n");
+ break;
+ }
+
+ if (ndopts->nd_opts_done)
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * ND6 timer routine to expire default route list and prefix list
+ */
+void
+nd6_timer(ignored_arg)
+ void *ignored_arg;
+{
+ int s;
+ register struct llinfo_nd6 *ln;
+ register struct nd_defrouter *dr;
+ register struct nd_prefix *pr;
+
+ s = splnet();
+ timeout(nd6_timer, (caddr_t)0, nd6_prune * hz);
+
+ ln = llinfo_nd6.ln_next;
+ /* XXX BSD/OS separates this code -- itojun */
+ while (ln && ln != &llinfo_nd6) {
+ struct rtentry *rt;
+ struct ifnet *ifp;
+ struct sockaddr_in6 *dst;
+ struct llinfo_nd6 *next = ln->ln_next;
+
+ if ((rt = ln->ln_rt) == NULL) {
+ ln = next;
+ continue;
+ }
+ if ((ifp = rt->rt_ifp) == NULL) {
+ ln = next;
+ continue;
+ }
+ dst = (struct sockaddr_in6 *)rt_key(rt);
+
+ if (ln->ln_expire > time_second) {
+ ln = next;
+ continue;
+ }
+
+ /* sanity check */
+ if (!rt)
+ panic("rt=0 in nd6_timer(ln=%p)\n", ln);
+ if (!dst)
+ panic("dst=0 in nd6_timer(ln=%p)\n", ln);
+
+ switch (ln->ln_state) {
+ case ND6_LLINFO_INCOMPLETE:
+ if (ln->ln_asked < nd6_mmaxtries) {
+ ln->ln_asked++;
+ ln->ln_expire = time_second +
+ nd_ifinfo[ifp->if_index].retrans / 1000;
+ nd6_ns_output(ifp, NULL, &dst->sin6_addr,
+ ln, 0);
+ } else {
+ struct mbuf *m = ln->ln_hold;
+ if (m) {
+ if (rt->rt_ifp) {
+ /*
+ * Fake rcvif to make ICMP error
+ * more helpful in diagnosing
+ * for the receiver.
+ * XXX: should we consider
+ * older rcvif?
+ */
+ m->m_pkthdr.rcvif = rt->rt_ifp;
+ }
+ icmp6_error(m, ICMP6_DST_UNREACH,
+ ICMP6_DST_UNREACH_ADDR, 0);
+ ln->ln_hold = NULL;
+ }
+ nd6_free(rt);
+ }
+ break;
+ case ND6_LLINFO_REACHABLE:
+ if (ln->ln_expire) {
+ ln->ln_state = ND6_LLINFO_STALE;
+ }
+ break;
+ /*
+ * ND6_LLINFO_STALE state requires nothing for timer
+ * routine.
+ */
+ case ND6_LLINFO_DELAY:
+ ln->ln_asked = 1;
+ ln->ln_state = ND6_LLINFO_PROBE;
+ ln->ln_expire = time_second +
+ nd_ifinfo[ifp->if_index].retrans / 1000;
+ nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr,
+ ln, 0);
+ break;
+
+ case ND6_LLINFO_PROBE:
+ if (ln->ln_asked < nd6_umaxtries) {
+ ln->ln_asked++;
+ ln->ln_expire = time_second +
+ nd_ifinfo[ifp->if_index].retrans / 1000;
+ nd6_ns_output(ifp, &dst->sin6_addr,
+ &dst->sin6_addr, ln, 0);
+ } else {
+ nd6_free(rt);
+ }
+ break;
+ case ND6_LLINFO_WAITDELETE:
+ nd6_free(rt);
+ break;
+ }
+ ln = next;
+ }
+
+ /* expire */
+ dr = nd_defrouter.lh_first;
+ while (dr) {
+ if (dr->expire && dr->expire < time_second) {
+ struct nd_defrouter *t;
+ t = dr->dr_next;
+ defrtrlist_del(dr);
+ dr = t;
+ } else
+ dr = dr->dr_next;
+ }
+ pr = nd_prefix.lh_first;
+ while (pr) {
+ struct in6_ifaddr *ia6;
+ struct in6_addrlifetime *lt6;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ ia6 = NULL;
+ else
+ ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
+
+ if (ia6) {
+ /* check address lifetime */
+ lt6 = &ia6->ia6_lifetime;
+ if (lt6->ia6t_preferred && lt6->ia6t_preferred < time_second)
+ ia6->ia6_flags |= IN6_IFF_DEPRECATED;
+ if (lt6->ia6t_expire && lt6->ia6t_expire < time_second) {
+ if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr);
+ /* xxx ND_OPT_PI_FLAG_ONLINK processing */
+ }
+ }
+
+ /*
+ * check prefix lifetime.
+ * since pltime is just for autoconf, pltime processing for
+ * prefix is not necessary.
+ *
+ * we offset expire time by NDPR_KEEP_EXPIRE, so that we
+ * can use the old prefix information to validate the
+ * next prefix information to come. See prelist_update()
+ * for actual validation.
+ */
+ if (pr->ndpr_expire
+ && pr->ndpr_expire + NDPR_KEEP_EXPIRED < time_second) {
+ struct nd_prefix *t;
+ t = pr->ndpr_next;
+
+ /*
+ * address expiration and prefix expiration are
+ * separate. NEVER perform in6_ifdel here.
+ */
+
+ prelist_remove(pr);
+ pr = t;
+ } else
+ pr = pr->ndpr_next;
+ }
+ splx(s);
+}
+
+struct rtentry *
+nd6_lookup(addr6, create, ifp)
+ struct in6_addr *addr6;
+ int create;
+ struct ifnet *ifp;
+{
+ struct rtentry *rt;
+ struct sockaddr_in6 sin6;
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = *addr6;
+ rt = rtalloc1((struct sockaddr *)&sin6, create, 0UL);
+ if (rt && (rt->rt_flags & RTF_LLINFO) == 0) {
+ /*
+ * This is the case for the default route.
+ * If we want to create a neighbor cache for the address, we
+ * should free the route for the destination and allocate an
+ * interface route.
+ */
+ if (create) {
+ RTFREE(rt);
+ rt = 0;
+ }
+ }
+ if (!rt) {
+ if (create && ifp) {
+ /*
+ * If no route is available and create is set,
+ * we allocate a host route for the destination
+ * and treat it like an interface route.
+ * This hack is necessary for a neighbor which can't
+ * be covered by our own prefix.
+ */
+ struct ifaddr *ifa =
+ ifaof_ifpforaddr((struct sockaddr *)&sin6, ifp);
+ if (ifa == NULL)
+ return(NULL);
+
+ /*
+ * Create a new route. RTF_LLINFO is necessary
+ * to create a Neighbor Cache entry for the
+ * destination in nd6_rtrequest which will be
+ * called in rtequest via ifa->ifa_rtrequest.
+ */
+ if (rtrequest(RTM_ADD, (struct sockaddr *)&sin6,
+ ifa->ifa_addr,
+ (struct sockaddr *)&all1_sa,
+ (ifa->ifa_flags |
+ RTF_HOST | RTF_LLINFO) & ~RTF_CLONING,
+ &rt))
+ log(LOG_ERR,
+ "nd6_lookup: failed to add route for a "
+ "neighbor(%s)\n", ip6_sprintf(addr6));
+ if (rt == NULL)
+ return(NULL);
+ if (rt->rt_llinfo) {
+ struct llinfo_nd6 *ln =
+ (struct llinfo_nd6 *)rt->rt_llinfo;
+ ln->ln_state = ND6_LLINFO_NOSTATE;
+ }
+ } else
+ return(NULL);
+ }
+ rt->rt_refcnt--;
+ /*
+ * Validation for the entry.
+ * XXX: we can't use rt->rt_ifp to check for the interface, since
+ * it might be the loopback interface if the entry is for our
+ * own address on a non-loopback interface. Instead, we should
+ * use rt->rt_ifa->ifa_ifp, which would specify the REAL interface.
+ */
+ if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 ||
+ rt->rt_gateway->sa_family != AF_LINK ||
+ (ifp && rt->rt_ifa->ifa_ifp != ifp)) {
+ if (create) {
+ log(LOG_DEBUG, "nd6_lookup: failed to lookup %s (if = %s)\n",
+ ip6_sprintf(addr6), ifp ? if_name(ifp) : "unspec");
+ /* xxx more logs... kazu */
+ }
+ return(0);
+ }
+ return(rt);
+}
+
+/*
+ * Detect if a given IPv6 address identifies a neighbor on a given link.
+ * XXX: should take care of the destination of a p2p link?
+ */
+int
+nd6_is_addr_neighbor(addr, ifp)
+ struct in6_addr *addr;
+ struct ifnet *ifp;
+{
+ register struct ifaddr *ifa;
+ int i;
+
+#define IFADDR6(a) ((((struct in6_ifaddr *)(a))->ia_addr).sin6_addr)
+#define IFMASK6(a) ((((struct in6_ifaddr *)(a))->ia_prefixmask).sin6_addr)
+
+ /* A link-local address is always a neighbor. */
+ if (IN6_IS_ADDR_LINKLOCAL(addr))
+ return(1);
+
+ /*
+ * If the address matches one of our addresses,
+ * it should be a neighbor.
+ */
+ for (ifa = ifp->if_addrlist.tqh_first;
+ ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ next: continue;
+
+ for (i = 0; i < 4; i++) {
+ if ((IFADDR6(ifa).s6_addr32[i] ^ addr->s6_addr32[i]) &
+ IFMASK6(ifa).s6_addr32[i])
+ goto next;
+ }
+ return(1);
+ }
+
+ /*
+ * Even if the address matches none of our addresses, it might be
+ * in the neighbor cache.
+ */
+ if (nd6_lookup(addr, 0, ifp))
+ return(1);
+
+ return(0);
+#undef IFADDR6
+#undef IFMASK6
+}
+
+/*
+ * Free an nd6 llinfo entry.
+ */
+void
+nd6_free(rt)
+ struct rtentry *rt;
+{
+ struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ struct sockaddr_dl *sdl;
+
+ if (ln->ln_router) {
+ /* remove from default router list */
+ struct nd_defrouter *dr;
+ struct in6_addr *in6;
+ int s;
+ in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
+
+ s = splnet();
+ dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->
+ sin6_addr,
+ rt->rt_ifp);
+ if (dr)
+ defrtrlist_del(dr);
+ else if (!ip6_forwarding && ip6_accept_rtadv) {
+ /*
+ * rt6_flush must be called in any case.
+ * see the comment in nd6_na_input().
+ */
+ rt6_flush(in6, rt->rt_ifp);
+ }
+ splx(s);
+ }
+
+ if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) &&
+ sdl->sdl_family == AF_LINK) {
+ sdl->sdl_alen = 0;
+ ln->ln_state = ND6_LLINFO_WAITDELETE;
+ ln->ln_asked = 0;
+ rt->rt_flags &= ~RTF_REJECT;
+ return;
+ }
+ rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt),
+ 0, (struct rtentry **)0);
+}
+
+/*
+ * Upper-layer reachability hint for Neighbor Unreachability Detection.
+ *
+ * XXX cost-effective metods?
+ */
+void
+nd6_nud_hint(rt, dst6)
+ struct rtentry *rt;
+ struct in6_addr *dst6;
+{
+ struct llinfo_nd6 *ln;
+
+ /*
+ * If the caller specified "rt", use that. Otherwise, resolve the
+ * routing table by supplied "dst6".
+ */
+ if (!rt) {
+ if (!dst6)
+ return;
+ if (!(rt = nd6_lookup(dst6, 0, NULL)))
+ return;
+ }
+
+ if ((rt->rt_flags & RTF_GATEWAY)
+ || (rt->rt_flags & RTF_LLINFO) == 0
+ || !rt->rt_llinfo
+ || !rt->rt_gateway
+ || rt->rt_gateway->sa_family != AF_LINK) {
+ /* This is not a host route. */
+ return;
+ }
+
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ if (ln->ln_state == ND6_LLINFO_INCOMPLETE)
+ return;
+
+ ln->ln_state = ND6_LLINFO_REACHABLE;
+ if (ln->ln_expire)
+ ln->ln_expire = time_second +
+ nd_ifinfo[rt->rt_ifp->if_index].reachable;
+}
+
+void
+nd6_rtrequest(req, rt, sa)
+ int req;
+ struct rtentry *rt;
+ struct sockaddr *sa; /* xxx unused */
+{
+ struct sockaddr *gate = rt->rt_gateway;
+ struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK};
+ struct ifnet *ifp = rt->rt_ifp;
+ struct ifaddr *ifa;
+
+ if (rt->rt_flags & RTF_GATEWAY)
+ return;
+
+ switch (req) {
+ case RTM_ADD:
+ /*
+ * There is no backward compatibility :)
+ *
+ * if ((rt->rt_flags & RTF_HOST) == 0 &&
+ * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
+ * rt->rt_flags |= RTF_CLONING;
+ */
+ if (rt->rt_flags & RTF_CLONING || rt->rt_flags & RTF_LLINFO) {
+ /*
+ * Case 1: This route should come from
+ * a route to interface. RTF_LLINFO flag is set
+ * for a host route whose destination should be
+ * treated as on-link.
+ */
+ rt_setgate(rt, rt_key(rt),
+ (struct sockaddr *)&null_sdl);
+ gate = rt->rt_gateway;
+ SDL(gate)->sdl_type = ifp->if_type;
+ SDL(gate)->sdl_index = ifp->if_index;
+ if (ln)
+ ln->ln_expire = time_second;
+ if (ln && ln->ln_expire == 0) {
+ /* cludge for desktops */
+ ln->ln_expire = 1;
+ }
+ if (rt->rt_flags & RTF_CLONING)
+ break;
+ }
+ /* Announce a new entry if requested. */
+ if (rt->rt_flags & RTF_ANNOUNCE)
+ nd6_na_output(ifp,
+ &SIN6(rt_key(rt))->sin6_addr,
+ &SIN6(rt_key(rt))->sin6_addr,
+ ip6_forwarding ? ND_NA_FLAG_ROUTER : 0,
+ 1);
+ /* FALLTHROUGH */
+ case RTM_RESOLVE:
+ if (gate->sa_family != AF_LINK ||
+ gate->sa_len < sizeof(null_sdl)) {
+ log(LOG_DEBUG, "nd6_rtrequest: bad gateway value\n");
+ break;
+ }
+ SDL(gate)->sdl_type = ifp->if_type;
+ SDL(gate)->sdl_index = ifp->if_index;
+ if (ln != 0)
+ break; /* This happens on a route change */
+ /*
+ * Case 2: This route may come from cloning, or a manual route
+ * add with a LL address.
+ */
+ R_Malloc(ln, struct llinfo_nd6 *, sizeof(*ln));
+ rt->rt_llinfo = (caddr_t)ln;
+ if (!ln) {
+ log(LOG_DEBUG, "nd6_rtrequest: malloc failed\n");
+ break;
+ }
+ nd6_inuse++;
+ nd6_allocated++;
+ Bzero(ln, sizeof(*ln));
+ ln->ln_rt = rt;
+ /* this is required for "ndp" command. - shin */
+ if (req == RTM_ADD) {
+ /*
+ * gate should have some valid AF_LINK entry,
+ * and ln->ln_expire should have some lifetime
+ * which is specified by ndp command.
+ */
+ ln->ln_state = ND6_LLINFO_REACHABLE;
+ } else {
+ /*
+ * When req == RTM_RESOLVE, rt is created and
+ * initialized in rtrequest(), so rt_expire is 0.
+ */
+ ln->ln_state = ND6_LLINFO_INCOMPLETE;
+ ln->ln_expire = time_second;
+ }
+ rt->rt_flags |= RTF_LLINFO;
+ ln->ln_next = llinfo_nd6.ln_next;
+ llinfo_nd6.ln_next = ln;
+ ln->ln_prev = &llinfo_nd6;
+ ln->ln_next->ln_prev = ln;
+
+ /*
+ * check if rt_key(rt) is one of my address assigned
+ * to the interface.
+ */
+ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(rt->rt_ifp,
+ &SIN6(rt_key(rt))->sin6_addr);
+ if (ifa) {
+ caddr_t macp = nd6_ifptomac(ifp);
+ ln->ln_expire = 0;
+ ln->ln_state = ND6_LLINFO_REACHABLE;
+ if (macp) {
+ Bcopy(macp, LLADDR(SDL(gate)), ifp->if_addrlen);
+ SDL(gate)->sdl_alen = ifp->if_addrlen;
+ }
+ if (nd6_useloopback) {
+ rt->rt_ifp = &loif[0]; /*XXX*/
+ /*
+ * Make sure rt_ifa be equal to the ifaddr
+ * corresponding to the address.
+ * We need this because when we refer
+ * rt_ifa->ia6_flags in ip6_input, we assume
+ * that the rt_ifa points to the address instead
+ * of the loopback address.
+ */
+ if (ifa != rt->rt_ifa) {
+ rt->rt_ifa->ifa_refcnt--;
+ ifa->ifa_refcnt++;
+ rt->rt_ifa = ifa;
+ }
+ }
+ }
+ break;
+
+ case RTM_DELETE:
+ if (!ln)
+ break;
+ nd6_inuse--;
+ ln->ln_next->ln_prev = ln->ln_prev;
+ ln->ln_prev->ln_next = ln->ln_next;
+ ln->ln_prev = NULL;
+ rt->rt_llinfo = 0;
+ rt->rt_flags &= ~RTF_LLINFO;
+ if (ln->ln_hold)
+ m_freem(ln->ln_hold);
+ Free((caddr_t)ln);
+ }
+}
+
+void
+nd6_p2p_rtrequest(req, rt, sa)
+ int req;
+ struct rtentry *rt;
+ struct sockaddr *sa; /* xxx unused */
+{
+ struct sockaddr *gate = rt->rt_gateway;
+ static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK};
+ struct ifnet *ifp = rt->rt_ifp;
+ struct ifaddr *ifa;
+
+ if (rt->rt_flags & RTF_GATEWAY)
+ return;
+
+ switch (req) {
+ case RTM_ADD:
+ /*
+ * There is no backward compatibility :)
+ *
+ * if ((rt->rt_flags & RTF_HOST) == 0 &&
+ * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
+ * rt->rt_flags |= RTF_CLONING;
+ */
+ if (rt->rt_flags & RTF_CLONING) {
+ /*
+ * Case 1: This route should come from
+ * a route to interface.
+ */
+ rt_setgate(rt, rt_key(rt),
+ (struct sockaddr *)&null_sdl);
+ gate = rt->rt_gateway;
+ SDL(gate)->sdl_type = ifp->if_type;
+ SDL(gate)->sdl_index = ifp->if_index;
+ break;
+ }
+ /* Announce a new entry if requested. */
+ if (rt->rt_flags & RTF_ANNOUNCE)
+ nd6_na_output(ifp,
+ &SIN6(rt_key(rt))->sin6_addr,
+ &SIN6(rt_key(rt))->sin6_addr,
+ ip6_forwarding ? ND_NA_FLAG_ROUTER : 0,
+ 1);
+ /* FALLTHROUGH */
+ case RTM_RESOLVE:
+ /*
+ * check if rt_key(rt) is one of my address assigned
+ * to the interface.
+ */
+ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(rt->rt_ifp,
+ &SIN6(rt_key(rt))->sin6_addr);
+ if (ifa) {
+ if (nd6_useloopback) {
+ rt->rt_ifp = &loif[0]; /*XXX*/
+ }
+ }
+ break;
+ }
+}
+
+int
+nd6_ioctl(cmd, data, ifp)
+ u_long cmd;
+ caddr_t data;
+ struct ifnet *ifp;
+{
+ struct in6_drlist *drl = (struct in6_drlist *)data;
+ struct in6_prlist *prl = (struct in6_prlist *)data;
+ struct in6_ndireq *ndi = (struct in6_ndireq *)data;
+ struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data;
+ struct nd_defrouter *dr, any;
+ struct nd_prefix *pr;
+ struct rtentry *rt;
+ int i = 0, error = 0;
+ int s;
+
+ switch (cmd) {
+ case SIOCGDRLST_IN6:
+ bzero(drl, sizeof(*drl));
+ s = splnet();
+ dr = nd_defrouter.lh_first;
+ while (dr && i < DRLSTSIZ) {
+ drl->defrouter[i].rtaddr = dr->rtaddr;
+ if (IN6_IS_ADDR_LINKLOCAL(&drl->defrouter[i].rtaddr)) {
+ /* XXX: need to this hack for KAME stack */
+ drl->defrouter[i].rtaddr.s6_addr16[1] = 0;
+ } else
+ log(LOG_ERR,
+ "default router list contains a "
+ "non-linklocal address(%s)\n",
+ ip6_sprintf(&drl->defrouter[i].rtaddr));
+
+ drl->defrouter[i].flags = dr->flags;
+ drl->defrouter[i].rtlifetime = dr->rtlifetime;
+ drl->defrouter[i].expire = dr->expire;
+ drl->defrouter[i].if_index = dr->ifp->if_index;
+ i++;
+ dr = dr->dr_next;
+ }
+ splx(s);
+ break;
+ case SIOCGPRLST_IN6:
+ bzero(prl, sizeof(*prl));
+ s = splnet();
+ pr = nd_prefix.lh_first;
+ while (pr && i < PRLSTSIZ) {
+ struct nd_pfxrouter *pfr;
+ int j;
+
+ prl->prefix[i].prefix = pr->ndpr_prefix.sin6_addr;
+ prl->prefix[i].raflags = pr->ndpr_raf;
+ prl->prefix[i].prefixlen = pr->ndpr_plen;
+ prl->prefix[i].vltime = pr->ndpr_vltime;
+ prl->prefix[i].pltime = pr->ndpr_pltime;
+ prl->prefix[i].if_index = pr->ndpr_ifp->if_index;
+ prl->prefix[i].expire = pr->ndpr_expire;
+
+ pfr = pr->ndpr_advrtrs.lh_first;
+ j = 0;
+ while(pfr) {
+ if (j < DRLSTSIZ) {
+#define RTRADDR prl->prefix[i].advrtr[j]
+ RTRADDR = pfr->router->rtaddr;
+ if (IN6_IS_ADDR_LINKLOCAL(&RTRADDR)) {
+ /* XXX: hack for KAME */
+ RTRADDR.s6_addr16[1] = 0;
+ } else
+ log(LOG_ERR,
+ "a router(%s) advertises "
+ "a prefix with "
+ "non-link local address\n",
+ ip6_sprintf(&RTRADDR));
+#undef RTRADDR
+ }
+ j++;
+ pfr = pfr->pfr_next;
+ }
+ prl->prefix[i].advrtrs = j;
+
+ i++;
+ pr = pr->ndpr_next;
+ }
+ splx(s);
+ {
+ struct rr_prefix *rpp;
+
+ for (rpp = LIST_FIRST(&rr_prefix); rpp;
+ rpp = LIST_NEXT(rpp, rp_entry)) {
+ if (i >= PRLSTSIZ)
+ break;
+ prl->prefix[i].prefix = rpp->rp_prefix.sin6_addr;
+ prl->prefix[i].raflags = rpp->rp_raf;
+ prl->prefix[i].prefixlen = rpp->rp_plen;
+ prl->prefix[i].vltime = rpp->rp_vltime;
+ prl->prefix[i].pltime = rpp->rp_pltime;
+ prl->prefix[i].if_index = rpp->rp_ifp->if_index;
+ prl->prefix[i].expire = rpp->rp_expire;
+ prl->prefix[i].advrtrs = 0;
+ i++;
+ }
+ }
+
+ break;
+ case SIOCGIFINFO_IN6:
+ ndi->ndi = nd_ifinfo[ifp->if_index];
+ break;
+ case SIOCSNDFLUSH_IN6:
+ /* flush default router list */
+ /*
+ * xxx sumikawa: should not delete route if default
+ * route equals to the top of default router list
+ */
+ bzero(&any, sizeof(any));
+ defrouter_delreq(&any, 0);
+ /* xxx sumikawa: flush prefix list */
+ break;
+ case SIOCSPFXFLUSH_IN6:
+ {
+ /* flush all the prefix advertised by routers */
+ struct nd_prefix *pr, *next;
+
+ s = splnet();
+ for (pr = nd_prefix.lh_first; pr; pr = next) {
+ next = pr->ndpr_next;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr);
+ prelist_remove(pr);
+ }
+ splx(s);
+ break;
+ }
+ case SIOCSRTRFLUSH_IN6:
+ {
+ /* flush all the default routers */
+ struct nd_defrouter *dr, *next;
+
+ s = splnet();
+ if ((dr = nd_defrouter.lh_first) != NULL) {
+ /*
+ * The first entry of the list may be stored in
+ * the routing table, so we'll delete it later.
+ */
+ for (dr = dr->dr_next; dr; dr = next) {
+ next = dr->dr_next;
+ defrtrlist_del(dr);
+ }
+ defrtrlist_del(nd_defrouter.lh_first);
+ }
+ splx(s);
+ break;
+ }
+ case SIOCGNBRINFO_IN6:
+ {
+ struct llinfo_nd6 *ln;
+ struct in6_addr nb_addr = nbi->addr; /* make local for safety */
+
+ /*
+ * XXX: KAME specific hack for scoped addresses
+ * XXXX: for other scopes than link-local?
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&nbi->addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&nbi->addr)) {
+ u_int16_t *idp = (u_int16_t *)&nb_addr.s6_addr[2];
+
+ if (*idp == 0)
+ *idp = htons(ifp->if_index);
+ }
+
+ s = splnet();
+ if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL) {
+ error = EINVAL;
+ break;
+ }
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ nbi->state = ln->ln_state;
+ nbi->asked = ln->ln_asked;
+ nbi->isrouter = ln->ln_router;
+ nbi->expire = ln->ln_expire;
+ splx(s);
+
+ break;
+ }
+ }
+ return(error);
+}
+
+/*
+ * Create neighbor cache entry and cache link-layer address,
+ * on reception of inbound ND6 packets. (RS/RA/NS/redirect)
+ */
+struct rtentry *
+nd6_cache_lladdr(ifp, from, lladdr, lladdrlen, type, code)
+ struct ifnet *ifp;
+ struct in6_addr *from;
+ char *lladdr;
+ int lladdrlen;
+ int type; /* ICMP6 type */
+ int code; /* type dependent information */
+{
+ struct rtentry *rt = NULL;
+ struct llinfo_nd6 *ln = NULL;
+ int is_newentry;
+ struct sockaddr_dl *sdl = NULL;
+ int do_update;
+ int olladdr;
+ int llchange;
+ int newstate = 0;
+
+ if (!ifp)
+ panic("ifp == NULL in nd6_cache_lladdr");
+ if (!from)
+ panic("from == NULL in nd6_cache_lladdr");
+
+ /* nothing must be updated for unspecified address */
+ if (IN6_IS_ADDR_UNSPECIFIED(from))
+ return NULL;
+
+ /*
+ * Validation about ifp->if_addrlen and lladdrlen must be done in
+ * the caller.
+ *
+ * XXX If the link does not have link-layer adderss, what should
+ * we do? (ifp->if_addrlen == 0)
+ * Spec says nothing in sections for RA, RS and NA. There's small
+ * description on it in NS section (RFC 2461 7.2.3).
+ */
+
+ rt = nd6_lookup(from, 0, ifp);
+ if (!rt) {
+ rt = nd6_lookup(from, 1, ifp);
+ is_newentry = 1;
+ } else
+ is_newentry = 0;
+
+ if (!rt)
+ return NULL;
+ if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) {
+fail:
+ nd6_free(rt);
+ return NULL;
+ }
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ if (!ln)
+ goto fail;
+ if (!rt->rt_gateway)
+ goto fail;
+ if (rt->rt_gateway->sa_family != AF_LINK)
+ goto fail;
+ sdl = SDL(rt->rt_gateway);
+
+ olladdr = (sdl->sdl_alen) ? 1 : 0;
+ if (olladdr && lladdr) {
+ if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen))
+ llchange = 1;
+ else
+ llchange = 0;
+ } else
+ llchange = 0;
+
+ /*
+ * newentry olladdr lladdr llchange (*=record)
+ * 0 n n -- (1)
+ * 0 y n -- (2)
+ * 0 n y -- (3) * STALE
+ * 0 y y n (4) *
+ * 0 y y y (5) * STALE
+ * 1 -- n -- (6) NOSTATE(= PASSIVE)
+ * 1 -- y -- (7) * STALE
+ */
+
+ if (lladdr) { /*(3-5) and (7)*/
+ /*
+ * Record source link-layer address
+ * XXX is it dependent to ifp->if_type?
+ */
+ sdl->sdl_alen = ifp->if_addrlen;
+ bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
+ }
+
+ if (!is_newentry) {
+ if ((!olladdr && lladdr) /*(3)*/
+ || (olladdr && lladdr && llchange)) { /*(5)*/
+ do_update = 1;
+ newstate = ND6_LLINFO_STALE;
+ } else /*(1-2,4)*/
+ do_update = 0;
+ } else {
+ do_update = 1;
+ if (!lladdr) /*(6)*/
+ newstate = ND6_LLINFO_NOSTATE;
+ else /*(7)*/
+ newstate = ND6_LLINFO_STALE;
+ }
+
+ if (do_update) {
+ /*
+ * Update the state of the neighbor cache.
+ */
+ ln->ln_state = newstate;
+
+ if (ln->ln_state == ND6_LLINFO_STALE) {
+ rt->rt_flags &= ~RTF_REJECT;
+ if (ln->ln_hold) {
+ nd6_output(ifp, ln->ln_hold,
+ (struct sockaddr_in6 *)rt_key(rt),
+ rt);
+ ln->ln_hold = 0;
+ }
+ } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
+ /* probe right away */
+ ln->ln_expire = time_second;
+ }
+ }
+
+ /*
+ * ICMP6 type dependent behavior.
+ *
+ * NS: clear IsRouter if new entry
+ * RS: clear IsRouter
+ * RA: set IsRouter if there's lladdr
+ * redir: clear IsRouter if new entry
+ *
+ * RA case, (1):
+ * The spec says that we must set IsRouter in the following cases:
+ * - If lladdr exist, set IsRouter. This means (1-5).
+ * - If it is old entry (!newentry), set IsRouter. This means (7).
+ * So, based on the spec, in (1-5) and (7) cases we must set IsRouter.
+ * A quetion arises for (1) case. (1) case has no lladdr in the
+ * neighbor cache, this is similar to (6).
+ * This case is rare but we figured that we MUST NOT set IsRouter.
+ *
+ * newentry olladdr lladdr llchange NS RS RA redir
+ * D R
+ * 0 n n -- (1) c ? s
+ * 0 y n -- (2) c s s
+ * 0 n y -- (3) c s s
+ * 0 y y n (4) c s s
+ * 0 y y y (5) c s s
+ * 1 -- n -- (6) c c c s
+ * 1 -- y -- (7) c c s c s
+ *
+ * (c=clear s=set)
+ */
+ switch (type & 0xff) {
+ case ND_NEIGHBOR_SOLICIT:
+ /*
+ * New entry must have is_router flag cleared.
+ */
+ if (is_newentry) /*(6-7)*/
+ ln->ln_router = 0;
+ break;
+ case ND_REDIRECT:
+ /*
+ * If the icmp is a redirect to a better router, always set the
+ * is_router flag. Otherwise, if the entry is newly created,
+ * clear the flag. [RFC 2461, sec 8.3]
+ *
+ */
+ if (code == ND_REDIRECT_ROUTER)
+ ln->ln_router = 1;
+ else if (is_newentry) /*(6-7)*/
+ ln->ln_router = 0;
+ break;
+ case ND_ROUTER_SOLICIT:
+ /*
+ * is_router flag must always be cleared.
+ */
+ ln->ln_router = 0;
+ break;
+ case ND_ROUTER_ADVERT:
+ /*
+ * Mark an entry with lladdr as a router.
+ */
+ if ((!is_newentry && (olladdr || lladdr)) /*(2-5)*/
+ || (is_newentry && lladdr)) { /*(7)*/
+ ln->ln_router = 1;
+ }
+ break;
+ }
+
+ return rt;
+}
+
+static void
+nd6_slowtimo(ignored_arg)
+ void *ignored_arg;
+{
+ int s = splnet();
+ register int i;
+ register struct nd_ifinfo *nd6if;
+
+ timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz);
+ for (i = 1; i < if_index + 1; i++) {
+ nd6if = &nd_ifinfo[i];
+ if (nd6if->basereachable && /* already initialized */
+ (nd6if->recalctm -= ND6_SLOWTIMER_INTERVAL) <= 0) {
+ /*
+ * Since reachable time rarely changes by router
+ * advertisements, we SHOULD insure that a new random
+ * value gets recomputed at least once every few hours.
+ * (RFC 2461, 6.3.4)
+ */
+ nd6if->recalctm = nd6_recalc_reachtm_interval;
+ nd6if->reachable = ND_COMPUTE_RTIME(nd6if->basereachable);
+ }
+ }
+ splx(s);
+}
+
+#define senderr(e) { error = (e); goto bad;}
+int
+nd6_output(ifp, m0, dst, rt0)
+ register struct ifnet *ifp;
+ struct mbuf *m0;
+ struct sockaddr_in6 *dst;
+ struct rtentry *rt0;
+{
+ register struct mbuf *m = m0;
+ register struct rtentry *rt = rt0;
+ struct llinfo_nd6 *ln = NULL;
+ int error = 0;
+
+ if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
+ goto sendpkt;
+
+ /*
+ * XXX: we currently do not make neighbor cache on any interface
+ * other than ARCnet, Ethernet and FDDI.
+ */
+ switch (ifp->if_type) {
+ case IFT_ARCNET:
+ case IFT_ETHER:
+ case IFT_FDDI:
+ break;
+ default:
+ goto sendpkt;
+ }
+
+ /*
+ * next hop determination. This routine is derived from ether_outpout.
+ */
+ if (rt) {
+ if ((rt->rt_flags & RTF_UP) == 0) {
+ if ((rt0 = rt = rtalloc1((struct sockaddr *)dst, 1, 0UL)) !=
+ NULL)
+ {
+ rt->rt_refcnt--;
+ if (rt->rt_ifp != ifp)
+ return nd6_output(ifp, m0, dst, rt); /* XXX: loop care? */
+ } else
+ senderr(EHOSTUNREACH);
+ }
+ if (rt->rt_flags & RTF_GATEWAY) {
+ if (rt->rt_gwroute == 0)
+ goto lookup;
+ if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
+ rtfree(rt); rt = rt0;
+ lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 0UL);
+ if ((rt = rt->rt_gwroute) == 0)
+ senderr(EHOSTUNREACH);
+ }
+ }
+ if (rt->rt_flags & RTF_REJECT)
+ senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
+ }
+
+ /*
+ * Address resolution or Neighbor Unreachability Detection
+ * for the next hop.
+ * At this point, the destination of the packet must be a unicast
+ * or an anycast address(i.e. not a multicast).
+ */
+
+ /* Look up the neighbor cache for the nexthop */
+ if (rt && (rt->rt_flags & RTF_LLINFO) != 0)
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ else {
+ if ((rt = nd6_lookup(&dst->sin6_addr, 1, ifp)) != NULL)
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ }
+ if (!ln || !rt) {
+ log(LOG_DEBUG, "nd6_output: can't allocate llinfo for %s "
+ "(ln=%p, rt=%p)\n",
+ ip6_sprintf(&dst->sin6_addr), ln, rt);
+ senderr(EIO); /* XXX: good error? */
+ }
+
+
+ /*
+ * The first time we send a packet to a neighbor whose entry is
+ * STALE, we have to change the state to DELAY and a sets a timer to
+ * expire in DELAY_FIRST_PROBE_TIME seconds to ensure do
+ * neighbor unreachability detection on expiration.
+ * (RFC 2461 7.3.3)
+ */
+ if (ln->ln_state == ND6_LLINFO_STALE) {
+ ln->ln_asked = 0;
+ ln->ln_state = ND6_LLINFO_DELAY;
+ ln->ln_expire = time_second + nd6_delay;
+ }
+
+ /*
+ * If the neighbor cache entry has a state other than INCOMPLETE
+ * (i.e. its link-layer address is already reloved), just
+ * send the packet.
+ */
+ if (ln->ln_state > ND6_LLINFO_INCOMPLETE)
+ goto sendpkt;
+
+ /*
+ * There is a neighbor cache entry, but no ethernet address
+ * response yet. Replace the held mbuf (if any) with this
+ * latest one.
+ *
+ * XXX Does the code conform to rate-limiting rule?
+ * (RFC 2461 7.2.2)
+ */
+ if (ln->ln_state == ND6_LLINFO_WAITDELETE ||
+ ln->ln_state == ND6_LLINFO_NOSTATE)
+ ln->ln_state = ND6_LLINFO_INCOMPLETE;
+ if (ln->ln_hold)
+ m_freem(ln->ln_hold);
+ ln->ln_hold = m;
+ if (ln->ln_expire) {
+ rt->rt_flags &= ~RTF_REJECT;
+ if (ln->ln_asked < nd6_mmaxtries &&
+ ln->ln_expire < time_second) {
+ ln->ln_asked++;
+ ln->ln_expire = time_second +
+ nd_ifinfo[ifp->if_index].retrans / 1000;
+ nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
+ }
+ }
+ return(0);
+
+ sendpkt:
+ return((*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt));
+
+ bad:
+ if (m)
+ m_freem(m);
+ return (error);
+}
+#undef senderr
+
+int
+nd6_storelladdr(ifp, rt, m, dst, desten)
+ struct ifnet *ifp;
+ struct rtentry *rt;
+ struct mbuf *m;
+ struct sockaddr *dst;
+ u_char *desten;
+{
+ struct sockaddr_dl *sdl;
+
+ if (m->m_flags & M_MCAST) {
+ switch (ifp->if_type) {
+ case IFT_ETHER:
+ case IFT_FDDI:
+ ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr,
+ desten);
+ return(1);
+ break;
+ case IFT_ARCNET:
+ *desten = 0;
+ return(1);
+ default:
+ return(0);
+ }
+ }
+
+ if (rt == NULL ||
+ rt->rt_gateway->sa_family != AF_LINK) {
+ printf("nd6_storelladdr: something odd happens\n");
+ return(0);
+ }
+ sdl = SDL(rt->rt_gateway);
+ if (sdl->sdl_alen != 0)
+ bcopy(LLADDR(sdl), desten, sdl->sdl_alen);
+
+ return(1);
+}
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
new file mode 100644
index 0000000..2bb4fe4
--- /dev/null
+++ b/sys/netinet6/nd6.h
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETINET6_ND6_H_
+#define _NETINET6_ND6_H_
+
+#include <sys/queue.h>
+
+struct llinfo_nd6 {
+ struct llinfo_nd6 *ln_next;
+ struct llinfo_nd6 *ln_prev;
+ struct rtentry *ln_rt;
+ struct mbuf *ln_hold; /* last packet until resolved/timeout */
+ long ln_asked; /* number of queries already sent for this addr */
+ u_long ln_expire; /* lifetime for NDP state transition */
+ short ln_state; /* reachability state */
+ short ln_router; /* 2^0: ND6 router bit */
+};
+
+#define ND6_LLINFO_NOSTATE -2
+#define ND6_LLINFO_WAITDELETE -1
+#define ND6_LLINFO_INCOMPLETE 0
+#define ND6_LLINFO_REACHABLE 1
+#define ND6_LLINFO_STALE 2
+#define ND6_LLINFO_DELAY 3
+#define ND6_LLINFO_PROBE 4
+
+struct nd_ifinfo {
+ u_int32_t linkmtu; /* LinkMTU */
+ u_int32_t maxmtu; /* Upper bound of LinkMTU */
+ u_int32_t basereachable; /* BaseReachableTime */
+ u_int32_t reachable; /* Reachable Time */
+ u_int32_t retrans; /* Retrans Timer */
+ int recalctm; /* BaseReacable re-calculation timer */
+ u_int8_t chlim; /* CurHopLimit */
+ u_int8_t receivedra;
+};
+
+struct in6_nbrinfo {
+ char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */
+ struct in6_addr addr; /* IPv6 address of the neighbor */
+ long asked; /* number of queries already sent for this addr */
+ int isrouter; /* if it acts as a router */
+ int state; /* reachability state */
+ int expire; /* lifetime for NDP state transition */
+};
+
+#define DRLSTSIZ 10
+#define PRLSTSIZ 10
+struct in6_drlist {
+ char ifname[IFNAMSIZ];
+ struct {
+ struct in6_addr rtaddr;
+ u_char flags;
+ u_short rtlifetime;
+ u_long expire;
+ u_short if_index;
+ } defrouter[DRLSTSIZ];
+};
+
+struct in6_prlist {
+ char ifname[IFNAMSIZ];
+ struct {
+ struct in6_addr prefix;
+ struct prf_ra raflags;
+ u_char prefixlen;
+ u_long vltime;
+ u_long pltime;
+ u_long expire;
+ u_short if_index;
+ u_short advrtrs; /* number of advertisement routers */
+ struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */
+ } prefix[PRLSTSIZ];
+};
+
+struct in6_ndireq {
+ char ifname[IFNAMSIZ];
+ struct nd_ifinfo ndi;
+};
+
+/* protocol constants */
+#define MAX_RTR_SOLICITATION_DELAY 1 /*1sec*/
+#define RTR_SOLICITATION_INTERVAL 4 /*4sec*/
+#define MAX_RTR_SOLICITATIONS 3
+
+#define ND6_INFINITE_LIFETIME 0xffffffff
+
+#ifdef _KERNEL
+/* node constants */
+#define MAX_REACHABLE_TIME 3600000 /* msec */
+#define REACHABLE_TIME 30000 /* msec */
+#define RETRANS_TIMER 1000 /* msec */
+#define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */
+#define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */
+#define ND_COMPUTE_RTIME(x) \
+ (((MIN_RANDOM_FACTOR * (x >> 10)) + (random() & \
+ ((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000)
+
+struct nd_defrouter {
+ LIST_ENTRY(nd_defrouter) dr_entry;
+#define dr_next dr_entry.le_next
+ struct in6_addr rtaddr;
+ u_char flags;
+ u_short rtlifetime;
+ u_long expire;
+ struct ifnet *ifp;
+};
+
+struct nd_prefix {
+ struct ifnet *ndpr_ifp;
+ LIST_ENTRY(nd_prefix) ndpr_entry;
+ struct sockaddr_in6 ndpr_prefix; /* prefix */
+ struct in6_addr ndpr_mask; /* netmask derived from the prefix */
+ struct in6_addr ndpr_addr; /* address that is derived from the prefix */
+ u_int32_t ndpr_vltime; /* advertised valid lifetime */
+ u_int32_t ndpr_pltime; /* advertised preferred lifetime */
+ time_t ndpr_expire; /* expiration time of the prefix */
+ time_t ndpr_preferred; /* preferred time of the prefix */
+ struct prf_ra ndpr_flags;
+ /* list of routers that advertise the prefix: */
+ LIST_HEAD(pr_rtrhead, nd_pfxrouter) ndpr_advrtrs;
+ u_char ndpr_plen;
+ struct ndpr_stateflags {
+ /* if this prefix can be regarded as on-link */
+ u_char onlink : 1;
+ } ndpr_stateflags;
+};
+
+#define ndpr_next ndpr_entry.le_next
+
+#define ndpr_raf ndpr_flags
+#define ndpr_raf_onlink ndpr_flags.onlink
+#define ndpr_raf_auto ndpr_flags.autonomous
+
+#define ndpr_statef_onlink ndpr_stateflags.onlink
+#define ndpr_statef_addmark ndpr_stateflags.addmark
+
+/*
+ * We keep expired prefix for certain amount of time, for validation purposes.
+ * 1800s = MaxRtrAdvInterval
+ */
+#define NDPR_KEEP_EXPIRED (1800 * 2)
+
+/*
+ * Message format for use in obtaining information about prefixes
+ * from inet6 sysctl function
+ */
+struct inet6_ndpr_msghdr {
+ u_short inpm_msglen; /* to skip over non-understood messages */
+ u_char inpm_version; /* future binary compatability */
+ u_char inpm_type; /* message type */
+ struct in6_addr inpm_prefix;
+ u_long prm_vltim;
+ u_long prm_pltime;
+ u_long prm_expire;
+ u_long prm_preferred;
+ struct in6_prflags prm_flags;
+ u_short prm_index; /* index for associated ifp */
+ u_char prm_plen; /* length of prefix in bits */
+};
+
+#define prm_raf_onlink prm_flags.prf_ra.onlink
+#define prm_raf_auto prm_flags.prf_ra.autonomous
+
+#define prm_statef_onlink prm_flags.prf_state.onlink
+
+#define prm_rrf_decrvalid prm_flags.prf_rr.decrvalid
+#define prm_rrf_decrprefd prm_flags.prf_rr.decrprefd
+
+#define ifpr2ndpr(ifpr) ((struct nd_prefix *)(ifpr))
+#define ndpr2ifpr(ndpr) ((struct ifprefix *)(ndpr))
+
+struct nd_pfxrouter {
+ LIST_ENTRY(nd_pfxrouter) pfr_entry;
+#define pfr_next pfr_entry.le_next
+ struct nd_defrouter *router;
+};
+
+LIST_HEAD(nd_drhead, nd_defrouter);
+LIST_HEAD(nd_prhead, nd_prefix);
+
+/* nd6.c */
+extern int nd6_prune;
+extern int nd6_delay;
+extern int nd6_umaxtries;
+extern int nd6_mmaxtries;
+extern int nd6_useloopback;
+extern int nd6_proxyall;
+extern struct llinfo_nd6 llinfo_nd6;
+extern struct nd_ifinfo *nd_ifinfo;
+extern struct nd_drhead nd_defrouter;
+extern struct nd_prhead nd_prefix;
+
+union nd_opts {
+ struct nd_opt_hdr *nd_opt_array[9];
+ struct {
+ struct nd_opt_hdr *zero;
+ struct nd_opt_hdr *src_lladdr;
+ struct nd_opt_hdr *tgt_lladdr;
+ struct nd_opt_prefix_info *pi_beg;/* multiple opts, start */
+ struct nd_opt_rd_hdr *rh;
+ struct nd_opt_mtu *mtu;
+ struct nd_opt_hdr *search; /* multiple opts */
+ struct nd_opt_hdr *last; /* multiple opts */
+ int done;
+ struct nd_opt_prefix_info *pi_end;/* multiple opts, end */
+ } nd_opt_each;
+};
+#define nd_opts_src_lladdr nd_opt_each.src_lladdr
+#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr
+#define nd_opts_pi nd_opt_each.pi_beg
+#define nd_opts_pi_end nd_opt_each.pi_end
+#define nd_opts_rh nd_opt_each.rh
+#define nd_opts_mtu nd_opt_each.mtu
+#define nd_opts_search nd_opt_each.search
+#define nd_opts_last nd_opt_each.last
+#define nd_opts_done nd_opt_each.done
+
+/* XXX: need nd6_var.h?? */
+/* nd6.c */
+void nd6_init __P((void));
+void nd6_ifattach __P((struct ifnet *));
+int nd6_is_addr_neighbor __P((struct in6_addr *, struct ifnet *));
+void nd6_option_init __P((void *, int, union nd_opts *));
+struct nd_opt_hdr *nd6_option __P((union nd_opts *));
+int nd6_options __P((union nd_opts *));
+struct rtentry *nd6_lookup __P((struct in6_addr *, int, struct ifnet *));
+void nd6_setmtu __P((struct ifnet *));
+void nd6_timer __P((void *));
+void nd6_free __P((struct rtentry *));
+void nd6_nud_hint __P((struct rtentry *, struct in6_addr *));
+int nd6_resolve __P((struct ifnet *, struct rtentry *,
+ struct mbuf *, struct sockaddr *, u_char *));
+void nd6_rtrequest __P((int, struct rtentry *, struct sockaddr *));
+void nd6_p2p_rtrequest __P((int, struct rtentry *, struct sockaddr *));
+int nd6_ioctl __P((u_long, caddr_t, struct ifnet *));
+struct rtentry *nd6_cache_lladdr __P((struct ifnet *, struct in6_addr *,
+ char *, int, int, int));
+/* for test */
+int nd6_output __P((struct ifnet *, struct mbuf *, struct sockaddr_in6 *,
+ struct rtentry *));
+int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *,
+ struct sockaddr *, u_char *));
+
+/* nd6_nbr.c */
+void nd6_na_input __P((struct mbuf *, int, int));
+void nd6_na_output __P((struct ifnet *, struct in6_addr *,
+ struct in6_addr *, u_long, int));
+void nd6_ns_input __P((struct mbuf *, int, int));
+void nd6_ns_output __P((struct ifnet *, struct in6_addr *,
+ struct in6_addr *, struct llinfo_nd6 *, int));
+caddr_t nd6_ifptomac __P((struct ifnet *));
+void nd6_dad_start __P((struct ifaddr *, int *));
+void nd6_dad_duplicated __P((struct ifaddr *));
+
+/* nd6_rtr.c */
+void nd6_rs_input __P((struct mbuf *, int, int));
+void nd6_ra_input __P((struct mbuf *, int, int));
+void prelist_del __P((struct nd_prefix *));
+void defrouter_addreq __P((struct nd_defrouter *));
+void defrouter_delreq __P((struct nd_defrouter *, int));
+void defrtrlist_del __P((struct nd_defrouter *));
+void prelist_remove __P((struct nd_prefix *));
+int prelist_update __P((struct nd_prefix *, struct nd_defrouter *,
+ struct mbuf *));
+struct nd_defrouter *defrouter_lookup __P((struct in6_addr *,
+ struct ifnet *));
+int in6_ifdel __P((struct ifnet *, struct in6_addr *));
+int in6_init_prefix_ltimes __P((struct nd_prefix *ndpr));
+void rt6_flush __P((struct in6_addr *, struct ifnet *));
+
+#endif /* _KERNEL */
+
+#endif /* _NETINET6_ND6_H_ */
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
new file mode 100644
index 0000000..2836d48
--- /dev/null
+++ b/sys/netinet6/nd6_nbr.c
@@ -0,0 +1,1123 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_inet.h"
+#include "opt_ipsec.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet6/icmp6.h>
+
+#include <net/net_osdep.h>
+
+#define SDL(s) ((struct sockaddr_dl *)s)
+
+struct dadq;
+static struct dadq *nd6_dad_find __P((struct ifaddr *));
+static void nd6_dad_timer __P((struct ifaddr *));
+static void nd6_dad_ns_input __P((struct ifaddr *));
+static void nd6_dad_na_input __P((struct ifaddr *));
+
+/* ignore NS in DAD - specwise incorrect, */
+int dad_ignore_ns = 0;
+
+/*
+ * Input an Neighbor Solicitation Message.
+ *
+ * Based on RFC 2461
+ * Based on RFC 2462 (duplicated address detection)
+ *
+ * XXX proxy advertisement
+ */
+void
+nd6_ns_input(m, off, icmp6len)
+ struct mbuf *m;
+ int off, icmp6len;
+{
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct nd_neighbor_solicit *nd_ns
+ = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off);
+ struct in6_addr saddr6 = ip6->ip6_src;
+ struct in6_addr daddr6 = ip6->ip6_dst;
+ struct in6_addr taddr6 = nd_ns->nd_ns_target;
+ struct in6_addr myaddr6;
+ char *lladdr = NULL;
+ struct ifaddr *ifa;
+ int lladdrlen = 0;
+ int anycast = 0, proxy = 0, tentative = 0;
+ int tlladdr;
+ union nd_opts ndopts;
+
+ if (ip6->ip6_hlim != 255) {
+ log(LOG_ERR,
+ "nd6_ns_input: invalid hlim %d\n", ip6->ip6_hlim);
+ return;
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
+ /* dst has to be solicited node multicast address. */
+ if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL
+ /*don't check ifindex portion*/
+ && daddr6.s6_addr32[1] == 0
+ && daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE
+ && daddr6.s6_addr8[12] == 0xff) {
+ ; /*good*/
+ } else {
+ log(LOG_INFO, "nd6_ns_input: bad DAD packet "
+ "(wrong ip6 dst)\n");
+ goto bad;
+ }
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
+ log(LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n");
+ goto bad;
+ }
+
+ if (IN6_IS_SCOPE_LINKLOCAL(&taddr6))
+ taddr6.s6_addr16[1] = htons(ifp->if_index);
+
+ icmp6len -= sizeof(*nd_ns);
+ nd6_option_init(nd_ns + 1, icmp6len, &ndopts);
+ if (nd6_options(&ndopts) < 0) {
+ log(LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n");
+ goto bad;
+ }
+
+ if (ndopts.nd_opts_src_lladdr) {
+ lladdr = (char *)(ndopts.nd_opts_src_lladdr +1);
+ lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) {
+ log(LOG_INFO, "nd6_ns_input: bad DAD packet "
+ "(link-layer address option)\n");
+ goto bad;
+ }
+
+ /*
+ * Attaching target link-layer address to the NA?
+ * (RFC 2461 7.2.4)
+ *
+ * NS IP dst is unicast/anycast MUST NOT add
+ * NS IP dst is solicited-node multicast MUST add
+ *
+ * In implementation, we add target link-layer address by default.
+ * We do not add one in MUST NOT cases.
+ */
+ if (!IN6_IS_ADDR_MULTICAST(&daddr6))
+ tlladdr = 0;
+ else
+ tlladdr = 1;
+
+ /*
+ * Target address (taddr6) must be either:
+ * (1) Valid unicast/anycast address for my receiving interface,
+ * (2) Unicast address for which I'm offering proxy service, or
+ * (3) "tentative" address on which DAD is being performed.
+ */
+ /* (1) and (3) check. */
+ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
+
+ /* (2) check. */
+ if (!ifa && nd6_proxyall) {
+ struct rtentry *rt;
+ struct sockaddr_in6 tsin6;
+
+ bzero(&tsin6, sizeof tsin6);
+ tsin6.sin6_len = sizeof(struct sockaddr_in6);
+ tsin6.sin6_family = AF_INET6;
+ tsin6.sin6_addr = taddr6;
+
+ rt = rtalloc1((struct sockaddr *)&tsin6, 0, 0);
+ if (rt && rt->rt_ifp != ifp) {
+ /*
+ * search link local addr for ifp, and use it for
+ * proxy NA.
+ */
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
+ if (ifa)
+ proxy = 1;
+ }
+ rtfree(rt);
+ }
+ if (!ifa) {
+ /*
+ * We've got a NS packet, and we don't have that adddress
+ * assigned for us. We MUST silently ignore it.
+ * See RFC2461 7.2.3.
+ */
+ return;
+ }
+ myaddr6 = *IFA_IN6(ifa);
+ anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST;
+ tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED)
+ return;
+
+ if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
+ log(LOG_INFO,
+ "nd6_ns_input: lladdrlen mismatch for %s "
+ "(if %d, NS packet %d)\n",
+ ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2);
+ }
+
+ if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) {
+ log(LOG_INFO,
+ "nd6_ns_input: duplicate IP6 address %s\n",
+ ip6_sprintf(&saddr6));
+ return;
+ }
+
+ /*
+ * We have neighbor solicitation packet, with target address equals to
+ * one of my tentative address.
+ *
+ * src addr how to process?
+ * --- ---
+ * multicast of course, invalid (rejected in ip6_input)
+ * unicast somebody is doing address resolution -> ignore
+ * unspec dup address detection
+ *
+ * The processing is defined in RFC 2462.
+ */
+ if (tentative) {
+ /*
+ * If source address is unspecified address, it is for
+ * duplicated address detection.
+ *
+ * If not, the packet is for addess resolution;
+ * silently ignore it.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
+ nd6_dad_ns_input(ifa);
+
+ return;
+ }
+
+ /*
+ * If the source address is unspecified address, entries must not
+ * be created or updated.
+ * It looks that sender is performing DAD. Output NA toward
+ * all-node multicast address, to tell the sender that I'm using
+ * the address.
+ * S bit ("solicited") must be zero.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
+ saddr6 = in6addr_linklocal_allnodes;
+ saddr6.s6_addr16[1] = htons(ifp->if_index);
+ nd6_na_output(ifp, &saddr6, &taddr6,
+ ((anycast || proxy || !tlladdr)
+ ? 0 : ND_NA_FLAG_OVERRIDE)
+ | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0),
+ tlladdr);
+ return;
+ }
+
+ nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0);
+
+ nd6_na_output(ifp, &saddr6, &taddr6,
+ ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE)
+ | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0)
+ | ND_NA_FLAG_SOLICITED,
+ tlladdr);
+ return;
+
+ bad:
+ log(LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6));
+ log(LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6));
+ log(LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6));
+ return;
+}
+
+/*
+ * Output an Neighbor Solicitation Message. Caller specifies:
+ * - ICMP6 header source IP6 address
+ * - ND6 header target IP6 address
+ * - ND6 header source datalink address
+ *
+ * Based on RFC 2461
+ * Based on RFC 2462 (duplicated address detection)
+ */
+void
+nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
+ struct ifnet *ifp;
+ struct in6_addr *daddr6, *taddr6;
+ struct llinfo_nd6 *ln; /* for source address determination */
+ int dad; /* duplicated address detection */
+{
+ struct mbuf *m;
+ struct ip6_hdr *ip6;
+ struct nd_neighbor_solicit *nd_ns;
+ struct in6_ifaddr *ia = NULL;
+ struct ip6_moptions im6o;
+ int icmp6len;
+ caddr_t mac;
+ struct ifnet *outif = NULL;
+
+ if (IN6_IS_ADDR_MULTICAST(taddr6))
+ return;
+
+ if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL)
+ return;
+
+ if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) {
+ m->m_flags |= M_MCAST;
+ im6o.im6o_multicast_ifp = ifp;
+ im6o.im6o_multicast_hlim = 255;
+ im6o.im6o_multicast_loop = 0;
+ }
+
+ icmp6len = sizeof(*nd_ns);
+ m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len;
+ MH_ALIGN(m, m->m_len + 16); /* 1+1+6 is enought. but just in case */
+
+ /* fill neighbor solicitation packet */
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc = IPV6_VERSION;
+ /* ip6->ip6_plen will be set later */
+ ip6->ip6_nxt = IPPROTO_ICMPV6;
+ ip6->ip6_hlim = 255;
+ if (daddr6)
+ ip6->ip6_dst = *daddr6;
+ else {
+ ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
+ ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index);
+ ip6->ip6_dst.s6_addr32[1] = 0;
+ ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE;
+ ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3];
+ ip6->ip6_dst.s6_addr8[12] = 0xff;
+ }
+ if (!dad) {
+ /* spec-wise correct, scope match */
+ /*
+ * RFC2461 7.2.2:
+ * "If the source address of the packet prompting the
+ * solicitation is the same as one of the addresses assigned
+ * to the outgoing interface, that address SHOULD be placed
+ * in the IP Source Address of the outgoing solicitation.
+ * Otherwise, any one of the addresses assigned to the
+ * interface should be used."
+ *
+ * We use the source address for the prompting packet
+ * (saddr6), if:
+ * - saddr6 is given from the caller (by giving "ln"), and
+ * - saddr6 belongs to the outgoing interface.
+ * Otherwise, we perform a scope-wise match.
+ */
+ struct ip6_hdr *hip6; /*hold ip6*/
+ struct in6_addr *saddr6;
+
+ if (ln && ln->ln_hold) {
+ hip6 = mtod(ln->ln_hold, struct ip6_hdr *);
+ /* XXX pullup? */
+ if (sizeof(*hip6) < ln->ln_hold->m_len)
+ saddr6 = &hip6->ip6_src;
+ else
+ saddr6 = NULL;
+ } else
+ saddr6 = NULL;
+ if (saddr6 && in6ifa_ifpwithaddr(ifp, saddr6))
+ bcopy(saddr6, &ip6->ip6_src, sizeof(*saddr6));
+ else {
+ ia = in6_ifawithifp(ifp, &ip6->ip6_dst);
+ if (ia == NULL) {
+ m_freem(m); /*XXX*/
+ return;
+ }
+ ip6->ip6_src = ia->ia_addr.sin6_addr;
+ }
+ } else {
+ /*
+ * Source address for DAD packet must always be IPv6
+ * unspecified address. (0::0)
+ */
+ bzero(&ip6->ip6_src, sizeof(ip6->ip6_src));
+ }
+ nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1);
+ nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
+ nd_ns->nd_ns_code = 0;
+ nd_ns->nd_ns_reserved = 0;
+ nd_ns->nd_ns_target = *taddr6;
+
+ if (IN6_IS_SCOPE_LINKLOCAL(&nd_ns->nd_ns_target))
+ nd_ns->nd_ns_target.s6_addr16[1] = 0;
+
+ /*
+ * Add source link-layer address option.
+ *
+ * spec implementation
+ * --- ---
+ * DAD packet MUST NOT do not add the option
+ * there's no link layer address:
+ * impossible do not add the option
+ * there's link layer address:
+ * Multicast NS MUST add one add the option
+ * Unicast NS SHOULD add one add the option
+ */
+ if (!dad && (mac = nd6_ifptomac(ifp))) {
+ int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
+ struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1);
+ /* 8 byte alignments... */
+ optlen = (optlen + 7) & ~7;
+
+ m->m_pkthdr.len += optlen;
+ m->m_len += optlen;
+ icmp6len += optlen;
+ bzero((caddr_t)nd_opt, optlen);
+ nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
+ nd_opt->nd_opt_len = optlen >> 3;
+ bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);
+ }
+
+ ip6->ip6_plen = htons((u_short)icmp6len);
+ nd_ns->nd_ns_cksum = 0;
+ nd_ns->nd_ns_cksum
+ = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len);
+
+#ifdef IPSEC
+ m->m_pkthdr.rcvif = NULL;
+#endif /*IPSEC*/
+ ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif);
+ if (outif) {
+ icmp6_ifstat_inc(outif, ifs6_out_msg);
+ icmp6_ifstat_inc(outif, ifs6_out_neighborsolicit);
+ }
+ icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]++;
+}
+
+/*
+ * Neighbor advertisement input handling.
+ *
+ * Based on RFC 2461
+ * Based on RFC 2462 (duplicated address detection)
+ */
+void
+nd6_na_input(m, off, icmp6len)
+ struct mbuf *m;
+ int off, icmp6len;
+{
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct nd_neighbor_advert *nd_na
+ = (struct nd_neighbor_advert *)((caddr_t)ip6 + off);
+ struct in6_addr daddr6 = ip6->ip6_dst;
+ struct in6_addr taddr6 = nd_na->nd_na_target;
+ int flags = nd_na->nd_na_flags_reserved;
+ int is_router = ((flags & ND_NA_FLAG_ROUTER) != 0);
+ int is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0);
+ int is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0);
+ char *lladdr = NULL;
+ int lladdrlen = 0;
+ struct ifaddr *ifa;
+ struct llinfo_nd6 *ln;
+ struct rtentry *rt;
+ struct sockaddr_dl *sdl;
+ union nd_opts ndopts;
+
+ if (ip6->ip6_hlim != 255) {
+ log(LOG_ERR,
+ "nd6_na_input: invalid hlim %d\n", ip6->ip6_hlim);
+ return;
+ }
+
+ if (IN6_IS_SCOPE_LINKLOCAL(&taddr6))
+ taddr6.s6_addr16[1] = htons(ifp->if_index);
+
+ if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
+ log(LOG_ERR,
+ "nd6_na_input: invalid target address %s\n",
+ ip6_sprintf(&taddr6));
+ return;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&daddr6))
+ if (is_solicited) {
+ log(LOG_ERR,
+ "nd6_na_input: a solicited adv is multicasted\n");
+ return;
+ }
+
+ icmp6len -= sizeof(*nd_na);
+ nd6_option_init(nd_na + 1, icmp6len, &ndopts);
+ if (nd6_options(&ndopts) < 0) {
+ log(LOG_INFO, "nd6_na_input: invalid ND option, ignored\n");
+ return;
+ }
+
+ if (ndopts.nd_opts_tgt_lladdr) {
+ lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);
+ lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
+ }
+
+ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
+
+ /*
+ * Target address matches one of my interface address.
+ *
+ * If my address is tentative, this means that there's somebody
+ * already using the same address as mine. This indicates DAD failure.
+ * This is defined in RFC 2462.
+ *
+ * Otherwise, process as defined in RFC 2461.
+ */
+ if (ifa
+ && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) {
+ nd6_dad_na_input(ifa);
+ return;
+ }
+
+ /* Just for safety, maybe unnecessery. */
+ if (ifa) {
+ log(LOG_ERR,
+ "nd6_na_input: duplicate IP6 address %s\n",
+ ip6_sprintf(&taddr6));
+ return;
+ }
+
+ if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
+ log(LOG_INFO,
+ "nd6_na_input: lladdrlen mismatch for %s "
+ "(if %d, NA packet %d)\n",
+ ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2);
+ }
+
+ /*
+ * If no neighbor cache entry is found, NA SHOULD silently be discarded.
+ */
+ rt = nd6_lookup(&taddr6, 0, ifp);
+ if ((rt == NULL) ||
+ ((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) ||
+ ((sdl = SDL(rt->rt_gateway)) == NULL))
+ return;
+
+ if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
+ /*
+ * If the link-layer has address, and no lladdr option came,
+ * discard the packet.
+ */
+ if (ifp->if_addrlen && !lladdr)
+ return;
+
+ /*
+ * Record link-layer address, and update the state.
+ */
+ sdl->sdl_alen = ifp->if_addrlen;
+ bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
+ if (is_solicited) {
+ ln->ln_state = ND6_LLINFO_REACHABLE;
+ if (ln->ln_expire)
+ ln->ln_expire = time_second +
+ nd_ifinfo[rt->rt_ifp->if_index].reachable;
+ } else
+ ln->ln_state = ND6_LLINFO_STALE;
+ ln->ln_router = is_router;
+ } else {
+ int llchange;
+
+ /*
+ * Check if the link-layer address has changed or not.
+ */
+ if (!lladdr)
+ llchange = 0;
+ else {
+ if (sdl->sdl_alen) {
+ if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen))
+ llchange = 1;
+ else
+ llchange = 0;
+ } else
+ llchange = 1;
+ }
+
+ /*
+ * This is VERY complex. Look at it with care.
+ *
+ * override solicit lladdr llchange action
+ * (L: record lladdr)
+ *
+ * 0 0 n -- (2c)
+ * 0 0 y n (2b) L
+ * 0 0 y y (1) REACHABLE->STALE
+ * 0 1 n -- (2c) *->REACHABLE
+ * 0 1 y n (2b) L *->REACHABLE
+ * 0 1 y y (1) REACHABLE->STALE
+ * 1 0 n -- (2a)
+ * 1 0 y n (2a) L
+ * 1 0 y y (2a) L *->STALE
+ * 1 1 n -- (2a) *->REACHABLE
+ * 1 1 y n (2a) L *->REACHABLE
+ * 1 1 y y (2a) L *->REACHABLE
+ */
+ if (!is_override && (lladdr && llchange)) { /* (1) */
+ /*
+ * If state is REACHABLE, make it STALE.
+ * no other updates should be done.
+ */
+ if (ln->ln_state == ND6_LLINFO_REACHABLE)
+ ln->ln_state = ND6_LLINFO_STALE;
+ return;
+ } else if (is_override /* (2a) */
+ || (!is_override && (lladdr && !llchange)) /* (2b) */
+ || !lladdr) { /* (2c) */
+ /*
+ * Update link-local address, if any.
+ */
+ if (lladdr) {
+ sdl->sdl_alen = ifp->if_addrlen;
+ bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
+ }
+
+ /*
+ * If solicited, make the state REACHABLE.
+ * If not solicited and the link-layer address was
+ * changed, make it STALE.
+ */
+ if (is_solicited) {
+ ln->ln_state = ND6_LLINFO_REACHABLE;
+ if (ln->ln_expire) {
+ ln->ln_expire = time_second +
+ nd_ifinfo[ifp->if_index].reachable;
+ }
+ } else {
+ if (lladdr && llchange)
+ ln->ln_state = ND6_LLINFO_STALE;
+ }
+ }
+
+ if (ln->ln_router && !is_router) {
+ /*
+ * The peer dropped the router flag.
+ * Remove the sender from the Default Router List and
+ * update the Destination Cache entries.
+ */
+ struct nd_defrouter *dr;
+ struct in6_addr *in6;
+ int s;
+
+ in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
+ s = splnet();
+ dr = defrouter_lookup(in6, rt->rt_ifp);
+ if (dr)
+ defrtrlist_del(dr);
+ else if (!ip6_forwarding && ip6_accept_rtadv) {
+ /*
+ * Even if the neighbor is not in the default
+ * router list, the neighbor may be used
+ * as a next hop for some destinations
+ * (e.g. redirect case). So we must
+ * call rt6_flush explicitly.
+ */
+ rt6_flush(&ip6->ip6_src, rt->rt_ifp);
+ }
+ splx(s);
+ }
+ ln->ln_router = is_router;
+ }
+ rt->rt_flags &= ~RTF_REJECT;
+ ln->ln_asked = 0;
+ if (ln->ln_hold) {
+ nd6_output(ifp, ln->ln_hold,
+ (struct sockaddr_in6 *)rt_key(rt), rt);
+ ln->ln_hold = 0;
+ }
+}
+
+/*
+ * Neighbor advertisement output handling.
+ *
+ * Based on RFC 2461
+ *
+ * XXX NA delay for anycast address is not implemented yet
+ * (RFC 2461 7.2.7)
+ * XXX proxy advertisement?
+ */
+void
+nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr)
+ struct ifnet *ifp;
+ struct in6_addr *daddr6, *taddr6;
+ u_long flags;
+ int tlladdr; /* 1 if include target link-layer address */
+{
+ struct mbuf *m;
+ struct ip6_hdr *ip6;
+ struct nd_neighbor_advert *nd_na;
+ struct in6_ifaddr *ia = NULL;
+ struct ip6_moptions im6o;
+ int icmp6len;
+ caddr_t mac;
+ struct ifnet *outif;
+
+ if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL)
+ return;
+
+ if (IN6_IS_ADDR_MULTICAST(daddr6)) {
+ m->m_flags |= M_MCAST;
+ im6o.im6o_multicast_ifp = ifp;
+ im6o.im6o_multicast_hlim = 255;
+ im6o.im6o_multicast_loop = 0;
+ }
+
+ icmp6len = sizeof(*nd_na);
+ m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len;
+ MH_ALIGN(m, m->m_len + 16); /* 1+1+6 is enough. but just in case */
+
+ /* fill neighbor advertisement packet */
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc = IPV6_VERSION;
+ ip6->ip6_nxt = IPPROTO_ICMPV6;
+ ip6->ip6_hlim = 255;
+ if (IN6_IS_ADDR_UNSPECIFIED(daddr6)) {
+ /* reply to DAD */
+ ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
+ ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index);
+ ip6->ip6_dst.s6_addr32[1] = 0;
+ ip6->ip6_dst.s6_addr32[2] = 0;
+ ip6->ip6_dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE;
+ flags &= ~ND_NA_FLAG_SOLICITED;
+ } else
+ ip6->ip6_dst = *daddr6;
+
+ /*
+ * Select a source whose scope is the same as that of the dest.
+ */
+ ia = in6_ifawithifp(ifp, &ip6->ip6_dst);
+ if (ia == NULL) {
+ m_freem(m);
+ return;
+ }
+ ip6->ip6_src = ia->ia_addr.sin6_addr;
+ nd_na = (struct nd_neighbor_advert *)(ip6 + 1);
+ nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
+ nd_na->nd_na_code = 0;
+ nd_na->nd_na_target = *taddr6;
+ if (IN6_IS_SCOPE_LINKLOCAL(&nd_na->nd_na_target))
+ nd_na->nd_na_target.s6_addr16[1] = 0;
+
+ /*
+ * "tlladdr" indicates NS's condition for adding tlladdr or not.
+ * see nd6_ns_input() for details.
+ * Basically, if NS packet is sent to unicast/anycast addr,
+ * target lladdr option SHOULD NOT be included.
+ */
+ if (tlladdr && (mac = nd6_ifptomac(ifp))) {
+ int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
+ struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1);
+
+ /* roundup to 8 bytes alignment! */
+ optlen = (optlen + 7) & ~7;
+
+ m->m_pkthdr.len += optlen;
+ m->m_len += optlen;
+ icmp6len += optlen;
+ bzero((caddr_t)nd_opt, optlen);
+ nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
+ nd_opt->nd_opt_len = optlen >> 3;
+ bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);
+ } else
+ flags &= ~ND_NA_FLAG_OVERRIDE;
+
+ ip6->ip6_plen = htons((u_short)icmp6len);
+ nd_na->nd_na_flags_reserved = flags;
+ nd_na->nd_na_cksum = 0;
+ nd_na->nd_na_cksum =
+ in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len);
+
+#ifdef IPSEC
+ m->m_pkthdr.rcvif = NULL;
+#endif /*IPSEC*/
+ ip6_output(m, NULL, NULL, 0, &im6o, &outif);
+ if (outif) {
+ icmp6_ifstat_inc(outif, ifs6_out_msg);
+ icmp6_ifstat_inc(outif, ifs6_out_neighboradvert);
+ }
+ icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]++;
+}
+
+caddr_t
+nd6_ifptomac(ifp)
+ struct ifnet *ifp;
+{
+ switch (ifp->if_type) {
+ case IFT_ARCNET:
+ case IFT_ETHER:
+ case IFT_FDDI:
+ return ((caddr_t)(ifp + 1));
+ break;
+ default:
+ return NULL;
+ }
+}
+
+TAILQ_HEAD(dadq_head, dadq);
+struct dadq {
+ TAILQ_ENTRY(dadq) dad_list;
+ struct ifaddr *dad_ifa;
+ int dad_count; /* max NS to send */
+ int dad_ns_ocount; /* NS sent so far */
+ int dad_ns_icount;
+ int dad_na_icount;
+ struct callout_handle dad_timer;
+};
+
+static struct dadq_head dadq;
+
+static struct dadq *
+nd6_dad_find(ifa)
+ struct ifaddr *ifa;
+{
+ struct dadq *dp;
+
+ for (dp = dadq.tqh_first; dp; dp = dp->dad_list.tqe_next) {
+ if (dp->dad_ifa == ifa)
+ return dp;
+ }
+ return NULL;
+}
+
+/*
+ * Start Duplicated Address Detection (DAD) for specified interface address.
+ */
+void
+nd6_dad_start(ifa, tick)
+ struct ifaddr *ifa;
+ int *tick; /* minimum delay ticks for IFF_UP event */
+{
+ struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
+ struct dadq *dp;
+ static int dad_init = 0;
+
+ if (!dad_init) {
+ TAILQ_INIT(&dadq);
+ dad_init++;
+ }
+
+ /*
+ * If we don't need DAD, don't do it.
+ * There are several cases:
+ * - DAD is disabled (ip6_dad_count == 0)
+ * - the interface address is anycast
+ */
+ if (!(ia->ia6_flags & IN6_IFF_TENTATIVE)) {
+ printf("nd6_dad_start: called with non-tentative address "
+ "%s(%s)\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
+ ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
+ return;
+ }
+ if (ia->ia6_flags & IN6_IFF_ANYCAST) {
+ ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+ return;
+ }
+ if (!ip6_dad_count) {
+ ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+ return;
+ }
+ if (!ifa->ifa_ifp)
+ panic("nd6_dad_start: ifa->ifa_ifp == NULL");
+ if (!(ifa->ifa_ifp->if_flags & IFF_UP))
+ return;
+ if (nd6_dad_find(ifa) != NULL) {
+ /* DAD already in progress */
+ return;
+ }
+
+ dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT);
+ if (dp == NULL) {
+ printf("nd6_dad_start: memory allocation failed for "
+ "%s(%s)\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
+ ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
+ return;
+ }
+ bzero(dp, sizeof(*dp));
+ TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list);
+
+ /* XXXJRT This is probably a purely debugging message. */
+ printf("%s: starting DAD for %s\n", if_name(ifa->ifa_ifp),
+ ip6_sprintf(&ia->ia_addr.sin6_addr));
+
+ /*
+ * Send NS packet for DAD, ip6_dad_count times.
+ * Note that we must delay the first transmission, if this is the
+ * first packet to be sent from the interface after interface
+ * (re)initialization.
+ */
+ dp->dad_ifa = ifa;
+ ifa->ifa_refcnt++; /*just for safety*/
+ dp->dad_count = ip6_dad_count;
+ dp->dad_ns_icount = dp->dad_na_icount = 0;
+ dp->dad_ns_ocount = 0;
+ if (!tick) {
+ dp->dad_ns_ocount++;
+ nd6_ns_output(ifa->ifa_ifp, NULL, &ia->ia_addr.sin6_addr,
+ NULL, 1);
+ dp->dad_timer =
+ timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa,
+ nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000);
+ } else {
+ int ntick;
+
+ if (*tick == 0)
+ ntick = random() % (MAX_RTR_SOLICITATION_DELAY * hz);
+ else
+ ntick = *tick + random() % (hz / 2);
+ *tick = ntick;
+ dp->dad_timer =
+ timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa,
+ ntick);
+ }
+}
+
+static void
+nd6_dad_timer(ifa)
+ struct ifaddr *ifa;
+{
+ int s;
+ struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
+ struct dadq *dp;
+
+ s = splnet(); /*XXX*/
+
+ /* Sanity check */
+ if (ia == NULL) {
+ printf("nd6_dad_timer: called with null parameter\n");
+ goto done;
+ }
+ dp = nd6_dad_find(ifa);
+ if (dp == NULL) {
+ printf("nd6_dad_timer: DAD structure not found\n");
+ goto done;
+ }
+ if (ia->ia6_flags & IN6_IFF_DUPLICATED) {
+ printf("nd6_dad_timer: called with duplicated address "
+ "%s(%s)\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
+ ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
+ goto done;
+ }
+ if ((ia->ia6_flags & IN6_IFF_TENTATIVE) == 0) {
+ printf("nd6_dad_timer: called with non-tentative address "
+ "%s(%s)\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
+ ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
+ goto done;
+ }
+
+ /* Need more checks? */
+ if (dp->dad_ns_ocount < dp->dad_count) {
+ /*
+ * We have more NS to go. Send NS packet for DAD.
+ */
+ dp->dad_ns_ocount++;
+ nd6_ns_output(ifa->ifa_ifp, NULL, &ia->ia_addr.sin6_addr,
+ NULL, 1);
+ dp->dad_timer =
+ timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa,
+ nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000);
+ } else {
+ /*
+ * We have transmitted sufficient number of DAD packets.
+ * See what we've got.
+ */
+ int duplicate;
+
+ duplicate = 0;
+
+ if (dp->dad_na_icount) {
+ /*
+ * the check is in nd6_dad_na_input(),
+ * but just in case
+ */
+ duplicate++;
+ }
+
+ if (dp->dad_ns_icount) {
+ /* We've seen NS, means DAD has failed. */
+ duplicate++;
+ }
+
+ if (duplicate) {
+ /* (*dp) will be freed in nd6_dad_duplicated() */
+ dp = NULL;
+ nd6_dad_duplicated(ifa);
+ } else {
+ /*
+ * We are done with DAD. No NA came, no NS came.
+ * duplicated address found.
+ */
+ ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+
+ /* XXXJRT This is probably a purely debugging message */
+ printf("%s: DAD complete for %s - no duplicates "
+ "found\n", if_name(ifa->ifa_ifp),
+ ip6_sprintf(&ia->ia_addr.sin6_addr));
+
+ TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
+ free(dp, M_IP6NDP);
+ dp = NULL;
+ ifa->ifa_refcnt--;
+ }
+ }
+
+done:
+ splx(s);
+}
+
+void
+nd6_dad_duplicated(ifa)
+ struct ifaddr *ifa;
+{
+ struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
+ struct dadq *dp;
+
+ dp = nd6_dad_find(ifa);
+ if (dp == NULL) {
+ printf("nd6_dad_duplicated: DAD structure not found\n");
+ return;
+ }
+
+ log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: %d NS, "
+ "%d NA\n", if_name(ifa->ifa_ifp),
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
+ dp->dad_ns_icount, dp->dad_na_icount);
+
+ ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+ ia->ia6_flags |= IN6_IFF_DUPLICATED;
+
+ /* We are done with DAD, with duplicated address found. (failure) */
+ untimeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa
+ , dp->dad_timer);
+
+ printf("%s: DAD complete for %s - duplicate found\n",
+ if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr));
+ printf("%s: manual intervention required\n", if_name(ifa->ifa_ifp));
+
+ TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
+ free(dp, M_IP6NDP);
+ dp = NULL;
+ ifa->ifa_refcnt--;
+}
+
+void
+nd6_dad_ns_input(ifa)
+ struct ifaddr *ifa;
+{
+ struct in6_ifaddr *ia;
+ struct ifnet *ifp;
+ struct in6_addr *taddr6;
+ struct dadq *dp;
+ int duplicate;
+
+ if (!ifa)
+ panic("ifa == NULL in nd6_dad_ns_input");
+
+ ia = (struct in6_ifaddr *)ifa;
+ ifp = ifa->ifa_ifp;
+ taddr6 = &ia->ia_addr.sin6_addr;
+ duplicate = 0;
+ dp = nd6_dad_find(ifa);
+
+ /*
+ * If it is from myself, ignore this.
+ */
+ if (ifp && (ifp->if_flags & IFF_LOOPBACK))
+ return;
+
+ /* Quickhack - completely ignore DAD NS packets */
+ if (dad_ignore_ns) {
+ log(LOG_INFO, "nd6_dad_ns_input: ignoring DAD NS packet for "
+ "address %s(%s)\n", ip6_sprintf(taddr6),
+ if_name(ifa->ifa_ifp));
+ return;
+ }
+
+ /*
+ * if I'm yet to start DAD, someone else started using this address
+ * first. I have a duplicate and you win.
+ */
+ if (!dp || dp->dad_ns_ocount == 0)
+ duplicate++;
+
+ /* XXX more checks for loopback situation - see nd6_dad_timer too */
+
+ if (duplicate) {
+ dp = NULL; /* will be freed in nd6_dad_duplicated() */
+ nd6_dad_duplicated(ifa);
+ } else {
+ /*
+ * not sure if I got a duplicate.
+ * increment ns count and see what happens.
+ */
+ if (dp)
+ dp->dad_ns_icount++;
+ }
+}
+
+void
+nd6_dad_na_input(ifa)
+ struct ifaddr *ifa;
+{
+ struct dadq *dp;
+
+ if (!ifa)
+ panic("ifa == NULL in nd6_dad_na_input");
+
+ dp = nd6_dad_find(ifa);
+ if (dp)
+ dp->dad_na_icount++;
+
+ /* remove the address. */
+ nd6_dad_duplicated(ifa);
+}
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
new file mode 100644
index 0000000..d177a66
--- /dev/null
+++ b/sys/netinet6/nd6_rtr.c
@@ -0,0 +1,1243 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <net/radix.h>
+
+#include <netinet/in.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet6/icmp6.h>
+
+#include <net/net_osdep.h>
+
+#define SDL(s) ((struct sockaddr_dl *)s)
+
+static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *));
+static int prelist_add __P((struct nd_prefix *, struct nd_defrouter *));
+static struct nd_prefix *prefix_lookup __P((struct nd_prefix *));
+static struct in6_ifaddr *in6_ifadd __P((struct ifnet *, struct in6_addr *,
+ struct in6_addr *, int));
+static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *,
+ struct nd_defrouter *));
+static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *));
+static void pfxrtr_del __P((struct nd_pfxrouter *));
+static void pfxlist_onlink_check __P((void));
+static void nd6_detach_prefix __P((struct nd_prefix *));
+static void nd6_attach_prefix __P((struct nd_prefix *));
+
+static void in6_init_address_ltimes __P((struct nd_prefix *ndpr,
+ struct in6_addrlifetime *lt6));
+
+static int rt6_deleteroute __P((struct radix_node *, void *));
+
+extern int nd6_recalc_reachtm_interval;
+
+/*
+ * Receive Router Solicitation Message - just for routers.
+ * Router solicitation/advertisement is mostly managed by userland program
+ * (rtadvd) so here we have no function like nd6_ra_output().
+ *
+ * Based on RFC 2461
+ */
+void
+nd6_rs_input(m, off, icmp6len)
+ struct mbuf *m;
+ int off, icmp6len;
+{
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct nd_router_solicit *nd_rs
+ = (struct nd_router_solicit *)((caddr_t)ip6 + off);
+ struct in6_addr saddr6 = ip6->ip6_src;
+ char *lladdr = NULL;
+ int lladdrlen = 0;
+ union nd_opts ndopts;
+
+ /* If I'm not a router, ignore it. */
+ if (ip6_accept_rtadv != 0 || ip6_forwarding != 1)
+ return;
+
+ /* Sanity checks */
+ if (ip6->ip6_hlim != 255) {
+ log(LOG_ERR,
+ "nd6_rs_input: invalid hlim %d\n", ip6->ip6_hlim);
+ return;
+ }
+
+ /*
+ * Don't update the neighbor cache, if src = ::.
+ * This indicates that the src has no IP address assigned yet.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
+ return;
+
+ icmp6len -= sizeof(*nd_rs);
+ nd6_option_init(nd_rs + 1, icmp6len, &ndopts);
+ if (nd6_options(&ndopts) < 0) {
+ log(LOG_INFO, "nd6_rs_input: invalid ND option, ignored\n");
+ return;
+ }
+
+ if (ndopts.nd_opts_src_lladdr) {
+ lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
+ lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
+ }
+
+ if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
+ log(LOG_INFO,
+ "nd6_rs_input: lladdrlen mismatch for %s "
+ "(if %d, RS packet %d)\n",
+ ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2);
+ }
+
+ nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0);
+}
+
+/*
+ * Receive Router Advertisement Message.
+ *
+ * Based on RFC 2461
+ * TODO: on-link bit on prefix information
+ * TODO: ND_RA_FLAG_{OTHER,MANAGED} processing
+ */
+void
+nd6_ra_input(m, off, icmp6len)
+ struct mbuf *m;
+ int off, icmp6len;
+{
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index];
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct nd_router_advert *nd_ra =
+ (struct nd_router_advert *)((caddr_t)ip6 + off);
+ struct in6_addr saddr6 = ip6->ip6_src;
+ union nd_opts ndopts;
+ struct nd_defrouter *dr;
+
+ if (ip6_accept_rtadv == 0)
+ return;
+
+ if (ip6->ip6_hlim != 255) {
+ log(LOG_ERR,
+ "nd6_ra_input: invalid hlim %d\n", ip6->ip6_hlim);
+ return;
+ }
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) {
+ log(LOG_ERR,
+ "nd6_ra_input: src %s is not link-local\n",
+ ip6_sprintf(&saddr6));
+ return;
+ }
+
+ icmp6len -= sizeof(*nd_ra);
+ nd6_option_init(nd_ra + 1, icmp6len, &ndopts);
+ if (nd6_options(&ndopts) < 0) {
+ log(LOG_INFO, "nd6_ra_input: invalid ND option, ignored\n");
+ return;
+ }
+
+ {
+ struct nd_defrouter dr0;
+ u_int32_t advreachable = nd_ra->nd_ra_reachable;
+
+ dr0.rtaddr = saddr6;
+ dr0.flags = nd_ra->nd_ra_flags_reserved;
+ dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime);
+ dr0.expire = time_second + dr0.rtlifetime;
+ dr0.ifp = ifp;
+ /* unspecified or not? (RFC 2461 6.3.4) */
+ if (advreachable) {
+ NTOHL(advreachable);
+ if (advreachable <= MAX_REACHABLE_TIME &&
+ ndi->basereachable != advreachable) {
+ ndi->basereachable = advreachable;
+ ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable);
+ ndi->recalctm = nd6_recalc_reachtm_interval; /* reset */
+ }
+ }
+ if (nd_ra->nd_ra_retransmit)
+ ndi->retrans = ntohl(nd_ra->nd_ra_retransmit);
+ if (nd_ra->nd_ra_curhoplimit)
+ ndi->chlim = nd_ra->nd_ra_curhoplimit;
+ dr = defrtrlist_update(&dr0);
+ }
+
+ /*
+ * prefix
+ */
+ if (ndopts.nd_opts_pi) {
+ struct nd_opt_hdr *pt;
+ struct nd_opt_prefix_info *pi;
+ struct nd_prefix pr;
+
+ for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi;
+ pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end;
+ pt = (struct nd_opt_hdr *)((caddr_t)pt +
+ (pt->nd_opt_len << 3))) {
+ if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION)
+ continue;
+ pi = (struct nd_opt_prefix_info *)pt;
+
+ if (pi->nd_opt_pi_len != 4) {
+ log(LOG_INFO, "nd6_ra_input: invalid option "
+ "len %d for prefix information option, "
+ "ignored\n", pi->nd_opt_pi_len);
+ continue;
+ }
+
+ if (128 < pi->nd_opt_pi_prefix_len) {
+ log(LOG_INFO, "nd6_ra_input: invalid prefix "
+ "len %d for prefix information option, "
+ "ignored\n", pi->nd_opt_pi_prefix_len);
+ continue;
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix)
+ || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) {
+ log(LOG_INFO, "nd6_ra_input: invalid prefix "
+ "%s, ignored\n",
+ ip6_sprintf(&pi->nd_opt_pi_prefix));
+ continue;
+ }
+
+ /* aggregatable unicast address, rfc2374 */
+ if ((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) == 0x20
+ && pi->nd_opt_pi_prefix_len != 64) {
+ log(LOG_INFO, "nd6_ra_input: invalid prefixlen "
+ "%d for rfc2374 prefix %s, ignored\n",
+ pi->nd_opt_pi_prefix_len,
+ ip6_sprintf(&pi->nd_opt_pi_prefix));
+ continue;
+ }
+
+ bzero(&pr, sizeof(pr));
+ pr.ndpr_prefix.sin6_family = AF_INET6;
+ pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix);
+ pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix;
+ pr.ndpr_ifp = (struct ifnet *)m->m_pkthdr.rcvif;
+
+ pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_ONLINK) ? 1 : 0;
+ pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_AUTO) ? 1 : 0;
+ pr.ndpr_plen = pi->nd_opt_pi_prefix_len;
+ pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time);
+ pr.ndpr_pltime =
+ ntohl(pi->nd_opt_pi_preferred_time);
+
+ if (in6_init_prefix_ltimes(&pr))
+ continue; /* prefix lifetime init failed */
+
+ (void)prelist_update(&pr, dr, m);
+ }
+ }
+
+ /*
+ * MTU
+ */
+ if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) {
+ u_int32_t mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
+
+ /* lower bound */
+ if (mtu < IPV6_MMTU) {
+ log(LOG_INFO, "nd6_ra_input: bogus mtu option "
+ "mtu=%d sent from %s, ignoring\n",
+ mtu, ip6_sprintf(&ip6->ip6_src));
+ goto skip;
+ }
+
+ /* upper bound */
+ if (ndi->maxmtu) {
+ if (mtu <= ndi->maxmtu) {
+ int change = (ndi->linkmtu != mtu);
+
+ ndi->linkmtu = mtu;
+ if (change) /* in6_maxmtu may change */
+ in6_setmaxmtu();
+ } else {
+ log(LOG_INFO, "nd6_ra_input: bogus mtu "
+ "mtu=%d sent from %s; "
+ "exceeds maxmtu %d, ignoring\n",
+ mtu, ip6_sprintf(&ip6->ip6_src),
+ ndi->maxmtu);
+ }
+ } else {
+ log(LOG_INFO, "nd6_ra_input: mtu option "
+ "mtu=%d sent from %s; maxmtu unknown, "
+ "ignoring\n",
+ mtu, ip6_sprintf(&ip6->ip6_src));
+ }
+ }
+
+ skip:
+
+ /*
+ * Src linkaddress
+ */
+ {
+ char *lladdr = NULL;
+ int lladdrlen = 0;
+
+ if (ndopts.nd_opts_src_lladdr) {
+ lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
+ lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
+ }
+
+ if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
+ log(LOG_INFO,
+ "nd6_ra_input: lladdrlen mismatch for %s "
+ "(if %d, RA packet %d)\n",
+ ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2);
+ }
+
+ nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0);
+ }
+}
+
+/*
+ * default router list proccessing sub routines
+ */
+void
+defrouter_addreq(new)
+ struct nd_defrouter *new;
+{
+ struct sockaddr_in6 def, mask, gate;
+ int s;
+
+ Bzero(&def, sizeof(def));
+ Bzero(&mask, sizeof(mask));
+ Bzero(&gate, sizeof(gate));
+
+ def.sin6_len = mask.sin6_len = gate.sin6_len
+ = sizeof(struct sockaddr_in6);
+ def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
+ gate.sin6_addr = new->rtaddr;
+
+ s = splnet();
+ (void)rtrequest(RTM_ADD, (struct sockaddr *)&def,
+ (struct sockaddr *)&gate, (struct sockaddr *)&mask,
+ RTF_GATEWAY, NULL);
+ splx(s);
+ return;
+}
+
+struct nd_defrouter *
+defrouter_lookup(addr, ifp)
+ struct in6_addr *addr;
+ struct ifnet *ifp;
+{
+ struct nd_defrouter *dr;
+
+ for(dr = nd_defrouter.lh_first; dr; dr = dr->dr_next)
+ if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr))
+ return(dr);
+
+ return(NULL); /* search failed */
+}
+
+void
+defrouter_delreq(dr, dofree)
+ struct nd_defrouter *dr;
+ int dofree;
+{
+ struct sockaddr_in6 def, mask, gate;
+
+ Bzero(&def, sizeof(def));
+ Bzero(&mask, sizeof(mask));
+ Bzero(&gate, sizeof(gate));
+
+ def.sin6_len = mask.sin6_len = gate.sin6_len
+ = sizeof(struct sockaddr_in6);
+ def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
+ gate.sin6_addr = dr->rtaddr;
+
+ rtrequest(RTM_DELETE, (struct sockaddr *)&def,
+ (struct sockaddr *)&gate,
+ (struct sockaddr *)&mask,
+ RTF_GATEWAY, (struct rtentry **)0);
+
+ if (dofree)
+ free(dr, M_IP6NDP);
+
+ if (nd_defrouter.lh_first)
+ defrouter_addreq(nd_defrouter.lh_first);
+
+ /*
+ * xxx update the Destination Cache entries for all
+ * destinations using that neighbor as a router (7.2.5)
+ */
+}
+
+void
+defrtrlist_del(dr)
+ struct nd_defrouter *dr;
+{
+ struct nd_defrouter *deldr = NULL;
+ struct nd_prefix *pr;
+
+ /*
+ * Flush all the routing table entries that use the router
+ * as a next hop.
+ */
+ if (!ip6_forwarding && ip6_accept_rtadv) {
+ /* above is a good condition? */
+ rt6_flush(&dr->rtaddr, dr->ifp);
+ }
+
+ if (dr == nd_defrouter.lh_first)
+ deldr = dr; /* The router is primary. */
+
+ LIST_REMOVE(dr, dr_entry);
+
+ /*
+ * Also delete all the pointers to the router in each prefix lists.
+ */
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ struct nd_pfxrouter *pfxrtr;
+ if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL)
+ pfxrtr_del(pfxrtr);
+ }
+ pfxlist_onlink_check();
+
+ /*
+ * If the router is the primary one, delete the default route
+ * entry in the routing table.
+ */
+ if (deldr)
+ defrouter_delreq(deldr, 0);
+ free(dr, M_IP6NDP);
+}
+
+static struct nd_defrouter *
+defrtrlist_update(new)
+ struct nd_defrouter *new;
+{
+ struct nd_defrouter *dr, *n;
+ int s = splnet();
+
+ if ((dr = defrouter_lookup(&new->rtaddr, new->ifp)) != NULL) {
+ /* entry exists */
+ if (new->rtlifetime == 0) {
+ defrtrlist_del(dr);
+ dr = NULL;
+ } else {
+ /* override */
+ dr->flags = new->flags; /* xxx flag check */
+ dr->rtlifetime = new->rtlifetime;
+ dr->expire = new->expire;
+ }
+ splx(s);
+ return(dr);
+ }
+
+ /* entry does not exist */
+ if (new->rtlifetime == 0) {
+ splx(s);
+ return(NULL);
+ }
+
+ n = (struct nd_defrouter *)malloc(sizeof(*n), M_IP6NDP, M_NOWAIT);
+ if (n == NULL) {
+ splx(s);
+ return(NULL);
+ }
+ bzero(n, sizeof(*n));
+ *n = *new;
+ if (nd_defrouter.lh_first == NULL) {
+ LIST_INSERT_HEAD(&nd_defrouter, n, dr_entry);
+ defrouter_addreq(n);
+ } else {
+ LIST_INSERT_AFTER(nd_defrouter.lh_first, n, dr_entry);
+ defrouter_addreq(n);
+ }
+ splx(s);
+
+ return(n);
+}
+
+static struct nd_pfxrouter *
+pfxrtr_lookup(pr, dr)
+ struct nd_prefix *pr;
+ struct nd_defrouter *dr;
+{
+ struct nd_pfxrouter *search;
+
+ for (search = pr->ndpr_advrtrs.lh_first; search; search = search->pfr_next) {
+ if (search->router == dr)
+ break;
+ }
+
+ return(search);
+}
+
+static void
+pfxrtr_add(pr, dr)
+ struct nd_prefix *pr;
+ struct nd_defrouter *dr;
+{
+ struct nd_pfxrouter *new;
+
+ new = (struct nd_pfxrouter *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT);
+ if (new == NULL)
+ return;
+ bzero(new, sizeof(*new));
+ new->router = dr;
+
+ LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry);
+
+ pfxlist_onlink_check();
+}
+
+static void
+pfxrtr_del(pfr)
+ struct nd_pfxrouter *pfr;
+{
+ LIST_REMOVE(pfr, pfr_entry);
+ free(pfr, M_IP6NDP);
+}
+
+static struct nd_prefix *
+prefix_lookup(pr)
+ struct nd_prefix *pr;
+{
+ struct nd_prefix *search;
+
+ for (search = nd_prefix.lh_first; search; search = search->ndpr_next) {
+ if (pr->ndpr_ifp == search->ndpr_ifp &&
+ pr->ndpr_plen == search->ndpr_plen &&
+ in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
+ &search->ndpr_prefix.sin6_addr,
+ pr->ndpr_plen)
+ ) {
+ break;
+ }
+ }
+
+ return(search);
+}
+
+static int
+prelist_add(pr, dr)
+ struct nd_prefix *pr;
+ struct nd_defrouter *dr;
+{
+ struct nd_prefix *new;
+ int i, s;
+
+ new = (struct nd_prefix *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT);
+ if (new == NULL)
+ return ENOMEM;
+ bzero(new, sizeof(*new));
+ *new = *pr;
+
+ /* initilization */
+ new->ndpr_statef_onlink = pr->ndpr_statef_onlink;
+ LIST_INIT(&new->ndpr_advrtrs);
+ in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen);
+ /* make prefix in the canonical form */
+ for (i = 0; i < 4; i++)
+ new->ndpr_prefix.sin6_addr.s6_addr32[i] &=
+ new->ndpr_mask.s6_addr32[i];
+
+ /* xxx ND_OPT_PI_FLAG_ONLINK processing */
+
+ s = splnet();
+ /* link ndpr_entry to nd_prefix list */
+ LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry);
+ splx(s);
+
+ if (dr)
+ pfxrtr_add(new, dr);
+
+ return 0;
+}
+
+void
+prelist_remove(pr)
+ struct nd_prefix *pr;
+{
+ struct nd_pfxrouter *pfr, *next;
+ int s;
+
+ s = splnet();
+ /* unlink ndpr_entry from nd_prefix list */
+ LIST_REMOVE(pr, ndpr_entry);
+ splx(s);
+
+ /* free list of routers that adversed the prefix */
+ for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) {
+ next = pfr->pfr_next;
+
+ free(pfr, M_IP6NDP);
+ }
+ free(pr, M_IP6NDP);
+
+ pfxlist_onlink_check();
+}
+
+/*
+ * NOTE: We set address lifetime to keep
+ * address lifetime <= prefix lifetime
+ * invariant. This is to simplify on-link determination code.
+ * If onlink determination is udated, this routine may have to be updated too.
+ */
+int
+prelist_update(new, dr, m)
+ struct nd_prefix *new;
+ struct nd_defrouter *dr; /* may be NULL */
+ struct mbuf *m;
+{
+ struct in6_ifaddr *ia6 = NULL;
+ struct nd_prefix *pr;
+ int s = splnet();
+ int error = 0;
+ int auth;
+ struct in6_addrlifetime *lt6;
+
+ auth = 0;
+ if (m) {
+ /*
+ * Authenticity for NA consists authentication for
+ * both IP header and IP datagrams, doesn't it ?
+ */
+#if defined(M_AUTHIPHDR) && defined(M_AUTHIPDGM)
+ auth = (m->m_flags & M_AUTHIPHDR
+ && m->m_flags & M_AUTHIPDGM) ? 1 : 0;
+#endif
+ }
+
+ if ((pr = prefix_lookup(new)) != NULL) {
+ if (pr->ndpr_ifp != new->ndpr_ifp) {
+ error = EADDRNOTAVAIL;
+ goto end;
+ }
+ /* update prefix information */
+ pr->ndpr_flags = new->ndpr_flags;
+ pr->ndpr_vltime = new->ndpr_vltime;
+ pr->ndpr_pltime = new->ndpr_pltime;
+ pr->ndpr_preferred = new->ndpr_preferred;
+ pr->ndpr_expire = new->ndpr_expire;
+
+ /*
+ * RFC 2462 5.5.3 (d) or (e)
+ * We got a prefix which we have seen in the past.
+ */
+ if (!new->ndpr_raf_auto)
+ goto noautoconf1;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ ia6 = NULL;
+ else
+ ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
+
+ if (ia6 == NULL) {
+ /*
+ * Special case:
+ * (1) We have seen the prefix advertised before, but
+ * we have never performed autoconfig for this prefix.
+ * This is because Autonomous bit was 0 previously, or
+ * autoconfig failed due to some other reasons.
+ * (2) We have seen the prefix advertised before and
+ * we have performed autoconfig in the past, but
+ * we seem to have no interface address right now.
+ * This is because the interface address have expired.
+ *
+ * This prefix is fresh, with respect to autoconfig
+ * process.
+ *
+ * Add an address based on RFC 2462 5.5.3 (d).
+ */
+ ia6 = in6_ifadd(pr->ndpr_ifp,
+ &pr->ndpr_prefix.sin6_addr, &pr->ndpr_addr,
+ new->ndpr_plen);
+ if (!ia6) {
+ error = EADDRNOTAVAIL;
+ log(LOG_ERR, "prelist_update: failed to add a "
+ "new address\n");
+ goto noautoconf1;
+ }
+
+ lt6 = &ia6->ia6_lifetime;
+
+ /* address lifetime <= prefix lifetime */
+ lt6->ia6t_vltime = new->ndpr_vltime;
+ lt6->ia6t_pltime = new->ndpr_pltime;
+ in6_init_address_ltimes(new, lt6);
+ } else {
+#define TWOHOUR (120*60)
+ /*
+ * We have seen the prefix before, and we have added
+ * interface address in the past. We still have
+ * the interface address assigned.
+ *
+ * update address lifetime based on RFC 2462
+ * 5.5.3 (e).
+ */
+ int update = 0;
+
+ lt6 = &ia6->ia6_lifetime;
+
+ /*
+ * update to RFC 2462 5.5.3 (e) from Jim Bound,
+ * (ipng 6712)
+ */
+ if (TWOHOUR < new->ndpr_vltime
+ || lt6->ia6t_vltime < new->ndpr_vltime) {
+ lt6->ia6t_vltime = new->ndpr_vltime;
+ update++;
+ } else if (auth) {
+ lt6->ia6t_vltime = new->ndpr_vltime;
+ update++;
+ }
+
+ /* jim bound rule is not imposed for pref lifetime */
+ lt6->ia6t_pltime = new->ndpr_pltime;
+
+ update++;
+ if (update)
+ in6_init_address_ltimes(new, lt6);
+ }
+
+ noautoconf1:
+
+ if (dr && pfxrtr_lookup(pr, dr) == NULL)
+ pfxrtr_add(pr, dr);
+ } else {
+ int error_tmp;
+
+ if (new->ndpr_vltime == 0) goto end;
+
+ bzero(&new->ndpr_addr, sizeof(struct in6_addr));
+
+ /*
+ * RFC 2462 5.5.3 (d)
+ * We got a fresh prefix. Perform some sanity checks
+ * and add an interface address by appending interface ID
+ * to the advertised prefix.
+ */
+ if (!new->ndpr_raf_auto)
+ goto noautoconf2;
+
+ ia6 = in6_ifadd(new->ndpr_ifp, &new->ndpr_prefix.sin6_addr,
+ &new->ndpr_addr, new->ndpr_plen);
+ if (!ia6) {
+ error = EADDRNOTAVAIL;
+ log(LOG_ERR, "prelist_update: "
+ "failed to add a new address\n");
+ goto noautoconf2;
+ }
+ /* set onlink bit if an interface route is configured */
+ new->ndpr_statef_onlink = (ia6->ia_flags & IFA_ROUTE) ? 1 : 0;
+
+ lt6 = &ia6->ia6_lifetime;
+
+ /* address lifetime <= prefix lifetime */
+ lt6->ia6t_vltime = new->ndpr_vltime;
+ lt6->ia6t_pltime = new->ndpr_pltime;
+ in6_init_address_ltimes(new, lt6);
+
+ noautoconf2:
+ error_tmp = prelist_add(new, dr);
+ error = error_tmp ? error_tmp : error;
+ }
+
+ end:
+ splx(s);
+ return error;
+}
+
+/*
+ * Check if each prefix in the prefix list has at least one available router
+ * that advertised the prefix.
+ * If the check fails, the prefix may be off-link because, for example,
+ * we have moved from the network but the lifetime of the prefix has not
+ * been expired yet. So we should not use the prefix if there is another
+ * prefix that has an available router.
+ * But if there is no prefix that has an availble router, we still regards
+ * all the prefixes as on-link. This is because we can't tell if all the
+ * routers are simply dead or if we really moved from the network and there
+ * is no router around us.
+ */
+static void
+pfxlist_onlink_check()
+{
+ struct nd_prefix *pr;
+
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next)
+ if (pr->ndpr_advrtrs.lh_first) /* pr has an available router */
+ break;
+
+ if (pr) {
+ /*
+ * There is at least one prefix that has a router. First,
+ * detach prefixes which has no advertising router and then
+ * attach other prefixes. The order is important since an
+ * attached prefix and a detached prefix may have a same
+ * interface route.
+ */
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ if (pr->ndpr_advrtrs.lh_first == NULL &&
+ pr->ndpr_statef_onlink) {
+ pr->ndpr_statef_onlink = 0;
+ nd6_detach_prefix(pr);
+ }
+ }
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ if (pr->ndpr_advrtrs.lh_first &&
+ pr->ndpr_statef_onlink == 0)
+ nd6_attach_prefix(pr);
+ }
+ } else {
+ /* there is no prefix that has a router */
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next)
+ if (pr->ndpr_statef_onlink == 0)
+ nd6_attach_prefix(pr);
+ }
+}
+
+static void
+nd6_detach_prefix(pr)
+ struct nd_prefix *pr;
+{
+ struct in6_ifaddr *ia6;
+ struct sockaddr_in6 sa6, mask6;
+
+ /*
+ * Delete the interface route associated with the prefix.
+ */
+ bzero(&sa6, sizeof(sa6));
+ sa6.sin6_family = AF_INET6;
+ sa6.sin6_len = sizeof(sa6);
+ bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr,
+ sizeof(struct in6_addr));
+ bzero(&mask6, sizeof(mask6));
+ mask6.sin6_family = AF_INET6;
+ mask6.sin6_len = sizeof(sa6);
+ bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr));
+ {
+ int e;
+
+ e = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL,
+ (struct sockaddr *)&mask6, 0, NULL);
+ if (e) {
+ log(LOG_ERR,
+ "nd6_detach_prefix: failed to delete route: "
+ "%s/%d (errno = %d)\n",
+ ip6_sprintf(&sa6.sin6_addr),
+ pr->ndpr_plen,
+ e);
+ }
+ }
+
+ /*
+ * Mark the address derived from the prefix detached so that
+ * it won't be used as a source address for a new connection.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ ia6 = NULL;
+ else
+ ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
+ if (ia6)
+ ia6->ia6_flags |= IN6_IFF_DETACHED;
+}
+
+static void
+nd6_attach_prefix(pr)
+ struct nd_prefix *pr;
+{
+ struct ifaddr *ifa;
+ struct in6_ifaddr *ia6;
+
+ /*
+ * Add the interface route associated with the prefix(if necessary)
+ * Should we consider if the L bit is set in pr->ndpr_flags?
+ */
+ ifa = ifaof_ifpforaddr((struct sockaddr *)&pr->ndpr_prefix,
+ pr->ndpr_ifp);
+ if (ifa == NULL) {
+ log(LOG_ERR,
+ "nd6_attach_prefix: failed to find any ifaddr"
+ " to add route for a prefix(%s/%d)\n",
+ ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen);
+ } else {
+ int e;
+ struct sockaddr_in6 mask6;
+
+ bzero(&mask6, sizeof(mask6));
+ mask6.sin6_family = AF_INET6;
+ mask6.sin6_len = sizeof(mask6);
+ mask6.sin6_addr = pr->ndpr_mask;
+ e = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix,
+ ifa->ifa_addr, (struct sockaddr *)&mask6,
+ ifa->ifa_flags, NULL);
+ if (e == 0)
+ pr->ndpr_statef_onlink = 1;
+ else {
+ log(LOG_ERR,
+ "nd6_attach_prefix: failed to add route for"
+ " a prefix(%s/%d), errno = %d\n",
+ ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen, e);
+ }
+ }
+
+ /*
+ * Now the address derived from the prefix can be used as a source
+ * for a new connection, so clear the detached flag.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ ia6 = NULL;
+ else
+ ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
+ if (ia6) {
+ ia6->ia6_flags &= ~IN6_IFF_DETACHED;
+ if (pr->ndpr_statef_onlink)
+ ia6->ia_flags |= IFA_ROUTE;
+ }
+}
+
+static struct in6_ifaddr *
+in6_ifadd(ifp, in6, addr, prefixlen)
+ struct ifnet *ifp;
+ struct in6_addr *in6;
+ struct in6_addr *addr;
+ int prefixlen; /* prefix len of the new prefix in "in6" */
+{
+ struct ifaddr *ifa;
+ struct in6_ifaddr *ia, *ib, *oia;
+ int s, error;
+ struct in6_addr mask;
+
+ in6_len2mask(&mask, prefixlen);
+
+ /* find link-local address (will be interface ID) */
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
+ if (ifa)
+ ib = (struct in6_ifaddr *)ifa;
+ else
+ return NULL;
+
+ /* prefixlen + ifidlen must be equal to 128 */
+ if (prefixlen != in6_mask2len(&ib->ia_prefixmask.sin6_addr)) {
+ log(LOG_ERR, "in6_ifadd: wrong prefixlen for %s"
+ "(prefix=%d ifid=%d)\n", if_name(ifp),
+ prefixlen,
+ 128 - in6_mask2len(&ib->ia_prefixmask.sin6_addr));
+ return NULL;
+ }
+
+ /* make ifaddr */
+ ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_DONTWAIT);
+ if (ia == NULL) {
+ printf("ENOBUFS in in6_ifadd %d\n", __LINE__);
+ return NULL;
+ }
+
+ bzero((caddr_t)ia, sizeof(*ia));
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
+ ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
+ ia->ia_ifp = ifp;
+
+ /* link to in6_ifaddr */
+ if ((oia = in6_ifaddr) != NULL) {
+ for( ; oia->ia_next; oia = oia->ia_next)
+ continue;
+ oia->ia_next = ia;
+ } else
+ in6_ifaddr = ia;
+
+ /* link to if_addrlist */
+ if (ifp->if_addrlist.tqh_first != NULL) {
+ TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia,
+ ifa_list);
+ }
+
+ /* new address */
+ ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
+ ia->ia_addr.sin6_family = AF_INET6;
+ /* prefix */
+ bcopy(in6, &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr));
+ ia->ia_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0];
+ ia->ia_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1];
+ ia->ia_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2];
+ ia->ia_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3];
+ /* interface ID */
+ ia->ia_addr.sin6_addr.s6_addr32[0]
+ |= (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]);
+ ia->ia_addr.sin6_addr.s6_addr32[1]
+ |= (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]);
+ ia->ia_addr.sin6_addr.s6_addr32[2]
+ |= (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]);
+ ia->ia_addr.sin6_addr.s6_addr32[3]
+ |= (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]);
+
+ /* new prefix */
+ ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ ia->ia_prefixmask.sin6_family = AF_INET6;
+ bcopy(&mask, &ia->ia_prefixmask.sin6_addr,
+ sizeof(ia->ia_prefixmask.sin6_addr));
+
+ /* same routine */
+ ia->ia_ifa.ifa_rtrequest =
+ (ifp->if_type == IFT_PPP) ? nd6_p2p_rtrequest : nd6_rtrequest;
+ ia->ia_ifa.ifa_flags |= RTF_CLONING;
+ ia->ia_ifa.ifa_metric = ifp->if_metric;
+
+ /* add interface route */
+ if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP|RTF_CLONING))) {
+ log(LOG_NOTICE, "in6_ifadd: failed to add an interface route "
+ "for %s/%d on %s, errno = %d\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr), prefixlen,
+ if_name(ifp), error);
+ } else
+ ia->ia_flags |= IFA_ROUTE;
+
+ *addr = ia->ia_addr.sin6_addr;
+
+ if (ifp->if_flags & IFF_MULTICAST) {
+ int error; /* not used */
+ struct in6_addr sol6;
+
+ /* join solicited node multicast address */
+ bzero(&sol6, sizeof(sol6));
+ sol6.s6_addr16[0] = htons(0xff02);
+ sol6.s6_addr16[1] = htons(ifp->if_index);
+ sol6.s6_addr32[1] = 0;
+ sol6.s6_addr32[2] = htonl(1);
+ sol6.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3];
+ sol6.s6_addr8[12] = 0xff;
+ (void)in6_addmulti(&sol6, ifp, &error);
+ }
+
+ ia->ia6_flags |= IN6_IFF_TENTATIVE;
+
+ /*
+ * To make the interface up. Only AF_INET6 in ia is used...
+ */
+ s = splimp();
+ if (ifp->if_ioctl && (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia)) {
+ splx(s);
+ return NULL;
+ }
+ splx(s);
+
+ /* Perform DAD, if needed. */
+ nd6_dad_start((struct ifaddr *)ia, NULL);
+
+ return ia;
+}
+
+int
+in6_ifdel(ifp, in6)
+ struct ifnet *ifp;
+ struct in6_addr *in6;
+{
+ struct in6_ifaddr *ia = (struct in6_ifaddr *)NULL;
+ struct in6_ifaddr *oia = (struct in6_ifaddr *)NULL;
+
+ if (!ifp)
+ return -1;
+
+ ia = in6ifa_ifpwithaddr(ifp, in6);
+ if (!ia)
+ return -1;
+
+ if (ifp->if_flags & IFF_MULTICAST) {
+ /*
+ * delete solicited multicast addr for deleting host id
+ */
+ struct in6_multi *in6m;
+ struct in6_addr llsol;
+ bzero(&llsol, sizeof(struct in6_addr));
+ llsol.s6_addr16[0] = htons(0xff02);
+ llsol.s6_addr16[1] = htons(ifp->if_index);
+ llsol.s6_addr32[1] = 0;
+ llsol.s6_addr32[2] = htonl(1);
+ llsol.s6_addr32[3] =
+ ia->ia_addr.sin6_addr.s6_addr32[3];
+ llsol.s6_addr8[12] = 0xff;
+
+ IN6_LOOKUP_MULTI(llsol, ifp, in6m);
+ if (in6m)
+ in6_delmulti(in6m);
+ }
+
+ if (ia->ia_flags & IFA_ROUTE) {
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
+ ia->ia_flags &= ~IFA_ROUTE;
+ }
+
+ TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+
+ /* lladdr is never deleted */
+ oia = ia;
+ if (oia == (ia = in6_ifaddr))
+ in6_ifaddr = ia->ia_next;
+ else {
+ while (ia->ia_next && (ia->ia_next != oia))
+ ia = ia->ia_next;
+ if (ia->ia_next)
+ ia->ia_next = oia->ia_next;
+ else
+ return -1;
+ }
+
+ IFAFREE((&oia->ia_ifa));
+/* xxx
+ rtrequest(RTM_DELETE,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)0
+ (struct sockaddr *)&ia->ia_prefixmask,
+ RTF_UP|RTF_CLONING,
+ (struct rtentry **)0);
+*/
+ return 0;
+}
+
+int
+in6_init_prefix_ltimes(struct nd_prefix *ndpr)
+{
+
+ /* check if preferred lifetime > valid lifetime */
+ if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) {
+ log(LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime"
+ "(%d) is greater than valid lifetime(%d)\n",
+ (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime);
+ return (EINVAL);
+ }
+ if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME)
+ ndpr->ndpr_preferred = 0;
+ else
+ ndpr->ndpr_preferred = time_second + ndpr->ndpr_pltime;
+ if (ndpr->ndpr_vltime == ND6_INFINITE_LIFETIME)
+ ndpr->ndpr_expire = 0;
+ else
+ ndpr->ndpr_expire = time_second + ndpr->ndpr_vltime;
+
+ return 0;
+}
+
+static void
+in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6)
+{
+
+ /* init ia6t_expire */
+ if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME)
+ lt6->ia6t_expire = 0;
+ else {
+ lt6->ia6t_expire = time_second;
+ lt6->ia6t_expire += lt6->ia6t_vltime;
+ }
+ /* init ia6t_preferred */
+ if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME)
+ lt6->ia6t_preferred = 0;
+ else {
+ lt6->ia6t_preferred = time_second;
+ lt6->ia6t_preferred += lt6->ia6t_pltime;
+ }
+ /* ensure addr lifetime <= prefix lifetime */
+ if (new->ndpr_expire && lt6->ia6t_expire &&
+ new->ndpr_expire < lt6->ia6t_expire)
+ lt6->ia6t_expire = new->ndpr_expire;
+ if (new->ndpr_preferred && lt6->ia6t_preferred
+ && new->ndpr_preferred < lt6->ia6t_preferred)
+ lt6->ia6t_preferred = new->ndpr_preferred;
+}
+
+/*
+ * Delete all the routing table entries that use the specified gateway.
+ * XXX: this function causes search through all entries of routing table, so
+ * it shouldn't be called when acting as a router.
+ */
+void
+rt6_flush(gateway, ifp)
+ struct in6_addr *gateway;
+ struct ifnet *ifp;
+{
+ struct radix_node_head *rnh = rt_tables[AF_INET6];
+ int s = splnet();
+
+ /* We'll care only link-local addresses */
+ if (!IN6_IS_ADDR_LINKLOCAL(gateway)) {
+ splx(s);
+ return;
+ }
+ /* XXX: hack for KAME's link-local address kludge */
+ gateway->s6_addr16[1] = htons(ifp->if_index);
+
+ rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway);
+ splx(s);
+}
+
+static int
+rt6_deleteroute(rn, arg)
+ struct radix_node *rn;
+ void *arg;
+{
+#define SIN6(s) ((struct sockaddr_in6 *)s)
+ struct rtentry *rt = (struct rtentry *)rn;
+ struct in6_addr *gate = (struct in6_addr *)arg;
+
+ if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6)
+ return(0);
+
+ if (!IN6_ARE_ADDR_EQUAL(gate, &SIN6(rt->rt_gateway)->sin6_addr))
+ return(0);
+
+ /*
+ * We delete only host route. This means, in particular, we don't
+ * delete default route.
+ */
+ if ((rt->rt_flags & RTF_HOST) == 0)
+ return(0);
+
+ return(rtrequest(RTM_DELETE, rt_key(rt),
+ rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0));
+#undef SIN6
+}
diff --git a/sys/netinet6/pim6.h b/sys/netinet6/pim6.h
new file mode 100644
index 0000000..1e47785
--- /dev/null
+++ b/sys/netinet6/pim6.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Protocol Independent Multicast (PIM) definitions
+ *
+ * Written by Ahmed Helmy, SGI, July 1996
+ *
+ * MULTICAST
+ */
+
+/*
+ * PIM packet header
+ */
+#define PIM_VERSION 2
+struct pim {
+#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+ u_char pim_type:4, /* the PIM message type, currently they are:
+ * Hello, Register, Register-Stop, Join/Prune,
+ * Bootstrap, Assert, Graft (PIM-DM only),
+ * Graft-Ack (PIM-DM only), C-RP-Adv
+ */
+ pim_ver:4; /* PIM version number; 2 for PIMv2 */
+#else
+ u_char pim_ver:4, /* PIM version */
+ pim_type:4; /* PIM type */
+#endif
+ u_char pim_rsv; /* Reserved */
+ u_short pim_cksum; /* IP style check sum */
+};
+
+#define PIM_MINLEN 8 /* The header min. length is 8 */
+#define PIM6_REG_MINLEN (PIM_MINLEN+40) /* Register message + inner IP6 header */
+
+/*
+ * Message types
+ */
+#define PIM_REGISTER 1 /* PIM Register type is 1 */
+
+/* second bit in reg_head is the null bit */
+#define PIM_NULL_REGISTER 0x40000000
diff --git a/sys/netinet6/pim6_var.h b/sys/netinet6/pim6_var.h
new file mode 100644
index 0000000..3610a40
--- /dev/null
+++ b/sys/netinet6/pim6_var.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/* $Id: pim6_var.h,v 1.2 1999/08/01 15:58:13 itojun Exp $ */
+
+#ifndef _NETINET6_PIM6_VAR_H_
+#define _NETINET6_PIM6_VAR_H_
+
+/*
+ * Protocol Independent Multicast (PIM),
+ * implementation-specific definitions.
+ *
+ * Written by George Edmond Eddy (Rusty), ISI, February 1998
+ * Modified by Pavlin Ivanov Radoslavov, USC/ISI, May 1998
+ */
+
+struct pim6stat {
+ u_int pim6s_rcv_total; /* total PIM messages received */
+ u_int pim6s_rcv_tooshort; /* received with too few bytes */
+ u_int pim6s_rcv_badsum; /* received with bad checksum */
+ u_int pim6s_rcv_badversion; /* received bad PIM version */
+ u_int pim6s_rcv_registers; /* received registers */
+ u_int pim6s_rcv_badregisters; /* received invalid registers */
+ u_int pim6s_snd_registers; /* sent registers */
+};
+
+#if (defined(KERNEL)) || (defined(_KERNEL))
+extern struct pim6stat pim6stat;
+
+int pim6_input __P((struct mbuf **, int*, int));
+#endif /* KERNEL */
+
+/*
+ * Names for PIM sysctl objects
+ */
+#define PIMCTL_STATS 1 /* statistics (read-only) */
+#define PIMCTL_MAXID 2
+
+#define PIMCTL_NAMES { \
+ { 0, 0 }, \
+ { 0, 0 }, \
+}
+
+#endif /* _NETINET6_PIM6_VAR_H_ */
diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c
new file mode 100644
index 0000000..5698f1c3
--- /dev/null
+++ b/sys/netinet6/raw_ip6.c
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include "opt_inet.h"
+#include "opt_ipsec.h"
+
+#include <stddef.h>
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+#include <netinet/in_pcb.h>
+#include <netinet6/in6_pcb.h>
+#include <netinet6/nd6.h>
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif /* INET6 */
+#endif /*IPSEC*/
+
+#include <machine/stdarg.h>
+
+/* #include "faith.h" */
+
+#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
+#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa))
+
+/*
+ * Raw interface to IP6 protocol.
+ */
+
+extern struct inpcbhead ripcb;
+extern struct inpcbinfo ripcbinfo;
+extern u_long rip_sendspace;
+extern u_long rip_recvspace;
+
+/*
+ * Setup generic address and protocol structures
+ * for raw_input routine, then pass them along with
+ * mbuf chain.
+ */
+int
+rip6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m = *mp;
+ register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ register struct inpcb *in6p;
+ struct inpcb *last = 0;
+ struct mbuf *opts = 0;
+ struct sockaddr_in6 rip6src;
+
+#if defined(NFAITH) && 0 < NFAITH
+ if (m->m_pkthdr.rcvif) {
+ if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
+ /* XXX send icmp6 host/port unreach? */
+ m_freem(m);
+ return IPPROTO_DONE;
+ }
+ }
+#endif
+ init_sin6(&rip6src, m); /* general init */
+
+ LIST_FOREACH(in6p, &ripcb, inp_list) {
+ if ((in6p->in6p_vflag & INP_IPV6) == NULL)
+ continue;
+ if (in6p->in6p_ip6_nxt &&
+ in6p->in6p_ip6_nxt != proto)
+ continue;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
+ !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst))
+ continue;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) &&
+ !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
+ continue;
+ if (in6p->in6p_cksum != -1
+ && in6_cksum(m, ip6->ip6_nxt, *offp,
+ m->m_pkthdr.len - *offp)) {
+ /* XXX bark something */
+ continue;
+ }
+ if (last) {
+ struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
+ if (n) {
+ if (last->in6p_flags & IN6P_CONTROLOPTS ||
+ last->in6p_socket->so_options & SO_TIMESTAMP)
+ ip6_savecontrol(last, &opts, ip6, n);
+ /* strip intermediate headers */
+ m_adj(n, *offp);
+ if (sbappendaddr(&last->in6p_socket->so_rcv,
+ (struct sockaddr *)&rip6src,
+ n, opts) == 0) {
+ /* should notify about lost packet */
+ m_freem(n);
+ if (opts)
+ m_freem(opts);
+ } else
+ sorwakeup(last->in6p_socket);
+ opts = NULL;
+ }
+ }
+ last = in6p;
+ }
+ if (last) {
+ if (last->in6p_flags & IN6P_CONTROLOPTS ||
+ last->in6p_socket->so_options & SO_TIMESTAMP)
+ ip6_savecontrol(last, &opts, ip6, m);
+ /* strip intermediate headers */
+ m_adj(m, *offp);
+ if (sbappendaddr(&last->in6p_socket->so_rcv,
+ (struct sockaddr *)&rip6src, m, opts) == 0) {
+ m_freem(m);
+ if (opts)
+ m_freem(opts);
+ } else
+ sorwakeup(last->in6p_socket);
+ } else {
+ if (proto == IPPROTO_NONE)
+ m_freem(m);
+ else {
+ char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_NEXTHEADER,
+ prvnxtp - mtod(m, char *));
+ }
+ ip6stat.ip6s_delivered--;
+ }
+ return IPPROTO_DONE;
+}
+
+/*
+ * Generate IPv6 header and pass packet to ip6_output.
+ * Tack on options user may have setup with control call.
+ */
+int
+#if __STDC__
+rip6_output(struct mbuf *m, ...)
+#else
+rip6_output(m, va_alist)
+ struct mbuf *m;
+ va_dcl
+#endif
+{
+ struct socket *so;
+ struct sockaddr_in6 *dstsock;
+ struct mbuf *control;
+ struct in6_addr *dst;
+ struct ip6_hdr *ip6;
+ struct inpcb *in6p;
+ u_int plen = m->m_pkthdr.len;
+ int error = 0;
+ struct ip6_pktopts opt, *optp = 0;
+ struct ifnet *oifp = NULL;
+ int type = 0, code = 0; /* for ICMPv6 output statistics only */
+ int priv = 0;
+ va_list ap;
+
+ va_start(ap, m);
+ so = va_arg(ap, struct socket *);
+ dstsock = va_arg(ap, struct sockaddr_in6 *);
+ control = va_arg(ap, struct mbuf *);
+ va_end(ap);
+
+ in6p = sotoin6pcb(so);
+
+ priv = 0;
+ if (so->so_cred->cr_uid == 0)
+ priv = 1;
+ dst = &dstsock->sin6_addr;
+ if (control) {
+ if ((error = ip6_setpktoptions(control, &opt, priv)) != 0)
+ goto bad;
+ optp = &opt;
+ } else
+ optp = in6p->in6p_outputopts;
+
+ /*
+ * For an ICMPv6 packet, we should know its type and code
+ * to update statistics.
+ */
+ if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
+ struct icmp6_hdr *icmp6;
+ if (m->m_len < sizeof(struct icmp6_hdr) &&
+ (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ icmp6 = mtod(m, struct icmp6_hdr *);
+ type = icmp6->icmp6_type;
+ code = icmp6->icmp6_code;
+ }
+
+ M_PREPEND(m, sizeof(*ip6), M_WAIT);
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ /*
+ * Next header might not be ICMP6 but use its pseudo header anyway.
+ */
+ ip6->ip6_dst = *dst;
+
+ /*
+ * If the scope of the destination is link-local, embed the interface
+ * index in the address.
+ *
+ * XXX advanced-api value overrides sin6_scope_id
+ */
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
+ struct in6_pktinfo *pi;
+
+ /*
+ * XXX Boundary check is assumed to be already done in
+ * ip6_setpktoptions().
+ */
+ if (optp && (pi = optp->ip6po_pktinfo) && pi->ipi6_ifindex) {
+ ip6->ip6_dst.s6_addr16[1] = htons(pi->ipi6_ifindex);
+ oifp = ifindex2ifnet[pi->ipi6_ifindex];
+ } else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
+ in6p->in6p_moptions &&
+ in6p->in6p_moptions->im6o_multicast_ifp) {
+ oifp = in6p->in6p_moptions->im6o_multicast_ifp;
+ ip6->ip6_dst.s6_addr16[1] = htons(oifp->if_index);
+ } else if (dstsock->sin6_scope_id) {
+ /* boundary check */
+ if (dstsock->sin6_scope_id < 0
+ || if_index < dstsock->sin6_scope_id) {
+ error = ENXIO; /* XXX EINVAL? */
+ goto bad;
+ }
+ ip6->ip6_dst.s6_addr16[1]
+ = htons(dstsock->sin6_scope_id & 0xffff);/*XXX*/
+ }
+ }
+
+ /*
+ * Source address selection.
+ */
+ {
+ struct in6_addr *in6a;
+
+ if ((in6a = in6_selectsrc(dstsock, optp,
+ in6p->in6p_moptions,
+ &in6p->in6p_route,
+ &in6p->in6p_laddr,
+ &error)) == 0) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+ ip6->ip6_src = *in6a;
+ if (in6p->in6p_route.ro_rt)
+ oifp = ifindex2ifnet[in6p->in6p_route.ro_rt->rt_ifp->if_index];
+ }
+
+ ip6->ip6_flow = in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK;
+ ip6->ip6_vfc = IPV6_VERSION;
+ /* ip6_plen will be filled in ip6_output, so not fill it here. */
+ ip6->ip6_nxt = in6p->in6p_ip6_nxt;
+ ip6->ip6_hlim = in6_selecthlim(in6p, oifp);
+
+ if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 ||
+ in6p->in6p_cksum != -1) {
+ struct mbuf *n;
+ int off;
+ u_int16_t *p;
+
+#define offsetof(type, member) ((size_t)(&((type *)0)->member)) /* XXX */
+
+ /* compute checksum */
+ if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
+ off = offsetof(struct icmp6_hdr, icmp6_cksum);
+ else
+ off = in6p->in6p_cksum;
+ if (plen < off + 1) {
+ error = EINVAL;
+ goto bad;
+ }
+ off += sizeof(struct ip6_hdr);
+
+ n = m;
+ while (n && n->m_len <= off) {
+ off -= n->m_len;
+ n = n->m_next;
+ }
+ if (!n)
+ goto bad;
+ p = (u_int16_t *)(mtod(n, caddr_t) + off);
+ *p = 0;
+ *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen);
+ }
+
+#ifdef IPSEC
+ m->m_pkthdr.rcvif = (struct ifnet *)so;
+#endif /*IPSEC*/
+
+ error = ip6_output(m, optp, &in6p->in6p_route, 0, in6p->in6p_moptions,
+ &oifp);
+ if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
+ if (oifp)
+ icmp6_ifoutstat_inc(oifp, type, code);
+ icmp6stat.icp6s_outhist[type]++;
+ }
+
+ goto freectl;
+
+ bad:
+ if (m)
+ m_freem(m);
+
+ freectl:
+ if (optp == &opt && optp->ip6po_rthdr && optp->ip6po_route.ro_rt)
+ RTFREE(optp->ip6po_route.ro_rt);
+ if (control)
+ m_freem(control);
+ return(error);
+}
+
+/*
+ * Raw IPv6 socket option processing.
+ */
+int
+rip6_ctloutput(so, sopt)
+ struct socket *so;
+ struct sockopt *sopt;
+{
+ int error;
+
+ if (sopt->sopt_level == IPPROTO_ICMPV6)
+ /*
+ * XXX: is it better to call icmp6_ctloutput() directly
+ * from protosw?
+ */
+ return(icmp6_ctloutput(so, sopt));
+ else if (sopt->sopt_level != IPPROTO_IPV6)
+ return (EINVAL);
+
+ error = 0;
+
+ switch (sopt->sopt_dir) {
+ case SOPT_GET:
+ switch (sopt->sopt_name) {
+ default:
+ error = ip6_ctloutput(so, sopt);
+ break;
+ }
+ break;
+
+ case SOPT_SET:
+ switch (sopt->sopt_name) {
+ default:
+ error = ip6_ctloutput(so, sopt);
+ break;
+ }
+ break;
+ }
+
+ return (error);
+}
+
+static int
+rip6_attach(struct socket *so, int proto, struct proc *p)
+{
+ struct inpcb *inp;
+ int error, s;
+
+ inp = sotoinpcb(so);
+ if (inp)
+ panic("rip6_attach");
+ if (p && (error = suser(p)) != 0)
+ return error;
+
+ if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
+ error = soreserve(so, rip_sendspace, rip_recvspace);
+ if (error)
+ return error;
+ }
+ s = splnet();
+ error = in_pcballoc(so, &ripcbinfo, p);
+ splx(s);
+ if (error)
+ return error;
+ inp = (struct inpcb *)so->so_pcb;
+ inp->inp_vflag |= INP_IPV6;
+ inp->in6p_ip6_nxt = (long)proto;
+ inp->in6p_hops = -1; /* use kernel default */
+ inp->in6p_cksum = -1;
+#ifdef IPSEC
+ error = ipsec_init_policy(so, &inp->in6p_sp);
+ if (error != 0) {
+ in6_pcbdetach(inp);
+ return (error);
+ }
+#endif /*IPSEC*/
+ MALLOC(inp->in6p_icmp6filt, struct icmp6_filter *,
+ sizeof(struct icmp6_filter), M_PCB, M_NOWAIT);
+ ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt);
+ return 0;
+}
+
+static int
+rip6_detach(struct socket *so)
+{
+ struct inpcb *inp;
+
+ inp = sotoinpcb(so);
+ if (inp == 0)
+ panic("rip6_detach");
+ /* xxx: RSVP */
+ if (inp->in6p_icmp6filt) {
+ FREE(inp->in6p_icmp6filt, M_PCB);
+ inp->in6p_icmp6filt = NULL;
+ }
+ in6_pcbdetach(inp);
+ return 0;
+}
+
+static int
+rip6_abort(struct socket *so)
+{
+ soisdisconnected(so);
+ return rip6_detach(so);
+}
+
+static int
+rip6_disconnect(struct socket *so)
+{
+ struct inpcb *inp = sotoinpcb(so);
+
+ if ((so->so_state & SS_ISCONNECTED) == 0)
+ return ENOTCONN;
+ inp->in6p_faddr = in6addr_any;
+ return rip6_abort(so);
+}
+
+static int
+rip6_bind(struct socket *so, struct sockaddr *nam, struct proc *p)
+{
+ struct inpcb *inp = sotoinpcb(so);
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
+ struct ifaddr *ia = NULL;
+
+ if (nam->sa_len != sizeof(*addr))
+ return EINVAL;
+
+ if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6)
+ return EADDRNOTAVAIL;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) &&
+ (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0)
+ return EADDRNOTAVAIL;
+ if (ia &&
+ ((struct in6_ifaddr *)ia)->ia6_flags &
+ (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|
+ IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) {
+ return(EADDRNOTAVAIL);
+ }
+ inp->in6p_laddr = addr->sin6_addr;
+ return 0;
+}
+
+static int
+rip6_connect(struct socket *so, struct sockaddr *nam, struct proc *p)
+{
+ struct inpcb *inp = sotoinpcb(so);
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
+ struct in6_addr *in6a = NULL;
+ int error = 0;
+
+ if (nam->sa_len != sizeof(*addr))
+ return EINVAL;
+ if (TAILQ_EMPTY(&ifnet))
+ return EADDRNOTAVAIL;
+ if (addr->sin6_family != AF_INET6)
+ return EAFNOSUPPORT;
+
+ /* Source address selection. XXX: need pcblookup? */
+ in6a = in6_selectsrc(addr, inp->in6p_outputopts,
+ inp->in6p_moptions, &inp->in6p_route,
+ &inp->in6p_laddr, &error);
+ if (in6a == NULL)
+ return (error ? error : EADDRNOTAVAIL);
+ inp->in6p_laddr = *in6a;
+ inp->in6p_faddr = addr->sin6_addr;
+ soisconnected(so);
+ return 0;
+}
+
+static int
+rip6_shutdown(struct socket *so)
+{
+ socantsendmore(so);
+ return 0;
+}
+
+static int
+rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
+ struct mbuf *control, struct proc *p)
+{
+ struct inpcb *inp = sotoinpcb(so);
+ struct sockaddr_in6 tmp;
+ struct sockaddr_in6 *dst;
+
+ if (so->so_state & SS_ISCONNECTED) {
+ if (nam) {
+ m_freem(m);
+ return EISCONN;
+ }
+ /* XXX */
+ bzero(&tmp, sizeof(tmp));
+ tmp.sin6_family = AF_INET6;
+ tmp.sin6_len = sizeof(struct sockaddr_in6);
+ bcopy(&inp->in6p_faddr, &tmp.sin6_addr,
+ sizeof(struct in6_addr));
+ dst = &tmp;
+ } else {
+ if (nam == NULL) {
+ m_freem(m);
+ return ENOTCONN;
+ }
+ dst = (struct sockaddr_in6 *)nam;
+ }
+ return rip6_output(m, so, dst, control);
+}
+
+struct pr_usrreqs rip6_usrreqs = {
+ rip6_abort, pru_accept_notsupp, rip6_attach, rip6_bind, rip6_connect,
+ pru_connect2_notsupp, in6_control, rip6_detach, rip6_disconnect,
+ pru_listen_notsupp, in6_setpeeraddr, pru_rcvd_notsupp,
+ pru_rcvoob_notsupp, rip6_send, pru_sense_null, rip6_shutdown,
+ in6_setsockaddr, sosend, soreceive, sopoll
+};
diff --git a/sys/netinet6/route6.c b/sys/netinet6/route6.c
new file mode 100644
index 0000000..d50cf90
--- /dev/null
+++ b/sys/netinet6/route6.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet6/in6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+
+#include <netinet/icmp6.h>
+
+static int ip6_rthdr0 __P((struct mbuf *, struct ip6_hdr *, struct ip6_rthdr0 *));
+
+int
+route6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto; /* proto is unused */
+{
+ register struct ip6_hdr *ip6;
+ register struct mbuf *m = *mp;
+ register struct ip6_rthdr *rh;
+ int off = *offp, rhlen;
+
+ IP6_EXTHDR_CHECK(m, off, sizeof(*rh), IPPROTO_DONE);
+ ip6 = mtod(m, struct ip6_hdr *);
+ rh = (struct ip6_rthdr *)((caddr_t)ip6 + off);
+
+ switch(rh->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ rhlen = (rh->ip6r_len + 1) << 3;
+ IP6_EXTHDR_CHECK(m, off, rhlen, IPPROTO_DONE);
+ if (ip6_rthdr0(m, ip6, (struct ip6_rthdr0 *)rh))
+ return(IPPROTO_DONE);
+ break;
+ default:
+ /* unknown routing type */
+ if (rh->ip6r_segleft == 0) {
+ rhlen = (rh->ip6r_len + 1) << 3;
+ break; /* Final dst. Just ignore the header. */
+ }
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
+ (caddr_t)&rh->ip6r_type - (caddr_t)ip6);
+ return(IPPROTO_DONE);
+ }
+
+ *offp += rhlen;
+ return(rh->ip6r_nxt);
+}
+
+/*
+ * Type0 routing header processing
+ */
+static int
+ip6_rthdr0(m, ip6, rh0)
+ struct mbuf *m;
+ struct ip6_hdr *ip6;
+ struct ip6_rthdr0 *rh0;
+{
+ int addrs, index;
+ struct in6_addr *nextaddr, tmpaddr;
+
+ if (rh0->ip6r0_segleft == 0)
+ return(0);
+
+ if (rh0->ip6r0_len % 2
+#ifdef COMPAT_RFC1883
+ || rh0->ip6r0_len > 46
+#endif
+ ) {
+ /*
+ * Type 0 routing header can't contain more than 23 addresses.
+ * RFC 2462: this limitation was removed since stict/loose
+ * bitmap field was deleted.
+ */
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
+ (caddr_t)&rh0->ip6r0_len - (caddr_t)ip6);
+ return(-1);
+ }
+
+ if ((addrs = rh0->ip6r0_len / 2) < rh0->ip6r0_segleft) {
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
+ (caddr_t)&rh0->ip6r0_segleft - (caddr_t)ip6);
+ return(-1);
+ }
+
+ index = addrs - rh0->ip6r0_segleft;
+ rh0->ip6r0_segleft--;
+ nextaddr = rh0->ip6r0_addr + index;
+
+ if (IN6_IS_ADDR_MULTICAST(nextaddr) ||
+ IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ ip6stat.ip6s_badoptions++;
+ m_freem(m);
+ return(-1);
+ }
+
+ /*
+ * Swap the IPv6 destination address and nextaddr. Forward the packet.
+ */
+ tmpaddr = *nextaddr;
+ *nextaddr = ip6->ip6_dst;
+ if (IN6_IS_ADDR_LINKLOCAL(nextaddr))
+ nextaddr->s6_addr16[1] = 0;
+ ip6->ip6_dst = tmpaddr;
+ if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst))
+ ip6->ip6_dst.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index);
+
+#ifdef COMPAT_RFC1883
+ if (rh0->ip6r0_slmap[index / 8] & (1 << (7 - (index % 8))))
+ ip6_forward(m, IPV6_SRCRT_NEIGHBOR);
+ else
+ ip6_forward(m, IPV6_SRCRT_NOTNEIGHBOR);
+#else
+ ip6_forward(m, 1);
+#endif
+
+ return(-1); /* m would be freed in ip6_forward() */
+}
diff --git a/sys/netinet6/tcp6_var.h b/sys/netinet6/tcp6_var.h
new file mode 100644
index 0000000..93f41d9
--- /dev/null
+++ b/sys/netinet6/tcp6_var.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993, 1994, 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_var.h 8.4 (Berkeley) 5/24/95
+ * $FreeBSD$
+ */
+
+#ifndef _NETINET_TCP6_VAR_H_
+#define _NETINET_TCP6_VAR_H_
+
+#ifdef KERNEL
+
+struct ip6_hdr;
+void tcp6_ctlinput __P((int, struct sockaddr *, void *));
+void tcp6_init __P((void));
+int tcp6_input __P((struct mbuf **, int *, int));
+struct rtentry *tcp_rtlookup6 __P((struct inpcb *));
+
+extern struct pr_usrreqs tcp6_usrreqs;
+
+#endif /* KERNEL */
+
+#endif /* _NETINET_TCP6_VAR_H_ */
diff --git a/sys/netinet6/udp6_var.h b/sys/netinet6/udp6_var.h
new file mode 100644
index 0000000..b95bc78
--- /dev/null
+++ b/sys/netinet6/udp6_var.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * 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
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _NETINET6_UDP6_VAR_H_
+#define _NETINET6_UDP6_VAR_H_
+
+#ifdef KERNEL
+extern struct pr_usrreqs udp6_usrreqs;
+
+void udp6_ctlinput __P((int, struct sockaddr *, void *));
+int udp6_input __P((struct mbuf **, int *, int));
+int udp6_output __P((struct inpcb *inp, struct mbuf *m,
+ struct sockaddr *addr, struct mbuf *control,
+ struct proc *p));
+#endif /* KERNEL */
+
+#endif /*_NETINET6_UDP6_VAR_H_*/
OpenPOWER on IntegriCloud