summaryrefslogtreecommitdiff
path: root/sys/netinet6
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1999-01-06 23:26:59 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1999-01-06 23:26:59 +0000
commit003294c571f5404cf3bb2ab3cf33eba8643b932b (patch)
tree0efc8463ff2d3c2555c4a6a830fe508568cc5e07 /sys/netinet6
parent21e642825078d715499c3110ace9c6e61f6c6db4 (diff)
first few files of NRL ipv6. This NRL release was officially exported
to me by US DOD officials, with the crypto already removed.
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/icmpv6.h81
-rw-r--r--sys/netinet6/icmpv6_var.h52
-rw-r--r--sys/netinet6/in6.c910
-rw-r--r--sys/netinet6/in6_cksum.c232
-rw-r--r--sys/netinet6/in6_pcb.c1426
-rw-r--r--sys/netinet6/in6_proto.c387
-rw-r--r--sys/netinet6/in6_var.h189
-rw-r--r--sys/netinet6/ipv6.h123
-rw-r--r--sys/netinet6/ipv6_addrconf.c241
-rw-r--r--sys/netinet6/ipv6_addrconf.h22
-rw-r--r--sys/netinet6/ipv6_discovery.c3285
-rw-r--r--sys/netinet6/ipv6_icmp.c1077
-rw-r--r--sys/netinet6/ipv6_icmp.h257
-rw-r--r--sys/netinet6/ipv6_input.c1414
-rw-r--r--sys/netinet6/ipv6_output.c2078
-rw-r--r--sys/netinet6/ipv6_rtrequest.c1005
-rw-r--r--sys/netinet6/ipv6_trans.c669
-rw-r--r--sys/netinet6/ipv6_trans.h26
-rw-r--r--sys/netinet6/ipv6_var.h346
-rw-r--r--sys/netinet6/nd6_protocol.h100
-rw-r--r--sys/netinet6/raw_ipv6.c1059
-rw-r--r--sys/netinet6/tcpipv6.h36
22 files changed, 15015 insertions, 0 deletions
diff --git a/sys/netinet6/icmpv6.h b/sys/netinet6/icmpv6.h
new file mode 100644
index 00000000000..31ac045a677
--- /dev/null
+++ b/sys/netinet6/icmpv6.h
@@ -0,0 +1,81 @@
+/*
+%%% portions-copyright-nrl-97
+Portions of this software are Copyright 1997-1998 by Randall Atkinson,
+Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights
+Reserved. All rights under this copyright have been assigned to the US
+Naval Research Laboratory (NRL). The NRL Copyright Notice and License
+Agreement Version 1.1 (January 17, 1995) applies to these portions of the
+software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+
+#ifndef _NETINET6_ICMPV6_H
+#define _NETINET6_ICMPV6_H 1
+
+#include <netinet6/ipv6.h>
+
+struct icmpv6hdr {
+ uint8_t icmpv6_type; /* type field */
+ uint8_t icmpv6_code; /* code field */
+ uint16_t icmpv6_cksum; /* checksum field */
+ union {
+ uint32_t un_data32[1]; /* type-specific field */
+ uint16_t un_data16[2]; /* type-specific field */
+ uint8_t un_data8[4]; /* type-specific field */
+ } icmpv6_dataun;
+};
+
+#define icmpv6_data32 icmpv6_dataun.un_data32
+#define icmpv6_data16 icmpv6_dataun.un_data16
+#define icmpv6_data8 icmpv6_dataun.un_data8
+#define icmpv6_pptr icmpv6_data32[0] /* parameter prob */
+#define icmpv6_mtu icmpv6_data32[0] /* packet too big */
+#define icmpv6_id icmpv6_data16[0] /* echo request/reply */
+#define icmpv6_seq icmpv6_data16[1] /* echo request/reply */
+#define icmpv6_maxdelay icmpv6_data16[0] /* mcast group membership */
+
+#define ICMPV6_DST_UNREACH 1
+#define ICMPV6_PACKET_TOOBIG 2
+#define ICMPV6_TIME_EXCEEDED 3
+#define ICMPV6_PARAMETER_PROBLEM 4
+
+#define ICMPV6_INFOMSG_MASK 128 /* all informational messages */
+#define ICMPV6_ECHO_REQUEST 128
+#define ICMPV6_ECHO_REPLY 129
+#define ICMPV6_MEMBERSHIP_QUERY 130
+#define ICMPV6_MEMBERSHIP_REPORT 131
+#define ICMPV6_MEMBERSHIP_REDUCTION 132
+
+#define ICMPV6_UNREACH_NOROUTE 0
+#define ICMPV6_UNREACH_ADMIN 1 /* administratively prohibited */
+#define ICMPV6_UNREACH_NOTNEIGHBOR 2 /* not a neighbor (and must be) */
+#define ICMPV6_UNREACH_ADDRESS 3
+#define ICMPV6_UNREACH_PORT 4
+
+#define ICMPV6_EXCEEDED_HOPS 0 /* Hop Limit == 0 in transit */
+#define ICMPV6_EXCEEDED_REASSEMBLY 1 /* Reassembly time out */
+
+#define ICMPV6_PARAMPROB_HDR 0 /* erroneous header field */
+#define ICMPV6_PARAMPROB_NEXTHDR 1 /* unrecognized Next Header */
+#define ICMPV6_PARAMPROB_OPTION 2 /* unrecognized option */
+
+struct icmpv6_filter {
+ uint32_t data[8]; /* 8*32 = 256 bits */
+};
+
+#define ICMPV6_FILTER_WILLPASS(type, filterp) \
+ ((((filterp)->data[(type) >> 5]) & (1 << ((type) & 31))) == 0)
+#define ICMPV6_FILTER_WILLBLOCK(type, filterp) \
+ ((((filterp)->data[(type) >> 5]) & (1 << ((type) & 31))) != 0)
+#define ICMPV6_FILTER_SETPASS(type, filterp) \
+ ((((filterp)->data[(type) >> 5]) &= ~(1 << ((type) & 31))))
+#define ICMPV6_FILTER_SETBLOCK(type, filterp) \
+ ((((filterp)->data[(type) >> 5]) |= (1 << ((type) & 31))))
+#define ICMPV6_FILTER_SETPASSALL(filterp) \
+ memset((filterp), 0, sizeof(struct icmpv6_filter))
+#define ICMPV6_FILTER_SETBLOCKALL(filterp) \
+ memset((filterp), 0xff, sizeof(struct icmpv6_filter))
+
+#endif /* _NETINET6_ICMPV6_H */
diff --git a/sys/netinet6/icmpv6_var.h b/sys/netinet6/icmpv6_var.h
new file mode 100644
index 00000000000..36f86fbda1b
--- /dev/null
+++ b/sys/netinet6/icmpv6_var.h
@@ -0,0 +1,52 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+#ifndef _NETINET6_ICMPV6_VAR_H
+#define _NETINET6_ICMPV6_VAR_H 1
+
+#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
+#define _ICMPV6STAT_TYPE u_quad_t
+#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+#define _ICMPV6STAT_TYPE u_long
+#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+
+struct icmpv6stat
+{
+ _ICMPV6STAT_TYPE icps_error;
+ _ICMPV6STAT_TYPE icps_tooshort;
+ _ICMPV6STAT_TYPE icps_checksum;
+ _ICMPV6STAT_TYPE icps_outhist[ICMPV6_MAXTYPE+1];
+ _ICMPV6STAT_TYPE icps_badlen;
+ _ICMPV6STAT_TYPE icps_badcode;
+ _ICMPV6STAT_TYPE icps_reflect; /* Number of in-kernel responses */
+ _ICMPV6STAT_TYPE icps_inhist[ICMPV6_MAXTYPE+1];
+};
+
+/*
+ * Names for ICMPV6 sysctl objects
+ */
+#define ICMPV6CTL_STATS 1 /* statistics */
+#define ICMPV6CTL_MAXID 2
+
+#define ICMPV6CTL_NAMES { \
+ { 0, 0 }, \
+ { "stats", CTLTYPE_STRUCT }, \
+}
+
+#define ICMPV6CTL_VARS { \
+ 0, \
+ 0, \
+}
+
+#ifdef KERNEL
+struct icmpv6stat icmpv6stat;
+#endif
+#endif /* _NETINET6_ICMPV6_VAR_H */
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
new file mode 100644
index 00000000000..db08496f193
--- /dev/null
+++ b/sys/netinet6/in6.c
@@ -0,0 +1,910 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/systm.h>
+#if __NetBSD__ || __FreeBSD__
+#include <sys/proc.h>
+#endif /* __NetBSD__ || __FreeBSD__ */
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+
+#include <netinet6/in6_var.h>
+#include <netinet6/ipv6.h>
+#include <netinet6/ipv6_var.h>
+#include <netinet6/ipv6_icmp.h>
+
+#include <sys/debug.h>
+
+/*
+ * Globals
+ */
+
+struct ifnet *mcastdefault = NULL; /* Should be changeable by sysctl(). */
+
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+
+/*
+ * External globals
+ */
+
+extern struct sockaddr_in6 in6_allones;
+extern struct in6_ifaddr *in6_ifaddr;
+extern struct in6_ifnet *in6_ifnet;
+extern int ipv6forwarding;
+
+static void setmcastdef __P((register struct ifnet *));
+void del_in6_ifnet __P((struct ifnet *));
+struct in6_ifnet *add_in6_ifnet __P((struct ifnet *, int *));
+int in6_ifscrub __P((struct ifnet *, struct in6_ifaddr *));
+int in6_ifinit __P((register struct ifnet *, register struct in6_ifaddr *, struct sockaddr_in6 *, int, int));
+void addrconf_dad __P((struct in6_ifaddr *));
+
+/*----------------------------------------------------------------------
+ * Set the default multicast interface. In single-homed case, this will
+ * always be the non-loopback interface. In multi-homed cases, the function
+ * should be able to set one accordingly. The multicast route entry
+ * (ff00::/8) will have its rt_ifp point to this interface, and its rt_ifa
+ * point to whatever rtrequest() does. The rt_ifa should be more intelligently
+ * set eventually.
+ ----------------------------------------------------------------------*/
+
+static void
+setmcastdef(ifp)
+ register struct ifnet *ifp;
+{
+#ifdef __FreeBSD__
+struct ifaddr *ifa = ifp->if_addrhead.tqh_first;
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ struct ifaddr *ifa = ifp->if_addrlist.tqh_first;
+#else /* __NetBSD__ || __OpenBSD__ */
+ struct ifaddr *ifa = ifp->if_addrlist;
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ struct sockaddr_dl lsdl;
+ struct sockaddr_in6 lsin6;
+ struct rtentry *newrt=NULL;
+ int s;
+
+ if (ifp == mcastdefault)
+ return;
+
+ /*
+ * If NULL, nuke any mcast entry.
+ */
+
+ /*
+ * Find link addr for ifp.
+ */
+
+ while (ifa != NULL && ifa->ifa_addr->sa_family != AF_LINK)
+#ifdef __FreeBSD__
+ ifa = ifa->ifa_link.tqe_next;
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ ifa = ifa->ifa_list.tqe_next;
+#else /* __NetBSD__ || __OpenBSD__ */
+ ifa = ifa->ifa_next;
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+
+ if (ifa == NULL)
+ panic("Can't find AF_LINK for new multicast default interface.");
+
+ bcopy(ifa->ifa_addr,&lsdl,ifa->ifa_addr->sa_len);
+ DDO(IDL_EVENT,dump_smart_sockaddr((struct sockaddr *)&lsdl));
+ lsdl.sdl_alen = 0;
+ lsdl.sdl_slen = 0;
+ lsdl.sdl_nlen = 0;
+
+ /*
+ * Delete old route, and add new one.
+ */
+
+ bzero(&lsin6,sizeof(lsin6));
+ lsin6.sin6_family = AF_INET6;
+ lsin6.sin6_len = sizeof(lsin6);
+ lsin6.sin6_addr.s6_addr[0]=0xff;
+
+ /* Neat property, mask and value are identical! */
+
+ s = splnet();
+ rtrequest(RTM_DELETE,(struct sockaddr *)&lsin6,NULL,
+ (struct sockaddr *)&lsin6,0,NULL);
+ /*
+ *
+ * NB: If we clone, we have mcast dests being on a route.
+ * Consider multihomed system with processes talking to the
+ * same mcast group, but out different interfaces.
+ *
+ * Also, the RTM_ADD will do its best to find a "source address" to stick
+ * in the rt_ifa field. (See ipv6_rtrequest.c for this code.)
+ */
+ rtrequest(RTM_ADD,(struct sockaddr *)&lsin6,(struct sockaddr *)&lsdl,
+ (struct sockaddr *)&lsin6,0,&newrt);
+ if (newrt == NULL)
+ panic("Assigning default multicast if.");
+ newrt->rt_rmx.rmx_mtu = ifp->if_mtu;
+ newrt->rt_refcnt--;
+ mcastdefault = ifp;
+ splx(s);
+}
+
+/*----------------------------------------------------------------------
+ * Delete an "IPv6 interface". Only called inside splnet().
+ ----------------------------------------------------------------------*/
+
+void
+del_in6_ifnet(ifp)
+ struct ifnet *ifp;
+{
+ struct in6_ifnet *i6ifp,*prev = NULL;
+
+ for (i6ifp = in6_ifnet; i6ifp != NULL; i6ifp = i6ifp->i6ifp_next)
+ {
+ if (i6ifp->i6ifp_ifp == ifp)
+ break;
+ prev = i6ifp;
+ }
+
+ if (i6ifp == NULL)
+ panic("Ooooh boy, consistency mismatch in del_in6_ifnet!");
+
+ if (--(i6ifp->i6ifp_numaddrs) == 0)
+ {
+ while (i6ifp->i6ifp_multiaddrs != NULL)
+ {
+ i6ifp->i6ifp_multiaddrs->in6m_refcount = 1;
+ in6_delmulti(i6ifp->i6ifp_multiaddrs);
+ }
+ if (prev == NULL)
+ in6_ifnet = i6ifp->i6ifp_next;
+ else prev->i6ifp_next = i6ifp->i6ifp_next;
+ free(i6ifp,M_I6IFP);
+ }
+}
+
+/*----------------------------------------------------------------------
+ * Add a new "IPv6 interface". Only called inside splnet().
+ * Perhaps send router adverts when this gets called. For now, they
+ * are issued when duplicate address detection succeeds on link-locals.
+ * See ipv6_addrconf.c for details.
+ ----------------------------------------------------------------------*/
+
+struct in6_ifnet *
+add_in6_ifnet(ifp, new)
+ struct ifnet *ifp; /* Assume an in6_ifaddr with this ifp is already
+ allocated and linked into the master list. */
+ int *new; /* XXX */
+{
+ struct in6_ifnet *i6ifp;
+
+ *new = 0;
+ for (i6ifp = in6_ifnet; i6ifp != NULL; i6ifp = i6ifp->i6ifp_next)
+ if (i6ifp->i6ifp_ifp == ifp)
+ break;
+
+ if (i6ifp == NULL)
+ {
+ i6ifp = malloc(sizeof(*i6ifp),M_I6IFP,M_NOWAIT);
+ if (i6ifp == NULL)
+ {
+ printf("DANGER! Malloc for i6ifp failed.\n");
+ return NULL;
+ }
+ i6ifp->i6ifp_ifp = ifp;
+ i6ifp->i6ifp_multiaddrs = NULL;
+ i6ifp->i6ifp_numaddrs = 1;
+ /* Other inits... */
+ i6ifp->i6ifp_next = in6_ifnet;
+ in6_ifnet = i6ifp;
+ *new = 1;
+ }
+
+ return i6ifp;
+}
+
+/*----------------------------------------------------------------------
+ * This function is called by the PRU_CONTROL handlers in both TCP and UDP.
+ * (Actually raw_ipv6 might need a PRU_CONTROL handler, but raw_ip doesn't
+ * have one.)
+ ----------------------------------------------------------------------*/
+
+int
+#if __NetBSD__ || __FreeBSD__
+in6_control(so, cmd, data, ifp, internal, p)
+#else /* __NetBSD__ || __FreeBSD__ */
+in6_control(so, cmd, data, ifp, internal)
+#endif /* __NetBSD__ || __FreeBSD__ */
+ struct socket *so;
+#if __NetBSD__
+ u_long cmd;
+#else /* __NetBSD__ */
+ int cmd;
+#endif /* __NetBSD__ */
+ caddr_t data;
+ register struct ifnet *ifp;
+ int internal;
+#if __NetBSD__ || __FreeBSD__
+ struct proc *p;
+#endif /* __NetBSD__ || __FreeBSD__ */
+{
+ register struct inet6_ifreq *ifr = (struct inet6_ifreq *)data;
+ register struct in6_ifaddr *i6a = 0;
+ struct in6_ifaddr *oi6a;
+ struct inet6_aliasreq *ifra = (struct inet6_aliasreq *)data;
+ struct sockaddr_in6 oldaddr;
+ int error, hostIsNew, maskIsNew, ifnetIsNew = 0;
+#if !__NetBSD__ && !__OpenBSD__ && !__FreeBSD__
+ struct ifaddr *ifa;
+#endif /* !__NetBSD__ && !__OpenBSD__ && !__FreeBSD__ */
+
+ /*
+ * If given an interface, find first IPv6 address on that interface.
+ * I may want to change how this is searched. I also may want to
+ * discriminate between link-local, site-local, v4-compatible, etc.
+ *
+ * This is used by the SIOCGIFADDR_INET6, and other such things.
+ * Those ioctls() currently assume only one IPv6 address on an interface.
+ * This is not a good assumption, and this code will have to be modified
+ * to correct that assumption.
+ */
+ if (ifp)
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ if (i6a->i6a_ifp == ifp)
+ break;
+
+ switch (cmd)
+ {
+ case SIOCAIFADDR_INET6:
+ case SIOCDIFADDR_INET6:
+ case SIOCVIFADDR_INET6:
+ /*
+ * For adding and deleting an address, find an exact match for
+ * that address. Note that ifr_addr and ifra_addr are in the same
+ * place, so even though VIFADDR uses a different struct than AIFADDR,
+ * the match will still occur.
+ */
+ if (ifra->ifra_addr.sin6_family == AF_INET6 &&
+ (cmd != SIOCDIFADDR_INET6 ||
+ !IN6_IS_ADDR_UNSPECIFIED(&ifra->ifra_addr.sin6_addr)))
+ for (oi6a = i6a; i6a; i6a = i6a->i6a_next)
+ {
+ if (i6a->i6a_ifp == ifp &&
+ IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &ifra->ifra_addr.sin6_addr))
+ break; /* Out of for loop. */
+ }
+
+ /*
+ * You can't delete what you don't have...
+ */
+ if (cmd == SIOCDIFADDR_INET6 && i6a == 0)
+ return EADDRNOTAVAIL;
+
+ /*
+ * User program requests verification of address. No harm done in
+ * letting ANY program use this ioctl(), so we put code in for it
+ * here.
+ *
+ * If I found the i6a, check if I'm not sure. Return EWOULDBLOCK if
+ * not sure, return 0 if sure. Return EADDRNOTAVAIL if not available
+ * (i.e. DAD failed.).
+ */
+ if (cmd == SIOCVIFADDR_INET6)
+ if (i6a == NULL)
+ return EADDRNOTAVAIL;
+ else if (i6a->i6a_addrflags & I6AF_NOTSURE)
+ return EWOULDBLOCK;
+ else return 0;
+
+ /* FALLTHROUGH TO... */
+
+ case SIOCSIFDSTADDR_INET6:
+#if __NetBSD__ || __FreeBSD__
+ if (p == 0 || (error = suser(p->p_ucred, &p->p_acflag)) )
+#else /* __NetBSD__ || __FreeBSD__ */
+ if ((so->so_state & SS_PRIV) == 0)
+#endif /* __NetBSD__ || __FreeBSD__ */
+ return EPERM;
+
+ if (ifp==0)
+ panic("in6_control, ifp==0");
+ if (i6a == NULL)
+ {
+ struct in6_ifaddr *tmp;
+
+ /*
+ * Create new in6_ifaddr (IPv6 interface address) for additions
+ * and destination settings.
+ */
+ if (!(tmp = (struct in6_ifaddr *)malloc(sizeof(struct in6_ifaddr),
+ M_IFADDR,M_NOWAIT)))
+ {
+ return ENOBUFS;
+ }
+
+ bzero(tmp,sizeof(struct in6_ifaddr));
+ /*
+ * Set NOTSURE addrflag before putting in list.
+ */
+ tmp->i6a_addrflags = I6AF_NOTSURE;
+ if ((i6a = in6_ifaddr))
+ {
+ for (; i6a->i6a_next; i6a=i6a->i6a_next)
+ ;
+ i6a->i6a_next = tmp;
+ }
+ else in6_ifaddr = tmp;
+ i6a = tmp;
+#ifdef __FreeBSD__
+ TAILQ_INSERT_TAIL(&ifp->if_addrhead, (struct ifaddr *)i6a,
+ ifa_link);
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)i6a,
+ ifa_list);
+#else /* __NetBSD__ || __OpenBSD__ */
+ if (ifa = ifp->if_addrlist)
+ {
+ for (; ifa->ifa_next; ifa=ifa->ifa_next)
+ ;
+ ifa->ifa_next = (struct ifaddr *)i6a;
+ }
+ else ifp->if_addrlist = (struct ifaddr *)i6a;
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ i6a->i6a_ifa.ifa_addr = (struct sockaddr *)&i6a->i6a_addr;
+ i6a->i6a_ifa.ifa_dstaddr = (struct sockaddr *)&i6a->i6a_dstaddr;
+ i6a->i6a_ifa.ifa_netmask
+ = (struct sockaddr *)&i6a->i6a_sockmask;
+ i6a->i6a_sockmask.sin6_len = sizeof(struct sockaddr_in6);
+ i6a->i6a_ifp = ifp;
+
+ /*
+ * Add address to IPv6 interface lists.
+ */
+ i6a->i6a_i6ifp = add_in6_ifnet(ifp, &ifnetIsNew);
+ }
+ break;
+ case SIOCGIFADDR_INET6:
+ case SIOCGIFNETMASK_INET6:
+ case SIOCGIFDSTADDR_INET6:
+ /*
+ * Can't get information on what is not there...
+ */
+ if (i6a == NULL)
+ return EADDRNOTAVAIL;
+ break;
+
+ default:
+ return EOPNOTSUPP;
+ }
+
+ switch (cmd)
+ {
+ /*
+ * The following three cases assume that there is only one address per
+ * interface; this is not good in IPv6-land. Unfortunately, the
+ * ioctl() interface, is such that I'll have to rewrite the way things
+ * work here, either that, or curious user programs will have to troll
+ * /dev/kmem (like netstat(8) does).
+ */
+ case SIOCGIFADDR_INET6:
+ bcopy(&(i6a->i6a_addr),&(ifr->ifr_addr),sizeof(struct sockaddr_in6));
+ break;
+
+ case SIOCGIFDSTADDR_INET6:
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ return EINVAL;
+ bcopy(&(i6a->i6a_dstaddr),&(ifr->ifr_dstaddr),
+ sizeof(struct sockaddr_in6));
+ break;
+
+ case SIOCGIFNETMASK_INET6:
+ bcopy(&(i6a->i6a_sockmask),&(ifr->ifr_addr),sizeof(struct sockaddr_in6));
+ break;
+
+ case SIOCSIFDSTADDR_INET6:
+ i6a->i6a_addrflags &= ~I6AF_NOTSURE;
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ return EINVAL;
+ oldaddr = i6a->i6a_dstaddr;
+ i6a->i6a_dstaddr = *(struct sockaddr_in6 *)&ifr->ifr_dstaddr;
+ if (ifp->if_ioctl && (error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR,
+ (caddr_t)i6a)))
+ {
+ i6a->i6a_dstaddr = oldaddr;
+ return error;
+ }
+ if (i6a->i6a_flags & IFA_ROUTE)
+ {
+ i6a->i6a_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr;
+ rtinit(&(i6a->i6a_ifa), RTM_DELETE, RTF_HOST);
+ i6a->i6a_ifa.ifa_dstaddr = (struct sockaddr *)&i6a->i6a_dstaddr;
+ rtinit(&(i6a->i6a_ifa), RTM_ADD, RTF_HOST|RTF_UP);
+ }
+ break;
+
+ /*
+ * For adding new IPv6 addresses to an interface, I stuck to the way
+ * that IPv4 uses, pretty much.
+ */
+ case SIOCAIFADDR_INET6:
+ maskIsNew = 0;
+ hostIsNew = 1;
+ error = 0;
+ if (i6a->i6a_addr.sin6_family == AF_INET6)
+ if (ifra->ifra_addr.sin6_len == 0)
+ {
+ bcopy(&(i6a->i6a_addr),&(ifra->ifra_addr),
+ sizeof(struct sockaddr_in6));
+ hostIsNew = 0;
+ }
+ else if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr, &i6a->i6a_addr.sin6_addr))
+ hostIsNew = 0;
+
+ if (ifra->ifra_mask.sin6_len)
+ {
+ in6_ifscrub(ifp,i6a);
+ bcopy(&(ifra->ifra_mask),&(i6a->i6a_sockmask),
+ sizeof(struct sockaddr_in6));
+ maskIsNew = 1;
+ }
+
+ if ((ifp->if_flags & IFF_POINTOPOINT) &&
+ (ifra->ifra_dstaddr.sin6_family == AF_INET6))
+ {
+ in6_ifscrub(ifp,i6a);
+ bcopy(&(ifra->ifra_dstaddr),&(i6a->i6a_dstaddr),
+ sizeof(struct sockaddr_in6));
+ maskIsNew = 1; /* We lie, simply so that in6_ifinit() will be
+ called to initialize the peer's address. */
+ }
+ if (ifra->ifra_addr.sin6_family == AF_INET6 && (hostIsNew || maskIsNew))
+ error = in6_ifinit(ifp,i6a,&ifra->ifra_addr,0,!internal);
+ /* else i6a->i6a_addrflags &= ~I6AF_NOTSURE; */
+
+ if (error == EEXIST) /* XXX, if route exists, we should be ok */
+ error = 0;
+
+ if (hostIsNew && !ifnetIsNew /* && (!error || error == EEXIST) */)
+ {
+ if (i6a->i6a_i6ifp)
+ i6a->i6a_i6ifp->i6ifp_numaddrs++;
+ else
+ panic("in6_control: missing i6ifp");
+ }
+ return error;
+
+ case SIOCDIFADDR_INET6:
+ in6_ifscrub(ifp, i6a);
+ /*
+ * If last address on this interface, delete IPv6 interface record.
+ */
+ del_in6_ifnet(ifp);
+
+#ifdef __FreeBSD__
+ TAILQ_REMOVE(&ifp->if_addrhead, (struct ifaddr *)i6a, ifa_link);
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)i6a, ifa_list);
+#else /* __NetBSD__ || __OpenBSD__ */
+ if ((ifa = ifp->if_addrlist) == (struct ifaddr *)i6a)
+ ifp->if_addrlist = ifa->ifa_next;
+ else
+ {
+ while (ifa->ifa_next &&
+ (ifa->ifa_next != (struct ifaddr *)i6a))
+ ifa=ifa->ifa_next;
+ if (ifa->ifa_next)
+ ifa->ifa_next = i6a->i6a_ifa.ifa_next;
+ else
+ DPRINTF(IDL_ERROR, ("Couldn't unlink in6_ifaddr from ifp!\n"));
+ }
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ oi6a = i6a;
+ if (oi6a == (i6a = in6_ifaddr))
+ in6_ifaddr = i6a->i6a_next;
+ else
+ {
+ while (i6a->i6a_next && (i6a->i6a_next != oi6a))
+ i6a = i6a->i6a_next;
+ if (i6a->i6a_next)
+ i6a->i6a_next = oi6a->i6a_next;
+ else
+ DPRINTF(IDL_ERROR, ("Didn't unlink in6_ifaddr from list.\n"));
+ }
+ IFAFREE((&oi6a->i6a_ifa)); /* For the benefit of routes pointing
+ to this ifa. */
+ break;
+
+ default:
+ DPRINTF(IDL_ERROR,
+ ("in6_control(): Default case not implemented.\n"));
+ return EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ * in6_ifscrub:
+ * Delete any existing route for an IPv6 interface.
+ ----------------------------------------------------------------------*/
+
+int
+in6_ifscrub(ifp,i6a)
+ register struct ifnet *ifp;
+ register struct in6_ifaddr *i6a;
+{
+ if (!(i6a->i6a_flags & IFA_ROUTE))
+ return 1;
+
+ if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT))
+ rtinit(&(i6a->i6a_ifa), (int)RTM_DELETE, RTF_HOST);
+ else
+ rtinit(&(i6a->i6a_ifa), (int)RTM_DELETE, 0);
+ i6a->i6a_flags &= ~IFA_ROUTE;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ * Initialize an IPv6 address for an interface.
+ *
+ * When I get around to doing duplicate address detection, this is probably
+ * the place to do it.
+ ----------------------------------------------------------------------*/
+
+int
+in6_ifinit(ifp, i6a, sin6, scrub, useDAD)
+ register struct ifnet *ifp;
+ register struct in6_ifaddr *i6a;
+ struct sockaddr_in6 *sin6;
+ int scrub;
+ int useDAD;
+{
+ int s, error, flags = RTF_UP;
+ struct sockaddr_in6 oldaddr;
+
+ DPRINTF(IDL_EVENT,("Before splimp in in6_ifinit()\n"));
+ s = splimp();
+
+ bcopy(&(i6a->i6a_addr),&oldaddr,sizeof(struct sockaddr_in6));
+ bcopy(sin6,&(i6a->i6a_addr),sizeof(struct sockaddr_in6));
+
+ /*
+ * 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)i6a)))
+ {
+ bcopy(&oldaddr,&(i6a->i6a_addr),sizeof(struct sockaddr_in6));
+ splx(s);
+ return error;
+ }
+
+ /*
+ * IPv4 in 4.4BSD sets the RTF_CLONING flag here if it's an Ethernet.
+ * I delay this until later.
+ */
+
+ splx(s);
+ DPRINTF(IDL_EVENT,("After splx() in in6_ifinit().\n"));
+
+ sin6->sin6_port = 0;
+
+ if (scrub)
+ {
+ i6a->i6a_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
+ in6_ifscrub(ifp, i6a);
+ i6a->i6a_ifa.ifa_addr = (struct sockaddr *)&i6a->i6a_addr;
+ }
+
+ /*
+ * Adjust the sin6_len such that it only counts mask bytes with
+ * 1's in them.
+ */
+
+ {
+ register char *cpbase = (char *)&(i6a->i6a_sockmask.sin6_addr);
+ register char *cp = cpbase + sizeof(struct in6_addr);
+
+ i6a->i6a_sockmask.sin6_len = 0;
+ while (--cp >=cpbase)
+ if (*cp)
+ {
+ i6a->i6a_sockmask.sin6_len = 1 + cp - (char *)&(i6a->i6a_sockmask);
+ break;
+ }
+ }
+
+ /*
+ * Add route. Also, set some properties of the interface address here.
+ * (Properties include permanance, lifetime, etc.)
+ */
+
+ i6a->i6a_ifa.ifa_metric = ifp->if_metric;
+ i6a->i6a_ifa.ifa_rtrequest = ipv6_rtrequest; /* Want this to be true
+ for ALL IPv6 ifaddrs. */
+ if (ifp->if_flags & IFF_LOOPBACK)
+ {
+ useDAD = 0;
+ i6a->i6a_ifa.ifa_dstaddr = i6a->i6a_ifa.ifa_addr;
+ flags |= RTF_HOST;
+
+ /* Loopback is definitely a permanent address. */
+ if (IN6_IS_ADDR_LOOPBACK(&i6a->i6a_addr.sin6_addr))
+ i6a->i6a_addrflags |= I6AF_PERMANENT;
+ }
+ else if (ifp->if_flags & IFF_POINTOPOINT)
+ {
+ useDAD = 0; /* ??!!?? */
+ if (i6a->i6a_dstaddr.sin6_family != AF_INET6)
+ return 0;
+
+ flags |= RTF_HOST;
+ }
+ else
+ {
+ /*
+ * No b-cast in IPv6, therefore the ifa_broadaddr (concidentally the
+ * dest address filled in above...) should be set to NULL!
+ */
+ i6a->i6a_ifa.ifa_broadaddr = NULL;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&i6a->i6a_addr.sin6_addr))
+ {
+ flags |= RTF_HOST;
+ i6a->i6a_ifa.ifa_dstaddr = i6a->i6a_ifa.ifa_addr;
+
+ /*
+ * Possibly do other stuff specific to link-local addresses, hence
+ * keeping this separate from IFF_LOOPBACK case above. I may move
+ * the link-local check to || with IFF_LOOPBACK.
+ *
+ * Other stuff includes setting i6a_preflen so when addrconf
+ * needs to know what part of the link-local is used for uniqueness,
+ * it doesn't have to gyrate.
+ */
+ switch(i6a->i6a_ifp->if_type)
+ {
+ case IFT_ETHER:
+ i6a->i6a_preflen = 64;
+ break;
+ default:
+ DPRINTF(IDL_ERROR,("Can't set i6a_preflen for type %d.\n",\
+ i6a->i6a_ifp->if_type));
+ break;
+ }
+
+ i6a->i6a_addrflags |= (I6AF_LINKLOC | I6AF_PERMANENT);
+ }
+ else
+ {
+ if (!(i6a->i6a_sockmask.sin6_len == sizeof(struct sockaddr_in6) &&
+ IN6_ARE_ADDR_EQUAL(&i6a->i6a_sockmask.sin6_addr, &in6_allones.sin6_addr)))
+ flags |= RTF_CLONING; /* IMHO, ALL network routes
+ have the cloning bit set for next-hop
+ resolution if they aren't loopback or
+ pt. to pt. */
+ i6a->i6a_addrflags |= I6AF_PREFIX; /* I'm a 'prefix list entry'. */
+ }
+ }
+
+ if ((error = rtinit(&(i6a->i6a_ifa), RTM_ADD,flags)) == 0)
+ {
+ i6a->i6a_flags |= IFA_ROUTE;
+ }
+
+ /*
+ * If the interface supports multicast, join the appropriate
+ * multicast groups (all {nodes, routers}) on that interface.
+ *
+ * Also join the solicited nodes discovery multicast group for that
+ * destination.
+ */
+ if (ifp->if_flags & IFF_MULTICAST)
+ {
+ struct in6_addr addr;
+ struct in6_multi *rc;
+
+ /* NOTE2: Set default multicast interface here.
+ Set up cloning route for ff00::0/8 */
+ if (ifp->if_type != IFT_LOOP && mcastdefault == NULL)
+ setmcastdef(ifp);
+
+ /* All-nodes. */
+ SET_IN6_ALLNODES(addr);
+ SET_IN6_MCASTSCOPE(addr,IN6_INTRA_LINK);
+ rc = in6_addmulti(&addr, ifp);
+
+ /* All-routers, if forwarding */
+ if (ipv6forwarding) {
+ SET_IN6_ALLROUTERS(addr);
+ SET_IN6_MCASTSCOPE(addr, IN6_INTRA_LINK);
+ rc = in6_addmulti(&addr, ifp);
+ };
+
+ /* Solicited-nodes. */
+ addr.in6a_words[0] = htonl(0xff020000);
+ addr.in6a_words[1] = 0;
+ addr.in6a_words[2] = htonl(1);
+ addr.in6a_words[3] = i6a->i6a_addr.sin6_addr.in6a_words[3] | htonl(0xff000000);
+
+ DDO(IDL_EVENT, dump_in6_addr(&addr));
+
+ rc=in6_addmulti(&addr, ifp);
+ }
+
+ if (useDAD /*&& error != 0*/)
+ addrconf_dad(i6a);
+ else
+ i6a->i6a_addrflags &= ~I6AF_NOTSURE;
+
+ return error;
+}
+
+/*----------------------------------------------------------------------
+ * Add IPv6 multicast address. IPv6 multicast addresses are handled
+ * pretty much like IP multicast addresses for now.
+ *
+ * Multicast addresses hang off in6_ifaddr's. Eventually, they should hang
+ * off the link-local multicast address, this way, there are no ambiguities.
+ ----------------------------------------------------------------------*/
+
+struct in6_multi *in6_addmulti(addr,ifp)
+ register struct in6_addr *addr;
+ struct ifnet *ifp;
+
+{
+ register struct in6_multi *in6m;
+ struct inet6_ifreq ifr;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ifr.ifr_addr;
+ struct in6_ifnet *i6ifp;
+ int s = splnet();
+
+ /*
+ * See if address is already in list..
+ */
+
+ IN6_LOOKUP_MULTI(addr,ifp,in6m);
+
+ if (in6m != NULL)
+ {
+ /* Increment the reference count. */
+ in6m->in6m_refcount++;
+ }
+ else
+ {
+#if __FreeBSD__
+ struct ifmultiaddr *ifma;
+#endif /* __FreeBSD__ */
+ /*
+ * Otherwise, allocate a new m-cast record and link it to
+ * the interface's multicast list.
+ */
+
+ if ((in6m=malloc(sizeof(struct in6_multi),M_IPMADDR,M_NOWAIT)) == NULL)
+ {
+ splx(s);
+ return NULL;
+ }
+ bzero(in6m,sizeof(struct in6_multi));
+ in6m->in6m_addr = *addr;
+ in6m->in6m_refcount = 1;
+ in6m->in6m_ifp = ifp;
+
+ for(i6ifp = in6_ifnet; i6ifp != NULL && i6ifp->i6ifp_ifp != ifp;
+ i6ifp = i6ifp->i6ifp_next)
+ ;
+ if (i6ifp == NULL)
+ {
+ free(in6m,M_IPMADDR);
+ splx(s);
+ return NULL;
+ }
+ in6m->in6m_i6ifp = i6ifp;
+ in6m->in6m_next = i6ifp->i6ifp_multiaddrs;
+ i6ifp->i6ifp_multiaddrs = in6m;
+
+ /*
+ * Ask the network driver to update its multicast reception
+ * filter appropriately for the new address.
+ */
+ sin6->sin6_family=AF_INET6;
+ sin6->sin6_len=sizeof(struct sockaddr_in6);
+ sin6->sin6_addr = *addr;
+ sin6->sin6_port = 0;
+ sin6->sin6_flowinfo = 0;
+
+
+#if __FreeBSD__
+ if (if_addmulti(ifp, (struct sockaddr *) sin6, &ifma))
+#else /* __FreeBSD */
+ if (ifp->if_ioctl == NULL ||
+ (*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0)
+#endif /* __FreeBSD__ */
+ {
+ i6ifp->i6ifp_multiaddrs = in6m->in6m_next;
+ free(in6m,M_IPMADDR);
+ splx(s);
+ return NULL;
+ }
+#ifdef __FreeBSD__
+ ifma->ifma_protospec = in6m;
+#endif /* __FreeBSD__ */
+
+ /* Tell IGMP that we've joined a new group. */
+ /*ipv6_igmp_joingroup(in6m);*/
+ }
+ splx(s);
+ return in6m;
+}
+
+/*----------------------------------------------------------------------
+ * Delete IPv6 multicast address.
+ ----------------------------------------------------------------------*/
+
+void
+in6_delmulti(in6m)
+ register struct in6_multi *in6m;
+{
+ register struct in6_multi **p;
+ struct inet6_ifreq ifr;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&(ifr.ifr_addr);
+ int s = splnet();
+
+ if (--in6m->in6m_refcount == 0)
+ {
+ /* Tell IGMP that I'm bailing this group. */
+ /* ipv6_igmp_leavegroup(in6m);*/
+
+ /* Unlink from list. */
+ for (p = &(in6m->in6m_i6ifp->i6ifp_multiaddrs);
+ *p != in6m;
+ p = &(*p)->in6m_next)
+ ;
+ *p = (*p)->in6m_next;
+
+ /*
+ * Notify the network driver to update its multicast reception
+ * filter.
+ */
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_port = 0;
+ sin6->sin6_flowinfo = 0;
+ sin6->sin6_addr = in6m->in6m_addr;
+ (*(in6m->in6m_ifp->if_ioctl))(in6m->in6m_ifp, SIOCDELMULTI,
+ (caddr_t)&ifr);
+
+ free(in6m,M_IPMADDR);
+ }
+ splx(s);
+}
diff --git a/sys/netinet6/in6_cksum.c b/sys/netinet6/in6_cksum.c
new file mode 100644
index 00000000000..0288b74daaf
--- /dev/null
+++ b/sys/netinet6/in6_cksum.c
@@ -0,0 +1,232 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+/*
+ * Copyright (c) 1988 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.
+ *
+ * (Originally from) @(#)in_cksum.c 7.3 (Berkeley) 6/28/90
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+
+#include <netinet/in.h>
+#include <netinet6/in6.h>
+#include <netinet6/ipv6.h>
+
+#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);}
+
+/*----------------------------------------------------------------------
+ * in6_cksum() takes an mbuf chain (with a IPv6 header at the beginning), and
+ * computes the checksum. This also assumes that the IPv6 header is pulled
+ * up into the first mbuf in the chain, and that said header has valid
+ * source and destination fields. I do the pseudo-header first, then
+ * I do the rest. Unlike v4, I treat the pseudo-header separately, so
+ * transports don't have to do funky gymnastics with zeroing out headers.
+ ----------------------------------------------------------------------*/
+
+int
+in6_cksum(m, proto, len, start)
+ struct mbuf *m; /* Chain, complete with IPv6 header. */
+ int proto; /* Protocol number of HLP that needs sum. */
+ uint len; /* Length of stuff to checksum. Note that len
+ and proto are what get computed in the pseudo-
+ header. Len is a uint because of potential
+ jumbograms. */
+ uint start; /* How far (in bytes) into chain's data to
+ start remainder of computation. (e.g. Where
+ the TCP segment begins. */
+{
+ u_short *w;
+ int sum = 0;
+ int mlen = 0;
+ int byte_swapped = 0;
+ struct ipv6 *header;
+ short done = 0;
+ union
+ {
+ uint8_t c[2];
+ uint16_t s;
+ } s_util;
+ union
+ {
+ uint16_t s[2];
+ uint32_t l;
+ } l_util;
+
+ /*
+ * Get pseudo-header summed up. Assume ipv6 is first.
+ * I do pseudo-header here because it'll save bletch in both TCP and
+ * UDP.
+ */
+
+ header = mtod(m,struct ipv6 *);
+
+ w = (u_short *)&(header->ipv6_src);
+ /* Source address */
+ sum+=w[0]; sum+=w[1]; sum+=w[2]; sum+=w[3];
+ sum+=w[4]; sum+=w[5]; sum+=w[6]; sum+=w[7];
+
+ /* Destination address */
+ sum+=w[8]; sum+=w[9]; sum+=w[10]; sum+=w[11];
+ sum+=w[12]; sum+=w[13]; sum+=w[14]; sum+=w[15];
+
+ /* Next header value for transport layer. */
+ sum += htons(proto);
+
+ /* Length of transport header and transport data. */
+ l_util.l = htonl(len);
+ sum+=l_util.s[0];
+ sum+=l_util.s[1];
+
+ /* Find starting point for rest of data.. */
+
+ while (!done)
+ if (m->m_len >start)
+ {
+ done = 1;
+ mlen = m->m_len - start;
+ }
+ else
+ {
+ start -= m->m_len;
+ m = m->m_next;
+ }
+
+ for (;m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+
+ w = (u_short *)(m->m_data + (done ? start : 0));
+
+ 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_short *)((char *)w + 1);
+ mlen = m->m_len - 1;
+ len--;
+ }
+ else
+ if (!done)
+ mlen = m->m_len;
+ else done=0;
+
+ if (len < mlen)
+ mlen = len;
+ len -= mlen;
+
+ /*
+ * Force to even boundary.
+ */
+#ifdef __alpha__
+ if ((1 & (long) w) && (mlen > 0)) {
+#else
+ if ((1 & (int) w) && (mlen > 0)) {
+#endif
+ REDUCE;
+ sum <<= 8;
+ s_util.c[0] = *(u_char *)w;
+ w = (u_short *)((char *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ /*
+ * Unroll the loop to make overhead from
+ * branches 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)
+ printf("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_pcb.c b/sys/netinet6/in6_pcb.c
new file mode 100644
index 00000000000..ec2a27b665e
--- /dev/null
+++ b/sys/netinet6/in6_pcb.c
@@ -0,0 +1,1426 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+/*
+ * Copyright (c) 1982, 1986, 1990, 1993, 1995
+ * 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.
+ *
+ */
+
+#if __FreeBSD__
+#include <sys/queue.h>
+#endif /* __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/socketvar.h>
+/*#include <sys/ioctl.h>*/
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+
+#include <netinet6/in6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ipv6.h>
+#include <netinet6/ipv6_var.h>
+
+#if __OpenBSD__ && defined(NRL_IPSEC)
+#define IPSEC 1
+#endif /* __OpenBSD__ && defined(NRL_IPSEC) */
+
+#ifdef IPSEC
+#include <sys/osdep.h>
+#include <net/netproc.h>
+#include <net/netproc_var.h>
+#endif /* IPSEC */
+
+#include <sys/debug.h>
+
+#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
+#include <machine/pcpu.h>
+#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+
+/*
+ * External globals
+ */
+
+#if __OpenBSD__
+#include <dev/rndvar.h>
+#endif /* __OpenBSD__ */
+
+#if __FreeBSD__
+/*
+ * These configure the range of local port addresses assigned to
+ * "unspecified" outgoing connections/packets/whatever.
+ */
+static int ipport_lowfirstauto = IPPORT_RESERVED - 1; /* 1023 */
+static int ipport_lowlastauto = IPPORT_RESERVEDSTART; /* 600 */
+static int ipport_firstauto = IPPORT_RESERVED; /* 1024 */
+static int ipport_lastauto = IPPORT_USERRESERVED; /* 5000 */
+static int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 49152 */
+static int ipport_hilastauto = IPPORT_HILASTAUTO; /* 65535 */
+extern void in_rtchange(struct inpcb *, int);
+#endif /* __FreeBSD__ */
+
+extern struct in6_ifaddr *in6_ifaddr;
+#if __FreeBSD__
+int in6_pcbladdr(register struct inpcb *, struct sockaddr *, struct sockaddr **);
+extern TAILQ_HEAD(in_ifaddrhead, in_ifaddr) in_ifaddrhead;
+#else /* __FreeBSD__ */
+extern struct in_ifaddr *in_ifaddr;
+#endif /* __FreeBSD__ */
+/*
+ * Globals
+ */
+
+struct in6_addr zeroin6_addr;
+
+#if __OpenBSD__
+extern int ipport_firstauto;
+extern int ipport_lastauto;
+extern int ipport_hifirstauto;
+extern int ipport_hilastauto;
+#endif /* __OpenBSD__ */
+
+#if __OpenBSD__
+int in_baddynamic __P((u_int16_t, u_int16_t));
+#endif /* __OpenBSD__ */
+
+/*
+ * Keep separate inet6ctlerrmap, because I may remap some of these.
+ * I also put it here, because, quite frankly, it belongs here, not in
+ * ip{v6,}_input().
+ */
+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
+};
+
+/*----------------------------------------------------------------------
+ * Bind an address (or at least a port) to an PF_INET6 socket.
+ ----------------------------------------------------------------------*/
+int
+in6_pcbbind(inp, nam)
+ register struct inpcb *inp;
+#if __FreeBSD__
+ struct sockaddr *nam;
+#else /* __FreeBSD__ */
+ struct mbuf *nam;
+#endif /* __FreeBSD__ */
+{
+ register struct socket *so = inp->inp_socket;
+
+#if __NetBSD__ || __OpenBSD__
+ register struct inpcbtable *head = inp->inp_table;
+#else /* __OpenBSD__ */
+#ifndef __FreeBSD__
+ register struct inpcb *head = inp->inp_head;
+#else /* __FreeBSD__ */
+ struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+#endif /* __FreeBSD__ */
+#endif /* __NetBSD__ || __OpenBSD__ */
+ register struct sockaddr_in6 *sin6;
+#if !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)
+ struct proc *p = curproc; /* XXX */
+#else /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
+ struct proc *p = PCPU(curproc); /* XXX */
+#endif /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
+ u_short lport = 0;
+ int wild = INPLOOKUP_IPV6, reuseport = (so->so_options & SO_REUSEPORT);
+ int error;
+
+ /*
+ * REMINDER: Once up to speed, flow label processing should go here,
+ * too. (Same with in6_pcbconnect.)
+ */
+
+#if __FreeBSD__
+ if (in6_ifaddr == 0 || in_ifaddrhead.tqh_first == 0)
+ return EADDRNOTAVAIL;
+#else /* __FreeBSD__ */
+ if (in6_ifaddr == 0 || in_ifaddr == 0)
+ return EADDRNOTAVAIL;
+#endif/* __FreeBSD__ */
+ if (inp->inp_lport != 0 || !IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6))
+ return EINVAL; /* If already bound, EINVAL! */
+
+ if ((so->so_options & (SO_REUSEADDR | SO_REUSEPORT)) == 0 &&
+ ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
+ (so->so_options & SO_ACCEPTCONN) == 0))
+ wild |= INPLOOKUP_WILDCARD;
+
+ /*
+ * If I did get a sockaddr passed in...
+ */
+ if (nam)
+ {
+#if __FreeBSD__
+ sin6 = (struct sockaddr_in6 *)nam;
+ if (nam->sa_len != sizeof (*sin6))
+ return EINVAL;
+#else /* __FreeBSD__ */
+ sin6 = mtod(nam, struct sockaddr_in6 *);
+ if (nam->m_len != sizeof (*sin6))
+ return EINVAL;
+#endif /* __FreeBSD__ */
+
+ /*
+ * Unlike v4, I have no qualms about EAFNOSUPPORT if the
+ * wretched family is not filled in!
+ */
+ if (sin6->sin6_family != AF_INET6)
+ return EAFNOSUPPORT;
+ lport = sin6->sin6_port;
+
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
+ {
+ /*
+ * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
+ * allow complete 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 sockaddr_in sin;
+
+ sin.sin_port = 0;
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = sin6->sin6_addr.in6a_words[3];
+ bzero(&sin.sin_zero,8);
+
+ sin6->sin6_port = 0; /* Yechhhh, because of upcoming call to
+ ifa_ifwithaddr(), which does bcmp's
+ over the PORTS as well. (What about flow?)
+ */
+ sin6->sin6_flowinfo = 0;
+ if (ifa_ifwithaddr((struct sockaddr *)sin6) == 0)
+ if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ||
+ ifa_ifwithaddr((struct sockaddr *)&sin) == 0)
+ return EADDRNOTAVAIL;
+ }
+ if (lport)
+ {
+ struct inpcb *t;
+ struct in_addr fa,la;
+
+ /* Question: Do we wish to continue the Berkeley tradition of
+ ports < IPPORT_RESERVED be only for root?
+
+ Answer: For now yes, but IMHO, it should be REMOVED!
+
+ OUCH: One other thing, is there no better way of finding
+ a process for a socket instead of using curproc? (Marked
+ with BSD's {in,}famous XXX ? */
+ if (ntohs(lport) < IPPORT_RESERVED &&
+ (error = suser(p->p_ucred, &p->p_acflag)))
+ return error;
+
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
+ {
+ fa.s_addr = 0;
+ la.s_addr = sin6->sin6_addr.in6a_words[3];
+ wild &= ~INPLOOKUP_IPV6;
+
+#if __FreeBSD__
+ t = in_pcblookup_local(inp->inp_pcbinfo,
+ (struct in_addr *)&la, lport, wild);
+#else /* __FreeBSD__ */
+#if __NetBSD__
+ t = in_pcblookup_port(inp->inp_table, (struct in_addr *)&la,
+ lport, wild);
+#else /* __NetBSD__ */
+ t = in_pcblookup(head, (struct in_addr *)&fa, 0,
+ (struct in_addr *)&la, lport, wild);
+#endif /* __NetBSD__ */
+#endif /* __FreeBSD__ */
+
+ }
+ else
+ {
+#if __FreeBSD__
+ t = in_pcblookup_local(inp->inp_pcbinfo,
+ (struct in_addr *)&sin6->sin6_addr, lport, wild);
+#else /* __FreeBSD__ */
+#if __NetBSD__
+ t = in_pcblookup_port(inp->inp_table,
+ (struct in_addr *)&in6addr_any, lport, wild);
+#else /* __NetBSD__ */
+ t = in_pcblookup(head, (struct in_addr *)&zeroin6_addr, 0,
+ (struct in_addr *)&sin6->sin6_addr, lport, wild);
+#endif /* __NetBSD__ */
+#endif /* __FreeBSD__ */
+ }
+
+ if (t && (reuseport & t->inp_socket->so_options) == 0)
+ return EADDRINUSE;
+ }
+ inp->inp_laddr6 = sin6->sin6_addr;
+
+ if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
+ inp->inp_ipv6.ipv6_versfl = htonl(0x60000000) |
+ (sin6->sin6_flowinfo & htonl(0x0fffffff));
+
+ /*
+ * Unroll first 2 compares of {UNSPEC,V4MAPPED}.
+ * Mark PF_INET6 socket as undecided (bound to port-only) or
+ * mapped (INET6 socket talking IPv4) here. I may need to move
+ * this code out of this if (nam) clause, and put it just before
+ * function return.
+ *
+ * Then again, the only time this function is called with NULL nam
+ * might be during a *_pcbconnect(), which then sets the local address
+ * ANYWAY.
+ */
+ if (inp->inp_laddr6.in6a_words[0] == 0 &&
+ inp->inp_laddr6.in6a_words[1] == 0)
+ {
+ if (inp->inp_laddr6.in6a_words[2] == ntohl(0xffff))
+ inp->inp_flags |= INP_IPV6_MAPPED;
+ if (inp->inp_laddr6.in6a_words[2] == 0 &&
+ inp->inp_laddr6.in6a_words[3] == 0)
+ inp->inp_flags |= INP_IPV6_UNDEC;
+ }
+ }
+
+ if (lport == 0) {
+ /* This code block was derived from OpenBSD */
+ uint16_t first, last, old = 0;
+ int count;
+ int loopcount = 0;
+ struct in_addr fa, la;
+ u_int16_t *lastport;
+#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
+ u_int16_t last_port = head->inp_lport;
+
+ lastport = &last_port;
+#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+#if __NetBSD__ || __OpenBSD__
+ lastport = &inp->inp_table->inpt_lastport;
+#endif /* __NetBSD__ || __OpenBSD__ */
+
+ if (inp->inp_flags & INP_IPV6_MAPPED) {
+ la.s_addr = inp->inp_laddr6.in6a_words[3];
+ fa.s_addr = 0;
+ wild &= ~INPLOOKUP_IPV6;
+ };
+
+#if __OpenBSD__ || __FreeBSD__
+#if __FreeBSD__
+ inp->inp_flags |= INP_ANONPORT;
+#endif /* __FreeBSD__ */
+
+ if (inp->inp_flags & INP_HIGHPORT) {
+ first = ipport_hifirstauto; /* sysctl */
+ last = ipport_hilastauto;
+#if __FreeBSD__
+ lastport = &pcbinfo->lasthi;
+#endif /* __FreeBSD__ */
+ } else if (inp->inp_flags & INP_LOWPORT) {
+ if ((error = suser(p->p_ucred, &p->p_acflag)))
+ return (EACCES);
+#if __FreeBSD__
+ first = ipport_lowfirstauto;
+ last = ipport_lowlastauto;
+ lastport = &pcbinfo->lastlow;
+#else /* __FreeBSD__ */
+ first = IPPORT_RESERVED-1; /* 1023 */
+ last = 600; /* not IPPORT_RESERVED/2 */
+#endif /* __FreeBSD__ */
+ } else {
+ first = ipport_firstauto; /* sysctl */
+ last = ipport_lastauto;
+#if __FreeBSD__
+ lastport = &pcbinfo->lastport;
+#endif /* __FreeBSD__ */
+ }
+#else /* __OpenBSD__ */
+#if __bsdi__
+ first = IPPORT_DYNAMIC;
+ last = IPPORT_DYNAMIC_LAST;
+#else /* __bsdi__ */
+ first = IPPORT_RESERVED;
+ last = IPPORT_USERRESERVED;
+#endif /* __bsdi__ */
+#endif /* __OpenBSD__ */
+
+ /*
+ * 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.
+ */
+
+portloop:
+ if (first > last) {
+ /*
+ * counting down
+ */
+ if (loopcount == 0) { /* only do this once. */
+ old = first;
+#if __OpenBSD__
+ first -= (arc4random() % (first - last));
+#else /* __OpenBSD__ */
+ first -= (random() % (first - last));
+#endif /* __OpenBSD__ */
+ }
+ count = first - last;
+ *lastport = first; /* restart each time */
+
+ do {
+ if (count-- <= 0) { /* completely used? */
+ if (loopcount == 0) {
+ last = old;
+ loopcount++;
+ goto portloop;
+ }
+ return (EADDRNOTAVAIL);
+ }
+ --*lastport;
+ if (*lastport > first || *lastport < last)
+ *lastport = first;
+ lport = htons(*lastport);
+#if __OpenBSD__
+ } while (in_baddynamic(*lastport, so->so_proto->pr_protocol) ||
+#else /* __OpenBSD__ */
+ } while (
+#endif /* __OpenBSD__ */
+ ((wild & INPLOOKUP_IPV6) ?
+#ifdef __FreeBSD__
+ in_pcblookup_local(pcbinfo,
+ (struct in_addr *)&inp->inp_laddr6, lport, wild) :
+ in_pcblookup_local(pcbinfo,
+ (struct in_addr *)&la, lport, wild)));
+#else /* __FreeBSD__ */
+#if __NetBSD__
+ in_pcblookup_port(head, (struct in_addr *)&inp->inp_laddr6,
+ lport, wild) :
+ in_pcblookup_port(head, (struct in_addr *)&la, lport, wild)));
+#else /* __NetBSD__ */
+ in_pcblookup(head, (struct in_addr *)&zeroin6_addr, 0,
+ (struct in_addr *)&inp->inp_laddr6, lport, wild) :
+ in_pcblookup(head, (struct in_addr *)&fa, 0,
+ (struct in_addr *)&la, lport, wild)));
+#endif /* __NetBSD__ */
+#endif /* __FreeBSD__ */
+ } else {
+ /*
+ * counting up
+ */
+ if (loopcount == 0) { /* only do this once. */
+ old = first;
+#if __OpenBSD__
+ first += (arc4random() % (last - first));
+#else /* __OpenBSD__ */
+ first += (random() % (last - first));
+#endif /* __OpenBSD__ */
+ }
+ count = last - first;
+ *lastport = first; /* restart each time */
+
+ do {
+ if (count-- <= 0) { /* completely used? */
+ if (loopcount == 0) {
+ first = old;
+ loopcount++;
+ goto portloop;
+ }
+ return (EADDRNOTAVAIL);
+ }
+ ++*lastport;
+ if (*lastport < first || *lastport > last)
+ *lastport = first;
+ lport = htons(*lastport);
+#if __OpenBSD__
+ } while (in_baddynamic(*lastport, so->so_proto->pr_protocol) ||
+#else /* __OpenBSD__ */
+ } while (
+#endif /* __OpenBSD__ */
+ ((wild & INPLOOKUP_IPV6) ?
+#ifdef __FreeBSD__
+ in_pcblookup_local(pcbinfo,
+ (struct in_addr *)&inp->inp_laddr6, lport, wild) :
+ in_pcblookup_local(pcbinfo,
+ (struct in_addr *)&la, lport, wild)));
+#else /* __FreeBSD__ */
+#if __NetBSD__
+ in_pcblookup_port(head, (struct in_addr *)&inp->inp_laddr6,
+ lport, wild) :
+ in_pcblookup_port(head, (struct in_addr *)&la, lport, wild)));
+#else /* __NetBSD__ */
+ in_pcblookup(head, (struct in_addr *)&zeroin6_addr, 0,
+ (struct in_addr *)&inp->inp_laddr6, lport, wild) :
+ in_pcblookup(head, (struct in_addr *)&fa, 0,
+ (struct in_addr *)&la, lport, wild)));
+#endif /* __NetBSD__ */
+#endif /* __FreeBSD__ */
+ }
+ }
+
+ inp->inp_lport = lport;
+
+#if __FreeBSD__
+#if 0
+ inp->inp_laddr.s_addr = inp->inp_laddr6.in6a_words[3]
+ ^ inp->inp_laddr6.in6a_words[1];
+ /*
+ * If this is FreeBSD then it requires the inpcb structure to be
+ * inserted into various hash-tables. Right now this is copies
+ * directly from FreeBSD's source, but this will have to change for
+ * v6 addresses soon I think. We're still not sure if word2 XOR word4
+ * is a good subject for the hash function.
+ */
+#endif /* 0 */
+ if (in_pcbinshash(inp) != 0) {
+ bzero(&inp->inp_laddru, sizeof(inp->inp_laddru));
+ inp->inp_lport = 0;
+ return (EAGAIN);
+ }
+#endif /* __FreeBSD__ */
+#if __NetBSD__
+ in_pcbstate(inp, INP_BOUND);
+#endif /* __NetBSD__ */
+
+ /* XXX hash */
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ * This is maximum suckage point value. This function is the first
+ * half or so of in6_pcbconnect. It exists because FreeBSD's code
+ * supports this function for T/TCP. If we want TCP to work with our
+ * code it must be supported.
+ ----------------------------------------------------------------------*/
+#if __FreeBSD__
+int
+in6_pcbladdr(inp, nam, plocal_sin)
+ register struct inpcb *inp;
+ struct sockaddr *nam;
+ struct sockaddr **plocal_sin;
+{
+
+ struct in6_ifaddr *i6a;
+ struct sockaddr_in *ifaddr = NULL;
+
+ register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
+ if (nam->sa_len != sizeof(struct sockaddr_in6))
+ return (EINVAL);
+
+ if (sin6->sin6_family != AF_INET6)
+ return (EAFNOSUPPORT);
+ if (sin6->sin6_port == 0)
+ return (EADDRNOTAVAIL);
+
+
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
+ {
+ struct domain hackdomain, *save;
+ struct sockaddr_in *sin;
+ int rc;
+
+ /*
+ * FreeBSD doesn't want an mbuf to pcbconnect.
+ */
+ struct sockaddr_in hacksin;
+
+ /*
+ * Hmmm, if this is a v4-mapped v6 address, re-call in_pcbconnect
+ * with a fake domain for now. Then re-adjust the socket, and
+ * return out of here. Since this is called at splnet(), I don't
+ * think temporarily altering the socket will matter. XXX
+ */
+
+ /*
+ * Can't have v6 talking to v4, now can we!
+ */
+ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) &&
+ !IN6_IS_ADDR_V4MAPPED(&inp->inp_laddr6))
+ return EINVAL;
+
+ bzero(&hackdomain,sizeof(hackdomain));
+ save = inp->inp_socket->so_proto->pr_domain;
+ inp->inp_socket->so_proto->pr_domain = &hackdomain;
+ hackdomain.dom_family = PF_INET;
+
+ sin = &hacksin;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_port = sin6->sin6_port;
+ sin->sin_addr.s_addr = sin6->sin6_addr.in6a_words[3];
+
+ /* CHANGE */
+ rc = in_pcbladdr(inp, (struct sockaddr *) sin, &ifaddr);
+
+ inp->inp_socket->so_proto->pr_domain = save;
+
+ if (rc == 0)
+ {
+ inp->inp_laddr6.in6a_words[2] = htonl(0xffff);
+ inp->inp_faddr6.in6a_words[2] = htonl(0xffff);
+ inp->inp_flags |= INP_IPV6_MAPPED;
+ inp->inp_flags &= ~INP_IPV6_UNDEC;
+ }
+ *plocal_sin = (struct sockaddr *)ifaddr;
+
+ return rc;
+ }
+
+ if (in6_ifaddr && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
+ {
+ /*
+ * If the destination address is all 0's,
+ * use the first non-loopback (and if possibly, non-link-local, else
+ * use the LAST link-local, non-loopback) address as the destination.
+ */
+#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
+#define sin6tosa(sin6) ((struct sockaddr *)(sin6))
+#define ifatoi6a(ifa) ((struct in6_ifaddr *)(ifa))
+ struct in6_ifaddr *ti6a = NULL;
+
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ {
+ /* Find first (non-link-local if possible) address for
+ source usage. If multiple link-locals, use last one found. */
+ if (IN6_IS_ADDR_LINKLOCAL(&I6A_SIN(i6a)->sin6_addr))
+ ti6a=i6a;
+ else if (!IN6_IS_ADDR_LOOPBACK(&I6A_SIN(i6a)->sin6_addr))
+ break;
+ }
+ if (i6a == NULL && ti6a != NULL)
+ i6a = ti6a;
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6))
+ {
+ register struct route6 *ro;
+
+ i6a = NULL;
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the rt_ifa, else punt.
+ */
+ ro = &inp->inp_route6;
+ if (ro->ro_rt &&
+ (ro->ro_dst.sin6_family != sin6->sin6_family ||
+ rt_key(ro->ro_rt)->sa_family != sin6->sin6_family ||
+ !IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, &sin6->sin6_addr) ||
+ inp->inp_socket->so_options & SO_DONTROUTE))
+ {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = NULL;
+ }
+ if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
+ (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL))
+ {
+ /* No route yet, try and acquire one. */
+ ro->ro_dst.sin6_family = AF_INET6; /* Is ro blanked out? */
+ ro->ro_dst.sin6_len = sizeof(struct sockaddr_in6);
+ ro->ro_dst.sin6_addr = sin6->sin6_addr;
+ /*
+ * Need to wipe out the flowlabel for ifa_ifwith*
+ * but don't need to for rtalloc.
+ */
+ rtalloc((struct route *)ro);
+ }
+
+ if (ro->ro_rt == NULL)
+ {
+ /*
+ * No route of any kind, so spray neighbor solicits out all
+ * interfaces, unless it's a multicast address.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
+ return ENETUNREACH;
+ ipv6_onlink_query((struct sockaddr_in6 *)&ro->ro_dst);
+ rtalloc((struct route *)ro);
+ }
+ if (ro->ro_rt == NULL)
+ {
+ /*
+ * ipv6_onlink_query() should've added a route. It probably
+ * failed.
+ */
+ DPRINTF(IDL_GROSS_EVENT, ("v6_output: onlink_query didn't add route!\n"));
+ return ENETUNREACH;
+ }
+
+ if (ro->ro_rt->rt_ifa == NULL)
+ {
+ /*
+ * We have a route where we don't quite know which interface
+ * the neighbor belongs to yet. If I get here, I know that this
+ * route is not pre-allocated (such as done by in6_pcbconnect()),
+ * because those pre-allocators will do the same ipv6_onlink_query()
+ * and ipv6_verify_onlink() in advance.
+ *
+ * I can therefore free the route, and get it again.
+ */
+ int error;
+
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = NULL;
+ switch (error = ipv6_verify_onlink(&ro->ro_dst))
+ {
+ case -1:
+ return ENETUNREACH;
+ case 0:
+ break;
+ default:
+ return error;
+ }
+ rtalloc((struct route *)ro);
+ if (ro->ro_rt == NULL || ro->ro_rt->rt_ifa == NULL)
+ panic("Oops2, I'm forgetting something after verify_onlink().");
+ }
+
+
+ /*
+ * If we found a route, use the address
+ * corresponding to the outgoing interface
+ * unless it is the loopback (in case a route
+ * to our address on another net goes to loopback).
+ *
+ * This code may be simplified if the above gyrations work.
+ */
+ if (ro->ro_rt && ro->ro_rt->rt_ifp &&
+ !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
+ i6a = ifatoi6a(ro->ro_rt->rt_ifa);
+ if (i6a == NULL)
+ {
+ u_short fport = sin6->sin6_port;
+
+ /*
+ * Source address selection when there is no route.
+ *
+ * If ND out all if's is to be used, this is the point to do it.
+ * (Similar to when the route lookup fails in ipv6_output.c, and
+ * the 'on-link' assumption kicks in.)
+ *
+ * For multicast, use i6a of mcastdef.
+ */
+
+ sin6->sin6_port = 0;
+ i6a = ifatoi6a(ifa_ifwithdstaddr(sin6tosa(sin6)));
+ if (i6a == NULL)
+ i6a = ifatoi6a(ifa_ifwithnet(sin6tosa(sin6)));
+ sin6->sin6_port = fport;
+ if (i6a == NULL)
+ {
+ struct in6_ifaddr *ti6a = NULL;
+
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ {
+ /* Find first (non-local if possible) address for
+ source usage. If multiple locals, use last one found. */
+ if (IN6_IS_ADDR_LINKLOCAL(&I6A_SIN(i6a)->sin6_addr))
+ ti6a=i6a;
+ else if (!IN6_IS_ADDR_LOOPBACK(&I6A_SIN(i6a)->sin6_addr))
+ break;
+ }
+ if (i6a == NULL && ti6a != NULL)
+ i6a = ti6a;
+ }
+ if (i6a == NULL)
+ return EADDRNOTAVAIL;
+ }
+
+ /*
+ * If I'm "connecting" to a multicast address, gyrate properly to
+ * get a source address based upon the user-requested mcast interface.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) && inp->inp_moptions6 != NULL &&
+ (inp->inp_flags & INP_IPV6_MCAST))
+ {
+ struct ipv6_moptions *i6mo;
+ struct ifnet *ifp;
+
+ i6mo = inp->inp_moptions6;
+ if (i6mo->i6mo_multicast_ifp != NULL)
+ {
+ ifp = i6mo->i6mo_multicast_ifp;
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ if (i6a->i6a_ifp == ifp) /* Linkloc vs. global? */
+ break;
+ if (i6a == NULL)
+ return EADDRNOTAVAIL;
+ }
+ }
+
+ *plocal_sin = (struct sockaddr *)&i6a->i6a_addr;
+ }
+
+ return (0);
+}
+#endif /* __FreeBSD__ */
+
+/*----------------------------------------------------------------------
+ * Connect from a socket to a specified address.
+ * Both address and port must be specified in argument sin6.
+ * Eventually, flow labels will have to be dealt with here, as well.
+ *
+ * If don't have a local address for this socket yet,
+ * then pick one.
+ *
+ * I believe this has to be called at splnet().
+ ----------------------------------------------------------------------*/
+
+int
+in6_pcbconnect(inp, nam)
+ register struct inpcb *inp;
+#if __FreeBSD__
+ struct sockaddr *nam;
+#else /* __FreeBSD__ */
+ struct mbuf *nam;
+#endif /* __FreeBSD__ */
+{
+ struct in6_ifaddr *i6a;
+ struct sockaddr_in6 *ifaddr = NULL;
+
+#if __FreeBSD__
+ register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
+#else /* __FreeBSD__ */
+ register struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *);
+ if (nam->m_len != sizeof(struct sockaddr_in6))
+ return (EINVAL);
+#endif /* __FreeBSD__ */
+
+ if (sin6->sin6_len != sizeof(struct sockaddr_in6))
+ return (EINVAL);
+ if (sin6->sin6_family != AF_INET6)
+ return (EAFNOSUPPORT);
+ if (sin6->sin6_port == 0)
+ return (EADDRNOTAVAIL);
+
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
+ {
+ struct domain hackdomain, *save;
+ struct mbuf hackmbuf;
+ struct sockaddr_in *sin;
+ int rc;
+
+#if __FreeBSD__
+ /*
+ * FreeBSD doesn't want an mbuf to pcbconnect.
+ */
+ struct sockaddr_in hacksin;
+#endif /* __FreeBSD__ */
+
+ /*
+ * Hmmm, if this is a v4-mapped v6 address, re-call in_pcbconnect
+ * with a fake domain for now. Then re-adjust the socket, and
+ * return out of here. Since this is called at splnet(), I don't
+ * think temporarily altering the socket will matter. XXX
+ */
+
+ /*
+ * Can't have v6 talking to v4, now can we!
+ */
+ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) &&
+ !IN6_IS_ADDR_V4MAPPED(&inp->inp_laddr6))
+ return EINVAL;
+
+ bzero(&hackdomain,sizeof(hackdomain));
+ bzero(&hackmbuf,sizeof(hackmbuf));
+ save = inp->inp_socket->so_proto->pr_domain;
+ inp->inp_socket->so_proto->pr_domain = &hackdomain;
+ hackdomain.dom_family = PF_INET;
+#ifndef __FreeBSD__
+ hackmbuf.m_hdr = nam->m_hdr;
+#endif /* __FreeBSD__ */
+ hackmbuf.m_len = sizeof(*sin);
+ hackmbuf.m_data = hackmbuf.m_dat;
+
+#if __FreeBSD__
+ sin = &hacksin;
+#else /* __FreeBSD__ */
+ sin = mtod(&hackmbuf,struct sockaddr_in *);
+#endif /* __FreeBSD__ */
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_port = sin6->sin6_port;
+ sin->sin_addr.s_addr = sin6->sin6_addr.in6a_words[3];
+
+#if __FreeBSD__
+ {
+#if !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)
+ struct proc *p = curproc; /* XXX */
+#else /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
+ struct proc *p = PCPU(curproc); /* XXX */
+#endif /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
+ rc = in_pcbconnect(inp, (struct sockaddr *) sin, p);
+ }
+#else /* __FreeBSD__ */
+ rc = in_pcbconnect(inp,&hackmbuf);
+#endif /* __FreeBSD__ */
+
+ inp->inp_socket->so_proto->pr_domain = save;
+
+ if (rc == 0)
+ {
+ inp->inp_laddr6.in6a_words[2] = htonl(0xffff);
+ inp->inp_faddr6.in6a_words[2] = htonl(0xffff);
+ inp->inp_flags |= INP_IPV6_MAPPED;
+ inp->inp_flags &= ~INP_IPV6_UNDEC;
+ }
+
+ return rc;
+ }
+
+ if (in6_ifaddr && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
+ {
+ /*
+ * If the destination address is all 0's,
+ * use the first non-loopback (and if possibly, non-link-local, else
+ * use the LAST link-local, non-loopback) address as the destination.
+ */
+#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
+#define sin6tosa(sin6) ((struct sockaddr *)(sin6))
+#define ifatoi6a(ifa) ((struct in6_ifaddr *)(ifa))
+ struct in6_ifaddr *ti6a = NULL;
+
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ {
+ /* Find first (non-link-local if possible) address for
+ source usage. If multiple link-locals, use last one found. */
+ if (IN6_IS_ADDR_LINKLOCAL(&I6A_SIN(i6a)->sin6_addr))
+ ti6a=i6a;
+ else if (!IN6_IS_ADDR_LOOPBACK(&I6A_SIN(i6a)->sin6_addr))
+ break;
+ }
+ if (i6a == NULL && ti6a != NULL)
+ i6a = ti6a;
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6))
+ {
+ register struct route6 *ro;
+
+
+ i6a = NULL;
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the rt_ifa, else punt.
+ */
+ ro = &inp->inp_route6;
+ if (ro->ro_rt &&
+ (ro->ro_dst.sin6_family != sin6->sin6_family ||
+ rt_key(ro->ro_rt)->sa_family != sin6->sin6_family ||
+ !IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, &sin6->sin6_addr) ||
+ inp->inp_socket->so_options & SO_DONTROUTE))
+ {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = NULL;
+ }
+ if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
+ (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL))
+ {
+ /* No route yet, try and acquire one. */
+ ro->ro_dst.sin6_family = AF_INET6; /* Is ro blanked out? */
+ ro->ro_dst.sin6_len = sizeof(struct sockaddr_in6);
+ ro->ro_dst.sin6_addr = sin6->sin6_addr;
+ /*
+ * Need to wipe out the flowlabel for ifa_ifwith*
+ * but don't need to for rtalloc.
+ */
+ rtalloc((struct route *)ro);
+ }
+
+ if (ro->ro_rt == NULL)
+ {
+ /*
+ * No route of any kind, so spray neighbor solicits out all
+ * interfaces, unless it's a multicast address.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
+ return ENETUNREACH;
+ ipv6_onlink_query((struct sockaddr_in6 *)&ro->ro_dst);
+ rtalloc((struct route *)ro);
+ }
+ if (ro->ro_rt == NULL)
+ {
+ /*
+ * ipv6_onlink_query() should've added a route. It probably
+ * failed.
+ */
+ DPRINTF(GROSSEVENT, ("v6_output: onlink_query didn't add route!\n"));
+ return ENETUNREACH;
+ }
+
+ if (ro->ro_rt->rt_ifa == NULL)
+ {
+ /*
+ * We have a route where we don't quite know which interface
+ * the neighbor belongs to yet. If I get here, I know that this
+ * route is not pre-allocated (such as done by in6_pcbconnect()),
+ * because those pre-allocators will do the same ipv6_onlink_query()
+ * and ipv6_verify_onlink() in advance.
+ *
+ * I can therefore free the route, and get it again.
+ */
+ int error;
+
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = NULL;
+ switch (error = ipv6_verify_onlink(&ro->ro_dst))
+ {
+ case -1:
+ return ENETUNREACH;
+ case 0:
+ break;
+ default:
+ return error;
+ }
+ rtalloc((struct route *)ro);
+ if (ro->ro_rt == NULL || ro->ro_rt->rt_ifa == NULL)
+ panic("Oops2, I'm forgetting something after verify_onlink().");
+ }
+
+
+ /*
+ * If we found a route, use the address
+ * corresponding to the outgoing interface
+ * unless it is the loopback (in case a route
+ * to our address on another net goes to loopback).
+ *
+ * This code may be simplified if the above gyrations work.
+ */
+ if (ro->ro_rt && ro->ro_rt->rt_ifp &&
+ !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
+ i6a = ifatoi6a(ro->ro_rt->rt_ifa);
+ if (i6a == NULL)
+ {
+ u_short fport = sin6->sin6_port;
+
+ /*
+ * Source address selection when there is no route.
+ *
+ * If ND out all if's is to be used, this is the point to do it.
+ * (Similar to when the route lookup fails in ipv6_output.c, and
+ * the 'on-link' assumption kicks in.)
+ *
+ * For multicast, use i6a of mcastdef.
+ */
+
+ sin6->sin6_port = 0;
+ i6a = ifatoi6a(ifa_ifwithdstaddr(sin6tosa(sin6)));
+ if (i6a == NULL)
+ i6a = ifatoi6a(ifa_ifwithnet(sin6tosa(sin6)));
+ sin6->sin6_port = fport;
+ if (i6a == NULL)
+ {
+ struct in6_ifaddr *ti6a = NULL;
+
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ {
+ /* Find first (non-local if possible) address for
+ source usage. If multiple locals, use last one found. */
+ if (IN6_IS_ADDR_LINKLOCAL(&I6A_SIN(i6a)->sin6_addr))
+ ti6a=i6a;
+ else if (!IN6_IS_ADDR_LOOPBACK(&I6A_SIN(i6a)->sin6_addr))
+ break;
+ }
+ if (i6a == NULL && ti6a != NULL)
+ i6a = ti6a;
+ }
+ if (i6a == NULL)
+ return EADDRNOTAVAIL;
+ }
+
+ /*
+ * If I'm "connecting" to a multicast address, gyrate properly to
+ * get a source address based upon the user-requested mcast interface.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) && inp->inp_moptions6 != NULL &&
+ (inp->inp_flags & INP_IPV6_MCAST))
+ {
+ struct ipv6_moptions *i6mo;
+ struct ifnet *ifp;
+
+ i6mo = inp->inp_moptions6;
+ if (i6mo->i6mo_multicast_ifp != NULL)
+ {
+ ifp = i6mo->i6mo_multicast_ifp;
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ if (i6a->i6a_ifp == ifp) /* Linkloc vs. global? */
+ break;
+ if (i6a == NULL)
+ return EADDRNOTAVAIL;
+ }
+ }
+
+ ifaddr = (struct sockaddr_in6 *)&i6a->i6a_addr;
+ }
+
+#if __FreeBSD__
+ if (in_pcblookup_hash(inp->inp_pcbinfo,
+ ((IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) ?
+ (struct in_addr *)&ifaddr->sin6_addr :
+ (struct in_addr *)&inp->inp_laddr6),
+ sin6->sin6_port, (struct in_addr *)&sin6->sin6_addr,
+ inp->inp_lport, INPLOOKUP_IPV6)) return EADDRINUSE;
+#else /* __FreeBSD__ */
+#if __NetBSD__
+ if (in6_pcblookup_connect(inp->inp_table,
+ ((IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) ? &ifaddr->sin6_addr :
+ &inp->inp_laddr6), sin6->sin6_port, &sin6->sin6_addr, inp->inp_lport))
+ return EADDRINUSE;
+#else /* __NetBSD__ */
+#ifdef __OpenBSD__
+ if (in_pcblookup(inp->inp_table, ((IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) ?
+#else /* __OpenBSD__ */
+ if (in_pcblookup(inp->inp_head, ((IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) ?
+#endif /* __OpenBSD__ */
+ (struct in_addr *)&ifaddr->sin6_addr :
+ (struct in_addr *)&inp->inp_laddr6),
+ sin6->sin6_port, (struct in_addr *)&sin6->sin6_addr,
+ inp->inp_lport, INPLOOKUP_IPV6)) return EADDRINUSE;
+#endif /* __NetBSD__ */
+#endif /* __FreeBSD__ */
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6))
+ {
+ if (inp->inp_lport == 0)
+ (void)in6_pcbbind(inp, NULL); /* To find free port & bind to it. */
+ inp->inp_laddr6 = ifaddr->sin6_addr;
+ }
+ inp->inp_faddr6 = sin6->sin6_addr;
+ inp->inp_fport = sin6->sin6_port;
+
+ /*
+ * Assumes user specify flowinfo in network order.
+ */
+ inp->inp_ipv6.ipv6_versfl = htonl(0x60000000) |
+ (sin6->sin6_flowinfo & htonl(0x0fffffff));
+
+#if __NetBSD__
+ in_pcbstate(inp, INP_CONNECTED);
+#endif /* __NetBSD__ */
+
+#if __FreeBSD__
+ /*
+ * See pcbbind near the end for reasoning on setting this for a value to
+ * hash on.
+ */
+#if 0
+ inp->inp_laddr.s_addr = inp->inp_laddr6.in6a_words[3]
+ ^ inp->inp_laddr6.in6a_words[1];
+#endif /* 0 */
+ in_pcbrehash(inp);
+#endif /* __FreeBSD__ */
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ * 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.
+ *
+ * Also perform input-side security policy check
+ * once PCB to be notified has been located.
+ *
+ * Must be called at splnet.
+ ----------------------------------------------------------------------*/
+
+int
+#ifdef IPSEC
+in6_pcbnotify(head, dst, fport_arg, la, lport_arg, cmd, notify, m, nexthdr)
+#else /* IPSEC */
+in6_pcbnotify(head, dst, fport_arg, la, lport_arg, cmd, notify)
+#endif /* IPSEC */
+#if __NetBSD__ || __OpenBSD__
+ struct inpcbtable *head;
+#else /* __NetBSD__ || __OpenBSD__ */
+#if __FreeBSD__
+ struct inpcbhead *head;
+#else /* __FreeBSD__ */
+ struct inpcb *head;
+#endif /* __FreeBSD__ */
+#endif /* __NetBSD__ || __OpenBSD__ */
+ struct sockaddr *dst;
+ uint fport_arg;
+ struct in6_addr *la;
+ uint lport_arg;
+ int cmd;
+ void (*notify) __P((struct inpcb *, int));
+#ifdef IPSEC
+ struct mbuf *m;
+ int nexthdr;
+#endif /* IPSEC */
+{
+ register struct inpcb *inp, *oinp;
+ struct in6_addr *faddr,laddr = *la;
+ u_short fport = fport_arg, lport = lport_arg;
+ int errno;
+#ifdef IPSEC
+ struct sockaddr_in6 srcsa, dstsa;
+#endif /* IPSEC */
+
+ DPRINTF(IDL_EVENT,("Entering in6_pcbnotify. head = 0x%lx, dst is\n", (unsigned long)head));
+ DDO(IDL_EVENT,dump_smart_sockaddr(dst));
+ DPRINTF(IDL_EVENT,("fport_arg = %d, lport_arg = %d, cmd = %d\n",\
+ fport_arg, lport_arg, cmd));
+ DDO(IDL_EVENT,printf("la is ");dump_in6_addr(la));
+
+ if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET6)
+ return 1;
+ faddr = &(((struct sockaddr_in6 *)dst)->sin6_addr);
+ if (IN6_IS_ADDR_UNSPECIFIED(faddr))
+ return 1;
+ if (IN6_IS_ADDR_V4MAPPED(faddr))
+ {
+ printf("Huh? Thought in6_pcbnotify() never got called with mapped!\n");
+ }
+
+ /*
+ * 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;
+ laddr = in6addr_any;
+ if (cmd != PRC_HOSTDEAD)
+ notify = in_rtchange;
+ }
+ errno = inet6ctlerrmap[cmd];
+
+#ifdef IPSEC
+ /* Build these again because args aren't necessarily correctly formed
+ sockaddrs, and the policy code will eventually need that. -cmetz */
+
+ memset(&srcsa, 0, sizeof(struct sockaddr_in6));
+ srcsa.sin6_len = sizeof(struct sockaddr_in6);
+ srcsa.sin6_family = AF_INET6;
+ /* defer port */
+ srcsa.sin6_addr = *faddr;
+
+ memset(&dstsa, 0, sizeof(struct sockaddr_in6));
+ dstsa.sin6_len = sizeof(struct sockaddr_in6);
+ dstsa.sin6_family = AF_INET6;
+ /* defer port */
+ dstsa.sin6_addr = laddr;
+#endif /* IPSEC */
+
+#if __NetBSD__ || __OpenBSD__
+ for (inp = head->inpt_queue.cqh_first;
+ inp != (struct inpcb *)&head->inpt_queue;)
+#else /* __NetBSD__ || __OpenBSD__ */
+#if __FreeBSD__
+ for (inp = head->lh_first; inp != NULL;)
+#else /* __FreeBSD__ */
+ for (inp = head->inp_next; inp != head;)
+#endif /* __FreeBSD__ */
+#endif /* __NetBSD__ || __OpenBSD__ */
+ {
+ if (!IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, faddr) ||
+ !inp->inp_socket ||
+ (lport && inp->inp_lport != lport) ||
+ (!IN6_IS_ADDR_UNSPECIFIED(&laddr) && !IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, &laddr)) ||
+ (fport && inp->inp_fport != fport))
+ {
+#if __NetBSD__ || __OpenBSD__
+ inp = inp->inp_queue.cqe_next;
+#else /* __NetBSD__ || __OpenBSD__ */
+#if __FreeBSD__
+ inp = inp->inp_list.le_next;
+#else /* __FreeBSD__ */
+ inp = inp->inp_next;
+#endif /* __FreeBSD__ */
+#endif /* __NetBSD__ || __OpenBSD__ */
+ continue;
+ }
+ oinp = inp;
+
+#if __NetBSD__ || __OpenBSD__
+ inp = inp->inp_queue.cqe_next;
+#else /* __NetBSD__ || __OpenBSD__ */
+#if __FreeBSD__
+ inp = inp->inp_list.le_next;
+#else /* __FreeBSD__ */
+ inp = inp->inp_next;
+#endif /* __FreeBSD__ */
+#endif /* __NetBSD__ || __OpenBSD__ */
+
+ if (notify)
+#ifdef IPSEC
+ {
+ /* Pretend the packet came in for this source/destination port pair,
+ since that's what we care about for policy. If the passed in
+ fport and/or lport were nonzero, the comparison will make sure
+ they match that of the PCB and the right thing will happen.
+ -cmetz */
+ srcsa.sin6_port = oinp->inp_fport;
+ dstsa.sin6_port = oinp->inp_lport;
+
+ /* XXX - state arg should not be NULL */
+ if (!netproc_inputpolicy(oinp->inp_socket, (struct sockaddr *)&srcsa,
+ (struct sockaddr *)&dstsa, nexthdr, m, NULL,
+ NULL))
+#endif /* IPSEC */
+ (*notify)(oinp, errno);
+#ifdef IPSEC
+ }
+#endif /* IPSEC */
+ }
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ * Get the local address/port, and put it in a sockaddr_in6.
+ * This services the getsockname(2) call.
+ ----------------------------------------------------------------------*/
+
+int
+in6_setsockaddr(inp, nam)
+ register struct inpcb *inp;
+#if __FreeBSD__
+ struct sockaddr **nam;
+#else /* __FreeBSD__ */
+ struct mbuf *nam;
+#endif /* __FreeBSD__ */
+{
+ register struct sockaddr_in6 *sin6;
+
+
+#if __FreeBSD__
+ /*
+ * In FreeBSD we have to allocate the sockaddr_in6 structure since we aren't
+ * given an mbuf, but a sockaddr ** as the second argument. (i.e. a location
+ * to reference the space this routine allocates.
+ */
+ if (!(sin6 = (struct sockaddr_in6 *)malloc(sizeof(struct sockaddr_in6),M_SONAME, M_DONTWAIT)))
+ return ENOBUFS; /* XXX */
+
+#else /* __FreeBSD__ */
+
+ nam->m_len = sizeof(struct sockaddr_in6);
+ sin6 = mtod(nam,struct sockaddr_in6 *);
+
+#endif /* __FreeBSD__ */
+
+ bzero ((caddr_t)sin6,sizeof(struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_port = inp->inp_lport;
+ sin6->sin6_addr = inp->inp_laddr6;
+
+#if __FreeBSD__
+ *nam = (struct sockaddr *) sin6;
+#endif /* __FreeBSD__ */
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ * Get the foreign address/port, and put it in a sockaddr_in6.
+ * This services the getpeername(2) call.
+ ----------------------------------------------------------------------*/
+
+int
+in6_setpeeraddr(inp, nam)
+ register struct inpcb *inp;
+#if __FreeBSD__
+ struct sockaddr **nam;
+#else /* __FreeBSD__ */
+ struct mbuf *nam;
+#endif /* __FreeBSD__ */
+{
+ register struct sockaddr_in6 *sin6;
+
+#if __FreeBSD__
+ /*
+ * In FreeBSD we have to allocate the sockaddr_in6 structure since we aren't
+ * given an mbuf, but a sockaddr ** as the second argument. (i.e. a location
+ * to reference the space this routine allocates.
+ */
+ if (!(sin6 = (struct sockaddr_in6 *)malloc(sizeof(struct sockaddr_in6),M_SONAME, M_DONTWAIT)))
+ return ENOBUFS; /* XXX */
+
+#else /* __FreeBSD__ */
+
+ nam->m_len = sizeof(struct sockaddr_in6);
+ sin6 = mtod(nam,struct sockaddr_in6 *);
+
+#endif /* __FreeBSD__ */
+
+ bzero ((caddr_t)sin6,sizeof(struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_port = inp->inp_fport;
+ sin6->sin6_addr = inp->inp_faddr6;
+ sin6->sin6_flowinfo = inp->inp_fflowinfo;
+
+#if __FreeBSD__
+ *nam = (struct sockaddr *) sin6;
+#endif /* __FreeBSD__ */
+
+ return 0;
+}
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
new file mode 100644
index 00000000000..a4222730caa
--- /dev/null
+++ b/sys/netinet6/in6_proto.c
@@ -0,0 +1,387 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/kernel.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/radix.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/in_pcb.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcpip.h>
+#if __FreeBSD__ && defined(_NETINET_IN_PCB_H_)
+#undef _NETINET_IN_PCB_H_
+#include <netinet/tcp_var.h>
+#define _NETINET_IN_PCB_H_
+#else /* __FreeBSD__ */
+#include <netinet/tcp_var.h>
+#endif /* __FreeBSD__ */
+#include <netinet/tcpip.h>
+#include <netinet/tcp_debug.h>
+
+#include <netinet6/in6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ipv6.h>
+#include <netinet6/ipv6_var.h>
+#include <netinet6/ipv6_icmp.h>
+
+#if defined(IPSEC) || defined(NRL_IPSEC)
+#include <netsec/ipsec.h>
+#endif /* defined(IPSEC) || defined(NRL_IPSEC) */
+
+#if __FreeBSD__
+#include <sys/sysctl.h>
+#endif /* __FreeBSD__ */
+
+extern struct domain inet6domain;
+
+#define CAST (void *)
+
+#if !__FreeBSD__
+struct protosw inet6sw[] = {
+/* normal protocol switch */
+ {
+ 0, &inet6domain, 0, 0, /* NOTE: This 0 is the same as IPPROTO_HOPOPTS,
+ but we specially demux IPPROTO_HOPOPTS
+ in ipv6_input(). */
+ CAST ipv6_hop, CAST ipv6_output, 0, 0, /* Watch for hop-by-hop input! */
+ 0,
+ ipv6_init, 0, ipv6_slowtimo, ipv6_drain, ipv6_sysctl
+ },
+
+ /* ICMPv6 entry */
+
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_ICMPV6, PR_ATOMIC|PR_ADDR,
+ CAST ipv6_icmp_input, CAST ipv6_icmp_output, 0, ripv6_ctloutput,
+ ipv6_icmp_usrreq,
+#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
+ 0, 0, 0, 0, ipv6_icmp_sysctl
+#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ 0, 0, 0, 0, 0
+#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ },
+
+ /* IPv6-in-IPv6 tunnel entry */
+
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR,
+ CAST ipv6_input, CAST ripv6_output, 0, ripv6_ctloutput,
+ ripv6_usrreq,
+#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
+ 0, 0, 0, 0, ipv6_sysctl
+#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ 0, 0, 0, 0, 0
+#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ },
+
+ /* IPv4-in-IPv6 tunnel entry */
+
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR,
+ CAST ipv4_input, 0, 0, 0,
+ 0,
+#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
+ 0, 0, 0, 0, ip_sysctl
+#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ 0, 0, 0, 0, 0
+#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ },
+
+ /* Fragment entry */
+
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_FRAGMENT, PR_ATOMIC|PR_ADDR,
+ CAST ipv6_reasm, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0, 0
+ },
+
+
+ /* UDP entry */
+
+ /*
+ * Eventually, that ipv6_ctloutput() will have to be replaced with a
+ * udp_ctloutput(), which knows whether or not to redirect things down to
+ * IP or IPv6 appropriately.
+ */
+
+ {
+ SOCK_DGRAM, &inet6domain, IPPROTO_UDP, PR_ATOMIC|PR_ADDR,
+ CAST udp_input, 0, CAST udp_ctlinput, ipv6_ctloutput,
+ udp_usrreq,
+#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
+ 0, 0, 0, 0, udp_sysctl
+#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ 0, 0, 0, 0, 0
+#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ },
+
+ /* TCP entry */
+
+ {
+ SOCK_STREAM, &inet6domain, IPPROTO_TCP, PR_CONNREQUIRED|PR_WANTRCVD,
+ CAST tcp_input, 0, CAST tcp_ctlinput, tcp_ctloutput,
+ tcp_usrreq,
+#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
+ 0, 0, 0, 0, tcp_sysctl /* init, fasttimo, etc. in v4 protosw already! */
+#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ 0, 0, 0, 0, 0 /* init, fasttimo, etc. in v4 protosw already! */
+#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ },
+
+#if defined(IPSEC) || defined(NRL_IPSEC)
+ /* IPv6 & IPv4 Authentication Header */
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_AH, PR_ATOMIC|PR_ADDR,
+ CAST ipsec_ah_input, 0, 0, 0,
+ 0,
+#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
+ 0, 0, 0, 0, ipsec_ah_sysctl
+#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ 0, 0, 0, 0, 0
+#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ },
+
+#ifdef IPSEC_ESP
+ /* IPv6 & IPv4 Encapsulating Security Payload Header */
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR,
+ CAST ipsec_esp_input, 0, 0, 0,
+ 0,
+#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
+ 0, 0, 0, 0, ipsec_esp_sysctl
+#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ 0, 0, 0, 0, 0
+#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ },
+#endif /* IPSEC_ESP */
+#endif /* defined(IPSEC) || defined(NRL_IPSEC) */
+
+ /* Unknown header. */
+
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_RAW, PR_ATOMIC|PR_ADDR,
+ CAST ripv6_input, CAST ripv6_output, 0, ripv6_ctloutput,
+ ripv6_usrreq,
+ 0,0,0,0,0
+ },
+
+ /* Raw wildcard */
+ {
+ SOCK_RAW, &inet6domain, 0, PR_ATOMIC|PR_ADDR,
+ CAST ripv6_input, CAST ripv6_output, 0, ripv6_ctloutput,
+ ripv6_usrreq,
+ ripv6_init,0,0,0,0
+ },
+};
+#else /* !__FreeBSD__ */
+extern struct pr_usrreqs nousrreqs;
+struct protosw inet6sw[] = {
+ {
+ 0, &inet6domain, 0, 0, /* NOTE: This 0 is the same as IPPROTO_HOPOPTS,
+ but we specially demux IPPROTO_HOPOPTS
+ in ipv6_input(). */
+ CAST ipv6_hop, CAST ipv6_output, 0, 0, /* Watch for hop-by-hop input! */
+ 0,
+ ipv6_init, 0, ipv6_slowtimo, ipv6_drain,
+ &nousrreqs
+ },
+
+ /* ICMPv6 entry */
+
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_ICMPV6, PR_ATOMIC|PR_ADDR,
+ CAST ipv6_icmp_input, CAST ipv6_icmp_output, 0, CAST ripv6_ctloutput,
+ 0,
+ 0, 0, 0, 0,
+ &ipv6_icmp_usrreqs,
+ },
+
+ /* IPv6-in-IPv6 tunnel entry */
+
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR,
+ CAST ipv6_input, CAST ripv6_output, 0, ripv6_ctloutput,
+ 0,
+ 0, 0, 0, 0,
+ &ripv6_usrreqs
+ },
+
+ /* IPv4-in-IPv6 tunnel entry */
+
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR,
+ CAST ipv4_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs
+ },
+
+ /* Fragment entry */
+
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_FRAGMENT, PR_ATOMIC|PR_ADDR,
+ CAST ipv6_reasm, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs
+ },
+
+
+ /* UDP entry */
+
+ /*
+ * Eventually, that ipv6_ctloutput() will have to be replaced with a
+ * udp_ctloutput(), which knows whether or not to redirect things down to
+ * IP or IPv6 appropriately.
+ */
+
+ {
+ SOCK_DGRAM, &inet6domain, IPPROTO_UDP, PR_ATOMIC|PR_ADDR,
+ CAST udp_input, 0, CAST udp_ctlinput, ipv6_ctloutput,
+ 0,
+ udp_init, 0, 0, 0,
+ &udp_usrreqs
+ },
+
+ /* TCP entry */
+
+ {
+ SOCK_STREAM, &inet6domain, IPPROTO_TCP, PR_CONNREQUIRED|PR_WANTRCVD,
+ CAST tcp_input, 0, CAST tcp_ctlinput, tcp_ctloutput,
+ 0,
+ tcp_init, tcp_fasttimo, tcp_slowtimo, tcp_drain,
+ &tcp_usrreqs,
+ },
+
+#if defined(IPSEC) || defined(NRL_IPSEC)
+ /* IPv6 & IPv4 Authentication Header */
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_AH, PR_ATOMIC|PR_ADDR,
+ CAST ipsec_ah_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs
+ },
+
+#ifdef IPSEC_ESP
+ /* IPv6 & IPv4 Encapsulating Security Payload Header */
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR,
+ CAST ipsec_esp_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs
+ },
+#endif /* IPSEC_ESP */
+#endif /* defined(IPSEC) || defined(NRL_IPSEC) */
+
+ /* Unknown header. */
+
+ {
+ SOCK_RAW, &inet6domain, IPPROTO_RAW, PR_ATOMIC|PR_ADDR,
+ CAST ripv6_input, CAST ripv6_output, 0, ripv6_ctloutput,
+ 0,
+ 0,0,0,0,
+ &ripv6_usrreqs
+ },
+
+ /* Raw wildcard */
+ {
+ SOCK_RAW, &inet6domain, 0, PR_ATOMIC|PR_ADDR,
+ CAST ripv6_input, CAST ripv6_output, 0, ripv6_ctloutput,
+ 0,
+ ripv6_init,0,0,0,
+ &ripv6_usrreqs,
+ },
+};
+
+#endif /* !__FreeBSD__ */
+
+#if !__FreeBSD__
+struct domain inet6domain =
+{
+ PF_INET6, "IPv6", 0, 0, 0,
+ inet6sw, &inet6sw[sizeof(inet6sw)/sizeof(inet6sw[0])], 0,
+ /*
+ * FreeBSD's IPv4 replaces rn_inithead() with an IPv4-specific function.
+ * Our IPv6 uses the ifa->ifa_rtrequest() function pointer to intercept
+ * rtrequest()s. The consequence of this is that we use the generic
+ * rn_inithead().
+ */
+ rn_inithead, 64, sizeof(struct sockaddr_in6)
+};
+#else /* !__FreeBSD__ */
+struct domain inet6domain =
+{
+ PF_INET6, "IPv6", 0, 0, 0,
+ inet6sw, &inet6sw[sizeof(inet6sw)/sizeof(inet6sw[0])], 0,
+ /*
+ * FreeBSD's IPv4 replaces rn_inithead() with an IPv4-specific function.
+ * Our IPv6 uses the ifa->ifa_rtrequest() function pointer to intercept
+ * rtrequest()s. The consequence of this is that we use the generic
+ * rn_inithead().
+ */
+ rn_inithead, 64, sizeof(struct sockaddr_in6)
+};
+
+DOMAIN_SET(inet6);
+#endif /* !__FreeBSD__ */
+
+/* Eventually, make these go away -- if you want to be a router, twiddle the
+ sysctls before bringing up your interfaces */
+
+#ifndef IPV6FORWARDING
+#ifdef IPV6GATEWAY
+#define IPV6FORWARDING 1
+#else
+#define IPV6FORWARDING 0
+#endif /* IPV6GATEWAY */
+#endif /* IPV6FORWARDING */
+
+#ifndef IPV6RSOLICIT
+#if IPV6FORWARDING
+#define IPV6RSOLICIT 0
+#else /* IPV6FORWARDING */
+#define IPV6RSOLICIT 1
+#endif /* IPV6FORWARDING */
+#endif /* IPV6RSOLICIT */
+
+#ifndef IFQMAXLEN
+#define IFQMAXLEN IFQ_MAXLEN
+#endif
+
+int ipv6forwarding = IPV6FORWARDING;
+int ipv6rsolicit = IPV6RSOLICIT;
+int ipv6_defhoplmt = MAXHOPLIMIT;
+int ipv6qmaxlen = IFQMAXLEN;
+
+#if __FreeBSD__
+SYSCTL_NODE(_net_inet, IPPROTO_IPV6, ipv6, CTLFLAG_RW, 0, "IPV6");
+SYSCTL_NODE(_net_inet, IPPROTO_ICMPV6, icmpv6, CTLFLAG_RW, 0, "ICMPV6");
+#endif /* __FreeBSD__ */
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
new file mode 100644
index 00000000000..3052498d387
--- /dev/null
+++ b/sys/netinet6/in6_var.h
@@ -0,0 +1,189 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+#ifndef _NETINET6_IN6_VAR_H
+#define _NETINET6_IN6_VAR_H 1
+
+/*
+ * IPv6 interface request and alias request. Use sockaddr_in6 because
+ * it is larger than sockaddr.
+ */
+
+struct inet6_ifreq
+{
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif /* IFNAMSIZ */
+ char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ union {
+ struct sockaddr_in6 ifru_addr;
+ struct sockaddr_in6 ifru_dstaddr;
+ struct sockaddr_in6 ifru_broadaddr;
+ short ifru_flags;
+ int ifru_metric;
+ caddr_t ifru_data;
+ } ifr_ifru;
+#define ifr_addr ifr_ifru.ifru_addr /* address */
+#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */
+#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
+#define ifr_flags ifr_ifru.ifru_flags /* flags */
+#define ifr_metric ifr_ifru.ifru_metric /* metric */
+#define ifr_data ifr_ifru.ifru_data /* for use by interface */
+ };
+
+/*
+ * IPv6 interface "alias" request. Used to add interface addresses. This
+ * may be needed to be expanded to pass down/up permanancy information, and
+ * possibly deprecation lifetime values. (That is, if the kernel doesn't
+ * compute that stuff itself.)
+ */
+
+struct inet6_aliasreq
+{
+ char ifra_name[IFNAMSIZ];
+ struct sockaddr_in6 ifra_addr;
+ struct sockaddr_in6 ifra_dstaddr;
+#define ifra_broadaddr ifra_dstaddr
+ struct sockaddr_in6 ifra_mask;
+};
+
+/* ioctl()'s for stuff with inet6_{aliasreq,ifreq} (gag!) */
+
+#define SIOCDIFADDR_INET6 _IOW('i',25, struct inet6_ifreq) /* delete IF addr */
+#define SIOCAIFADDR_INET6 _IOW('i',26, struct inet6_aliasreq)/* add/chg IFalias */
+#define SIOCGIFADDR_INET6 _IOWR('i',33, struct inet6_ifreq) /* get ifnet address */
+#define SIOCGIFDSTADDR_INET6 _IOWR('i',34, struct inet6_ifreq) /* get dst address */
+#define SIOCSIFDSTADDR_INET6 _IOW('i', 14, struct inet6_ifreq) /* set dst address */
+#define SIOCGIFNETMASK_INET6 _IOWR('i',37, struct inet6_ifreq) /* get netmask */
+
+#define SIOCVIFADDR_INET6 _IOW('i',69,struct inet6_ifreq) /* Verify IPv6 addr */
+
+/*
+ * INET6 interface address. This might also serve as the prefix list,
+ * with the help of the I6AF_PREFIX flag.
+ */
+
+struct in6_ifaddr
+{
+ struct ifaddr i6a_ifa; /* protocol-independent info (32 bytes) */
+#define i6a_ifp i6a_ifa.ifa_ifp
+#ifdef KERNEL
+#define i6a_flags i6a_ifa.ifa_flags
+#endif
+
+ /* All sorts of INET6-specific junk, some of it, very similar to IP's
+ in_ifaddr. */
+
+ /* Put any subnetting, etc here. */
+
+ struct in6_ifaddr *i6a_next;
+ struct in6_ifnet *i6a_i6ifp; /* Pointer to IPv6 interface info */
+ struct sockaddr_in6 i6a_addr; /* Address. */
+ struct sockaddr_in6 i6a_dstaddr; /* Dest. if PPP link. */
+ struct sockaddr_in6 i6a_sockmask; /* Netmask. This is IPv6, so
+ there is no "subnet/net"
+ distinction. */
+
+ /*
+ * IPv6 addresses have lifetimes. Put in expiration information in
+ * here. A DEPRECATED address is still valid for inbound, but not for
+ * outbound. An EXPIRED address is invalid for both inbound and outbound,
+ * and should be put out of its misery (and our in6_ifaddr list) ASAP.
+ */
+ u_long i6a_preferred; /* Preferred lifetime. */
+ u_long i6a_expire; /* Expiration time. */
+ u_short i6a_preflen; /* Prefix length for link-locals
+ (in bits). */
+ u_short i6a_addrflags; /* Additional flags because
+ ifa_flags isn't big enough. */
+};
+
+#define I6A_SIN(i6a) (&(((struct in6_ifaddr *)(i6a))->i6a_addr))
+#define IS_EXPIRED(i6a) ((i6a->i6a_expire != 0 && \
+ i6a->i6a_expire < time.tv_sec))
+#define IS_DEPRECATED(i6a) ((i6a)->i6a_addrflags & I6AF_DEPRECATED)
+
+#define I6AF_LINKLOC 0x1 /* Link-local address. Saves the IS_IN6_LINKLOC
+ check. */
+#define I6AF_PERMANENT 0x2 /* Permanent address */
+#define I6AF_PREFIX 0x4 /* I am a, "prefix list entry," meaning that
+ the portion of the address inside the mask
+ is directly attached to the link. */
+#define I6AF_NOTSURE 0x8 /* I'm not sure if I'm allowed to be used yet.
+ This is designed for use with addresses
+ that haven't been verified as unique on a
+ link yet. */
+#define I6AF_DEPRECATED 0x10 /* The use of this address should be discouraged.
+ The address should not be used as a source
+ address for new communications. The address
+ is still valid for receiving packets. */
+
+/*
+ * IPv6 multicast structures and macros.
+ */
+
+struct in6_multi
+{
+ struct in6_multi *in6m_next; /* Ptr. to next one. */
+ struct in6_addr in6m_addr; /* Multicast address. */
+ struct ifnet *in6m_ifp; /* Pointer to interface. */
+ struct in6_ifnet *in6m_i6ifp; /* Back ptr. to IPv6 if info. */
+ uint in6m_refcount; /* Number of membership claims by
+ sockets. */
+ uint in6m_timer; /* IGMP membership report timer. */
+};
+
+#ifdef KERNEL
+/* General case IN6 multicast lookup. Can be optimized out in certain
+ places (like netinet6/ipv6_input.c ?). */
+
+#define IN6_LOOKUP_MULTI(addr,ifp,in6m) \
+{\
+ register struct in6_ifnet *i6ifp;\
+\
+ for (i6ifp=in6_ifnet; i6ifp != NULL && i6ifp->i6ifp_ifp != ifp;\
+ i6ifp=i6ifp->i6ifp_next)\
+ ;\
+ if (i6ifp == NULL)\
+ in6m=NULL;\
+ else\
+ for ((in6m) = i6ifp->i6ifp_multiaddrs;\
+ (in6m) != NULL && !IN6_ARE_ADDR_EQUAL(&(in6m)->in6m_addr,(addr));\
+ (in6m) = (in6m)->in6m_next) ;\
+}
+
+
+#define IN6_MCASTOPTS 0x2
+
+#define ETHER_MAP_IN6_MULTICAST(in6addr,enaddr) { \
+ (enaddr)[0] = 0x33; \
+ (enaddr)[1] = 0x33; \
+ (enaddr)[2] = in6addr.s6_addr[12]; \
+ (enaddr)[3] = in6addr.s6_addr[13]; \
+ (enaddr)[4] = in6addr.s6_addr[14]; \
+ (enaddr)[5] = in6addr.s6_addr[15]; \
+ }
+
+struct in6_ifnet
+{
+ struct in6_ifnet *i6ifp_next; /* Next in list. */
+ struct ifnet *i6ifp_ifp; /* Back pointer to actual interface. */
+ struct in6_multi *i6ifp_multiaddrs; /* Multicast addresses for this
+ interface. */
+ uint i6ifp_numaddrs; /* Number of IPv6 addresses on this
+ interface. */
+
+ /* Addrconf and ND variables will go here. */
+};
+
+#endif /* KERNEL */
+
+#endif /* _NETINET6_IN6_VAR_H */
diff --git a/sys/netinet6/ipv6.h b/sys/netinet6/ipv6.h
new file mode 100644
index 00000000000..311231a0e7d
--- /dev/null
+++ b/sys/netinet6/ipv6.h
@@ -0,0 +1,123 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+#ifndef _NETINET6_IPV6_H
+#define _NETINET6_IPV6_H 1
+
+#define IPV6VERSION 6
+
+/*
+ * Header structures.
+ */
+
+struct ipv6
+{
+ uint32_t ipv6_versfl; /* Version and flow label word. */
+
+ uint16_t ipv6_length; /* Datagram length (not including the length
+ of this header). */
+ uint8_t ipv6_nexthdr; /* Next header type. */
+ uint8_t ipv6_hoplimit; /* Hop limit. */
+
+ struct in6_addr ipv6_src; /* Source address. */
+ struct in6_addr ipv6_dst; /* Destination address. */
+};
+
+#if __linux__
+#include <endian.h>
+#else /* __linux__ */
+#include <machine/endian.h>
+#endif /* __linux__ */
+
+struct ipv6hdr {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t ipv6_priority:4; /* going away? */
+ uint8_t ipv6_version:4;
+ uint32_t ipv6_flowid:24;
+#elif BYTE_ORDER == BIG_ENDIAN
+ uint32_t ipv6_flowid:24;
+ uint8_t ipv6_priority:4; /* going away? */
+ uint8_t ipv6_version:4;
+#else
+#error "Don't know what endian to use."
+#endif
+ uint16_t ipv6_len;
+ uint8_t ipv6_nextheader;
+ uint8_t ipv6_hoplimit;
+ struct in6_addr ipv6_src; /* source address */
+ struct in6_addr ipv6_dst; /* destination address */
+};
+
+/*
+ * Macros and defines for header fields, and values thereof.
+ * Assume things are in host order for these three macros.
+ */
+
+#define IPV6_VERSION(h) ((h)->ipv6_versfl >> 28)
+#define IPV6_PRIORITY(h) (((h)->ipv6_versfl & 0x0f000000) >> 24)
+#define IPV6_FLOWID(h) ((h)->ipv6_versfl & 0x00ffffff)
+
+#define MAXHOPLIMIT 64
+#define IPV6_MINMTU 576
+
+/*
+ * Other IPv6 header definitions.
+ */
+
+/* Fragmentation header & macros for it. NOTE: Host order assumption. */
+
+struct ipv6_fraghdr
+{
+ uint8_t frag_nexthdr; /* Next header type. */
+ uint8_t frag_reserved;
+ uint16_t frag_bitsoffset; /* More bit and fragment offset. */
+ uint32_t frag_id; /* Fragment identifier. */
+};
+
+#define FRAG_MOREMASK 0x1
+#define FRAG_OFFMASK 0xFFF8
+#define FRAG_MORE_BIT(fh) ((fh)->frag_bitsoffset & FRAG_MOREMASK)
+#define FRAG_OFFSET(fh) ((fh)->frag_bitsoffset & FRAG_OFFMASK)
+
+/* Source routing header. Host order assumption for macros. */
+
+struct ipv6_srcroute0
+{
+ uint8_t i6sr_nexthdr; /* Next header type. */
+ uint8_t i6sr_len; /* RH len in 8-byte addrs, !incl this structure */
+ uint8_t i6sr_type; /* Routing type, should be 0 */
+ uint8_t i6sr_left; /* Segments left */
+ uint32_t i6sr_reserved; /* 8 bits of reserved padding. */
+};
+
+#define I6SR_BITMASK(i6sr) ((i6sr)->i6sr_reserved & 0xffffff)
+
+/* Options header. For "ignoreable" options. */
+
+struct ipv6_opthdr
+{
+ uint8_t oh_nexthdr; /* Next header type. */
+ uint8_t oh_extlen; /* Header extension length. */
+ uint8_t oh_data[6]; /* Option data, may be reserved for
+ alignment purposes. */
+};
+
+#define OPT_PAD1 0
+#define OPT_PADN 1
+#define OPT_JUMBO 194
+
+struct ipv6_option
+{
+ uint8_t opt_type; /* Option type. */
+ uint8_t opt_datalen; /* Option data length. */
+ uint8_t opt_data[1]; /* Option data. */
+};
+#endif /* _NETINET6_IPV6_H */
diff --git a/sys/netinet6/ipv6_addrconf.c b/sys/netinet6/ipv6_addrconf.c
new file mode 100644
index 00000000000..b818854c7a8
--- /dev/null
+++ b/sys/netinet6/ipv6_addrconf.c
@@ -0,0 +1,241 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+
+#include <sys/param.h>
+#include <sys/systm.h>
+/*#include <sys/ioctl.h> */
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+
+#include <netinet6/in6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ipv6.h>
+#include <netinet6/ipv6_var.h>
+#include <netinet6/ipv6_icmp.h>
+#include <netinet6/ipv6_addrconf.h>
+
+#include <sys/debug.h>
+
+/*
+ * External Globals
+ */
+
+extern struct in6_ifaddr *in6_ifaddr;
+extern u_long v6d_retranstime;
+extern int ipv6rsolicit;
+
+void send_nsolicit __P((struct rtentry *, struct ifnet *, struct in6_addr *, int));
+struct mbuf *get_discov_cluster __P((void));
+
+static void send_rsolicit __P((struct ifnet *));
+
+/*----------------------------------------------------------------------
+ * Initialize addrconf.
+ ----------------------------------------------------------------------*/
+
+void
+addrconf_init()
+{
+ timeout(addrconf_timer,NULL,v6d_retranstime * hz);
+}
+
+/*----------------------------------------------------------------------
+ * Send a router solicitation out a certain interface.
+ ----------------------------------------------------------------------*/
+
+static void
+send_rsolicit(ifp)
+ struct ifnet *ifp;
+{
+ struct mbuf *solicit = NULL;
+ struct ipv6 *header;
+ struct ipv6_icmp *icmp;
+ struct ipv6_moptions i6mo,*i6mop = NULL;
+ struct in6_ifaddr *i6a;
+
+ if ((solicit = get_discov_cluster()) == NULL)
+ {
+ DPRINTF(IDL_ERROR, ("Can't allocate mbuf in send_gsolicit().\n"));
+ return;
+ }
+ header = mtod(solicit,struct ipv6 *);
+ icmp = (struct ipv6_icmp *)(header + 1);/* I want the bytes after the hdr. */
+
+ bzero(&i6mo,sizeof(struct ipv6_moptions));
+ i6mo.i6mo_multicast_ifp = ifp;
+ i6mo.i6mo_multicast_ttl = 255;
+ i6mop = &i6mo;
+ /* Find source link-local or use unspec. */
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ if (i6a->i6a_ifp == ifp && (i6a->i6a_addrflags & I6AF_LINKLOC) &&
+ !(i6a->i6a_addrflags & I6AF_NOTSURE))
+ break;
+
+ if (i6a == NULL)
+ header->ipv6_src = in6addr_any;
+ else
+ header->ipv6_src = i6a->i6a_addr.sin6_addr;
+
+ header->ipv6_dst.in6a_words[0] = htonl(0xff020000);
+ header->ipv6_dst.in6a_words[1] = 0;
+ header->ipv6_dst.in6a_words[2] = 0;
+ header->ipv6_dst.in6a_words[3] = htonl(2);
+
+ header->ipv6_versfl = htonl(0x6f000000);
+ header->ipv6_hoplimit = 255; /* Guaranteed to be intra-link if arrives with
+ 255. */
+ header->ipv6_nexthdr = IPPROTO_ICMPV6;
+ header->ipv6_length = ICMPV6_RSOLMINLEN; /* For now. */
+ icmp->icmp_type = ICMPV6_ROUTERSOL;
+ icmp->icmp_code = 0;
+ icmp->icmp_unused = 0;
+ icmp->icmp_cksum = 0;
+
+ /*
+ * For now, just let ND run its normal course and don't include the link
+ * extension.
+ */
+
+ solicit->m_len = solicit->m_pkthdr.len =
+ header->ipv6_length + sizeof(*header);
+ icmp->icmp_cksum = in6_cksum(solicit,IPPROTO_ICMPV6,header->ipv6_length,
+ sizeof(struct ipv6));
+ /*
+ * NOTE: The solicit mbuf chain will have a NULL instead of a valid
+ * socket ptr. When ipv6_output() calls ipsec_output_policy(),
+ * this socket ptr will STILL be NULL. Sooo, the security
+ * policy on outbound packets from here will == system security
+ * level (set in ipsec_init() of netinet6/ipsec.c). If your
+ * system security level is paranoid, then you won't move packets
+ * unless you have _preloaded_ keys for at least the ND addresses.
+ * - danmcd rja
+ */
+ ipv6_output(solicit, NULL, IPV6_RAWOUTPUT, i6mop, NULL, NULL);
+}
+
+/*----------------------------------------------------------------------
+ * Scan list if in6_ifaddrs and see if any are expired (or can go to
+ * being unique).
+ ----------------------------------------------------------------------*/
+/* XXX - This function doesn't appear to ever actually remove addresses... */
+void
+addrconf_timer(whocares)
+ void *whocares;
+{
+ struct in6_ifaddr *i6a = in6_ifaddr;
+ int s = splnet();
+
+ while (i6a != NULL)
+ {
+ /*
+ * Scan address list for all sorts of neat stuff. Also, in6_ifaddr
+ * may be a "prefix list" as well. This will be difficult when
+ * an router advert. advertises an on-link prefix, but I don't have
+ * (for whatever reason) an address on that link.
+ */
+#ifdef __FreeBSD__
+ if (i6a->i6a_preferred && i6a->i6a_preferred <= time_second)
+#else /* __FreeBSD__ */
+ if (i6a->i6a_preferred && i6a->i6a_preferred <= time.tv_sec)
+#endif /* __FreeBSD__ */
+ {
+ i6a->i6a_addrflags |= I6AF_DEPRECATED;
+ DPRINTF(IDL_EVENT,("Address has been deprecated.\n"));
+ }
+#ifdef __FreeBSD__
+ if (i6a->i6a_expire && i6a->i6a_expire <= time_second)
+#else /* __FreeBSD__ */
+ if (i6a->i6a_expire && i6a->i6a_expire <= time.tv_sec)
+#endif /* __FreeBSD__ */
+ if (i6a->i6a_addrflags & I6AF_NOTSURE)
+ {
+ DPRINTF(IDL_FINISHED, ("Address appears to be unique.\n"));
+ i6a->i6a_addrflags &= ~I6AF_NOTSURE;
+ /*
+ * From what I can tell, addrs that survive DAD are
+ * permanent. I won't mark as permanent, but I will zero
+ * expiration for now.
+ *
+ * If this is a link-local address, it may be a good idea to
+ * send a router solicit. (But only if I'm a host.)
+ */
+ if (ipv6rsolicit && IN6_IS_ADDR_LINKLOCAL(&i6a->i6a_addr.sin6_addr))
+ send_rsolicit(i6a->i6a_ifp);
+
+ i6a->i6a_expire = 0;
+ i6a->i6a_preferred = 0;
+ }
+ else
+ {
+ /*
+ * Do address deletion, and nuke any routes, pcb's, etc.
+ * that use this address.
+ *
+ * As an implementation note, it's probably more likely than
+ * not that addresses that get deprecated (see above) will be
+ * moved off the master list, as that keeps them away from
+ * some things. This is something we couldn't implement in time,
+ * however.
+ */
+ }
+ i6a = i6a->i6a_next;
+ }
+ timeout(addrconf_timer,NULL,v6d_retranstime * hz);
+ splx(s);
+}
+
+/*----------------------------------------------------------------------
+ * Send multicast solicit for this address from all 0's. Set timer such
+ * that if address is still in in6_ifaddr list, it's good.
+ ----------------------------------------------------------------------*/
+
+void
+addrconf_dad(i6a)
+ struct in6_ifaddr *i6a;
+{
+ int s;
+ struct rtentry dummy;
+
+ DPRINTF(IDL_GROSS_EVENT,("Sending DAD solicit!\n"));
+ s = splnet();
+ rt_key(&dummy) = i6a->i6a_ifa.ifa_addr;
+ dummy.rt_gateway = NULL;
+ /*
+ * Set i6a flags and expirations such that it is NOT SURE about uniqueness.
+ *
+ * What about random delay?
+ */
+ i6a->i6a_addrflags |= I6AF_NOTSURE; /* Might be done already. */
+ i6a->i6a_preferred = 0;
+#ifdef __FreeBSD__
+ i6a->i6a_expire = time_second + v6d_retranstime;
+#else /* __FreeBSD__ */
+ i6a->i6a_expire = time.tv_sec + v6d_retranstime;
+#endif /* __FreeBSD__ */
+ splx(s);
+ /*
+ * It would be nice if I delayed a random amount of time here.
+ */
+ send_nsolicit(&dummy, i6a->i6a_ifp, (struct in6_addr *)&in6addr_any, 1);
+}
diff --git a/sys/netinet6/ipv6_addrconf.h b/sys/netinet6/ipv6_addrconf.h
new file mode 100644
index 00000000000..26f6bf76034
--- /dev/null
+++ b/sys/netinet6/ipv6_addrconf.h
@@ -0,0 +1,22 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+#ifndef _NETINET6_IPV6_ADDRCONF_H
+#define _NETINET6_IPV6_ADDRCONF_H 1
+
+/*
+ * Function definitions.
+ */
+void addrconf_init __P((void));
+void addrconf_timer __P((void *));
+void addrconf_dad __P((struct in6_ifaddr *));
+
+#endif /* _NETINET6_IPV6_ADDRCONF_H */
diff --git a/sys/netinet6/ipv6_discovery.c b/sys/netinet6/ipv6_discovery.c
new file mode 100644
index 00000000000..6614a18e2aa
--- /dev/null
+++ b/sys/netinet6/ipv6_discovery.c
@@ -0,0 +1,3285 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/ioctl.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/radix.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#include <netinet6/in6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ipv6.h>
+#include <netinet6/ipv6_var.h>
+#include <netinet6/ipv6_icmp.h>
+
+#include <sys/debug.h>
+
+#if __OpenBSD__ && defined(NRL_IPSEC)
+#define IPSEC 1
+#endif /* __OpenBSD__ && defined(NRL_IPSEC) */
+
+#ifdef IPSEC
+#include <netsec/ipsec.h>
+#endif /* IPSEC */
+
+#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
+#include <machine/pcpu.h>
+#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+
+#ifdef IPSEC
+#include <net/netproc.h>
+#include <net/netproc_var.h>
+
+extern struct netproc_security fixedencrypt;
+extern struct netproc_auth fixedauth;
+#endif /* IPSEC */
+
+/*
+ * Globals (and forward function declarations).
+ */
+
+struct discq dqhead;
+struct v6router defrtr,nondefrtr;
+struct sockaddr_in6 in6_allones;
+
+void ipv6_nsolicit __P((struct ifnet *, struct mbuf *, struct rtentry *));
+void ipv6_uni_nsolicit __P((struct rtentry *));
+
+/*
+ * These should be sysctl-tweakable. See ipv6_var.h for units.
+ * They share the common prefix v6d_.
+ */
+
+u_long v6d_maxinitrai = MAX_INITIAL_RTR_ADVERT_INTERVAL;
+u_long v6d_maxinitra = MAX_INITIAL_RTR_ADVERTISEMENTS;
+u_long v6d_maxrtrdel = MAX_RTR_RESPONSE_DELAY;
+
+u_long v6d_maxrtsoldel = MAX_RTR_SOLICITATION_DELAY;
+u_long v6d_rtsolint = RTR_SOLICITATION_INTERVAL;
+u_long v6d_maxrtsol = MAX_RTR_SOLICITATIONS;
+
+u_long v6d_maxmcastsol = MAX_MULTICAST_SOLICIT;
+u_long v6d_maxucastsol = MAX_UNICAST_SOLICIT;
+u_long v6d_maxacastdel = MAX_ANYCAST_DELAY_TIME;
+u_long v6d_maxnadv = MAX_NEIGHBOR_ADVERTISEMENTS;
+u_long v6d_minnadvint = MIN_NEIGHBOR_ADVERT_INTERVAL;
+u_long v6d_reachtime = REACHABLE_TIME;
+u_long v6d_retranstime = RETRANS_TIMER;
+u_long v6d_delfirstprobe= DELAY_FIRST_PROBE_TIME;
+/* Need to somehow define random factors. */
+
+u_long v6d_nexthopclean = NEXTHOP_CLEAN_INTERVAL;
+u_long v6d_toolong = NEXTHOP_CLEAN_INTERVAL >> 1; /* Half the cleaning
+ interval. */
+
+u_long v6d_down = REJECT_TIMER; /* Dead node routes are marked REJECT
+ for this amt. */
+
+/*
+ * External globals.
+ */
+
+extern struct in6_ifaddr *in6_ifaddr;
+extern struct in6_ifnet *in6_ifnet;
+
+extern int ipv6forwarding;
+extern int ipv6rsolicit;
+extern int ipv6_defhoplmt;
+
+#if __NetBSD__ || __OpenBSD__
+#define if_name if_xname
+#endif /* __NetBSD__ || __OpenBSD__ */
+
+/*
+ * General notes:
+ *
+ * Currently this module does not support encryption/authentication
+ * of ND messages. That support is probably needed in some environments.
+ * NRL intends to add it in a later release.
+ *
+ * Please keep this in mind.
+ * danmcd rja
+ */
+int ipv6_enabled __P((struct ifnet *));
+
+void ipv6_neighbor_timer __P((void *));
+int ipv6_clean_nexthop __P((struct radix_node *, void *));
+void ipv6_nexthop_timer __P((void *));
+void ipv6_discovery_init __P((void));
+struct mbuf *get_discov_cluster __P((void));
+void send_nsolicit __P((struct rtentry *, struct ifnet *, struct in6_addr *, int));
+struct mbuf *ipv6_discov_pullup __P((struct mbuf *, int));
+struct v6router *ipv6_add_defrouter_advert __P((struct in6_addr *, int lifetime, struct ifnet *));
+struct v6router *ipv6_add_defrouter_rtrequest __P((struct rtentry *));
+void ipv6_nadvert __P((struct in6_ifaddr *, struct ifnet *, struct rtentry *, uint32_t));
+static int update_defrouter __P((struct v6router *, struct ipv6_icmp *));
+int ipv6_delete_defrouter __P((struct v6router *));
+static void prefix_concat __P((struct in6_addr *, struct in6_addr *, struct in6_addr *));
+struct rtentry *ipv6_new_neighbor __P((struct sockaddr_in6 *, struct ifnet *));
+int ipv6_discov_resolve __P((struct ifnet *, struct rtentry *, struct mbuf *, struct sockaddr *, u_char *));
+void tunnel_parent __P((struct rtentry *));
+void tunnel_parent_clean __P((struct rtentry *));
+void tunnel_child __P((struct rtentry *));
+void tunnel_child_clean __P((struct rtentry *));
+
+/*
+ * Functions and macros.
+ */
+
+/*----------------------------------------------------------------------
+ void padto(u_char *ptr, u_long mask, int alignto)
+
+ Examples: padto(foo, 0x3, 0) will get 4-byte alignment
+ padto(foo, 0x7, 6) will get to 2 bytes before an 8-byte
+ boundary.
+
+ Padding generated will be most efficient.
+
+ This is now deprecated for discovery, but it still has
+ uses in generating IPv6 options (the option bags).
+
+----------------------------------------------------------------------*/
+#define padto(ptr,mask,alignto)\
+{\
+ int difference = 0;\
+ \
+ if (((u_long)(ptr) & (mask)) != (alignto))\
+ {\
+ difference = (((u_long)(ptr) & ~(mask)) + (alignto)) - (long)(ptr);\
+ if (difference < 0)\
+ difference += ((mask) +1);\
+ if (difference == 1)\
+ (ptr)[0] = EXT_PAD1;\
+ else\
+ {\
+ (ptr)[0] = EXT_PADN;\
+ (ptr)[1] = (difference) - 2; /* difference >= 2 always here. */\
+ bzero((ptr)+2,(ptr)[1]);\
+ }\
+ (ptr) += difference;\
+ }\
+}
+
+
+/*----------------------------------------------------------------------
+ * ipv6_neighbor_timer():
+ * Scan neighbor list and resend (1) mcast ND solicits for all
+ * neighbors in INCOMPLETE state and (2) unicast ND solicits
+ * for all neighbors in PROBE state
+ ----------------------------------------------------------------------*/
+void
+ipv6_neighbor_timer(whocares)
+ void *whocares;
+{
+ struct discq *dq;
+ int s = splnet();
+ struct rtentry *rt;
+
+ /*
+ * 1. Scan all neighbors (go down dq list) and retransmit those which
+ * are still in INCOMPLETE.
+ * 2. Also, those in PROBE state should have unicast solicits sent.
+ * 3. ...
+ */
+
+ dq = dqhead.dq_next;
+ while (dq != &dqhead)
+ {
+ struct discq *current = dq;
+
+ dq = dq->dq_next; /* Before any rtrequest */
+
+ rt = current->dq_rt;
+#ifdef __FreeBSD__
+ if (rt->rt_rmx.rmx_expire <= time_second)
+#else /* __FreeBSD__ */
+ if (rt->rt_rmx.rmx_expire <= time.tv_sec)
+#endif /* __FreeBSD__ */
+ {
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)rt->rt_gateway;
+
+ /*
+ * Gotta do SOMETHING...
+ */
+ if (sdl->sdl_alen == 0) {
+ /* If RTF_REJECT, then delete... */
+ if (rt->rt_flags & RTF_REJECT)
+ {
+ DPRINTF(GROSSEVENT,
+ ("neighbor_timer() deleting rejected route\n"));
+ rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);
+ continue;
+ }
+
+ /* Am in INCOMPLETE mode... */
+
+ if (current->dq_unanswered >= 0) /* If not newly created... */
+ if (current->dq_unanswered >= v6d_maxmcastsol)
+ {
+ /* Dead node. For now, just delete.
+ May want to do an RTF_REJECT entry.
+ May also want to send ICMP errors on enqueued packet(s).
+ May also want to do routing cleanup if a defult router.
+ XXX */
+ DPRINTF(GROSSEVENT,("Deleting dead node... "));
+ rt->rt_flags |= RTF_REJECT;
+ /*
+ * If there's no ifa, add an aribitrary one.
+ * (May be unnecessary if rtfree() ifa != NULL check
+ * is in place.)
+ *
+ * It turns out that adding a dummy ifa causes
+ * problems for subsequent packets that successfully
+ * rtalloc-ed this route (since rtalloc1 doesn't
+ * check if a route is marked RTF_REJECT... the
+ * check is done at a further down the stack, e.g.,
+ * ether_output(). This causes ipv6_output to
+ * call ipv6_nsolicit (with an incomplete route that
+ * has an ifa but no ifp). ipv6_nsolicit then uses
+ * the address of the dummy ifa as the source address
+ * to be used in send_nsolicit which may panic
+ * if the address turns out to not have a link-local
+ * address (e.g. loopback).
+ *
+ * The solution, for now, is to check for RTF_REJECT
+ * in ipv6_nsolicit. This may change in the future.
+ */
+ if (rt->rt_ifa == NULL)
+ {
+ rt->rt_ifa = (struct ifaddr *)in6_ifaddr;
+ rt->rt_ifa->ifa_refcnt++;
+ }
+#ifdef __FreeBSD__
+ rt->rt_rmx.rmx_expire = time_second + v6d_down;
+#else /* __FreeBSD__ */
+ rt->rt_rmx.rmx_expire = time.tv_sec + v6d_down;
+#endif /* __FreeBSD__ */
+
+ /* ICMPv6 error part. */
+ while (current->dq_queue != NULL)
+ {
+ struct mbuf *badpack = current->dq_queue;
+
+ current->dq_queue = badpack->m_nextpkt;
+ /* Set rcvif for icmp_error, is this good? */
+ badpack->m_pkthdr.rcvif = current->dq_rt->rt_ifp;
+ ipv6_icmp_error(badpack,ICMPV6_UNREACH,
+ ICMPV6_UNREACH_ADDRESS,0);
+ }
+ }
+ else
+ {
+ /* Send another solicit. The ipv6_nsolicit call will
+ ensure the rate-limitation. Send the queued packet
+ to ipv6_nsolicit for source address determination. */
+ ipv6_nsolicit(rt->rt_ifp,NULL,rt);
+ }
+ }
+ else
+ /*
+ * Am in either PROBE mode or REACHABLE mode.
+ *
+ * Either way, that will (in the case of REACHABLE->PROBE)
+ * or might (in the case of PROBE->INCOMPLETE) change.
+ */
+ if (current->dq_unanswered >= 0) /* PROBE */
+ if (current->dq_unanswered >= v6d_maxucastsol)
+ /* PROBE -> INCOMPLETE */
+ if (rt->rt_refcnt > 0)
+ {
+ /* The code below is an optimization for the case
+ * when someone is using this route :
+ * - defer actual deletion until mcast solicits fail.
+ *
+ * Q: Do we still want to do this?
+ */
+ sdl->sdl_alen = 0;
+ current->dq_unanswered = 0;
+#ifdef __FreeBSD__
+ rt->rt_rmx.rmx_expire = time_second + v6d_retranstime;
+#else /* __FreeBSD__ */
+ rt->rt_rmx.rmx_expire = time.tv_sec + v6d_retranstime;
+#endif /* __FreeBSD__ */
+ }
+ else
+ {
+ /*
+ * Unicast probes failed and no one is using this route.
+ * Delete and let address resolution take its course.
+ *
+ * At this point, I have an ifa, so I don't need to add
+ * one.
+ */
+ rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);
+ }
+ else
+ {
+ /* Retry PROBE */
+ ipv6_uni_nsolicit(rt);
+ }
+ else
+ {
+ /*
+ * Do nothing if REACHABLE expires. Only on output,
+ * BUT... some of these might hang around for too long.
+ * See ipv6_clean_nexthop for details on a solution.
+ *
+ * I am now in the STALE state.
+ */
+ }
+ }
+ }
+
+ timeout(ipv6_neighbor_timer,NULL,v6d_retranstime * hz);
+ splx(s);
+}
+
+/*----------------------------------------------------------------------
+ * ipv6_clean_nexthop:
+ * Delete requested route, if either the route is both GATEWAY and HOST
+ * and not held (or the caller REALLY requests it).
+ ----------------------------------------------------------------------*/
+int
+ipv6_clean_nexthop(rn, arg)
+ struct radix_node *rn;
+ void *arg;
+{
+ struct rtentry *rt = (struct rtentry *)rn;
+#ifdef __alpha__
+ long hardcore = (long)arg;
+#else
+ int hardcore = (int)arg;
+#endif
+ /* If hardcore is not zero, then the caller REALLY insists that we
+ delete the route. A drain function would call this with hardcore != 0. */
+
+ DPRINTF(GROSSEVENT,("Entering ipv6_clean_nexthop... "));
+ if (!(rt->rt_flags & RTF_HOST) ||
+ !(rt->rt_flags & (RTF_LLINFO|RTF_TUNNEL|RTF_GATEWAY)) ||
+ /* Keep the static host routes; unless told to delete? */
+ ( (rt->rt_flags & (RTF_STATIC)) && !hardcore) ||
+ (!hardcore && rt->rt_refcnt > 0))
+ {
+ /*
+ * Unless asked (i.e. a hardcore clean :), don't delete held routes.
+ * Only delete host routes (that aren't my own addresses) this way.
+ */
+
+ DPRINTF(GROSSEVENT,("not deleting.\n"));
+ return 0;
+ }
+
+ if ((rt->rt_flags & RTF_LLINFO) && rt->rt_refcnt == 0)
+ {
+ struct discq *dq = (struct discq *)rt->rt_llinfo;
+ /*
+ * This is a neighbor cache entry. Delete only if not held, and
+ * if in STALE state (i.e. pre-PROBE) for too long.
+ */
+#ifdef __FreeBSD__
+ if (rt->rt_rmx.rmx_expire + v6d_toolong >= time_second &&
+#else /* __FreeBSD__ */
+ if (rt->rt_rmx.rmx_expire + v6d_toolong >= time.tv_sec &&
+#endif /* __FreeBSD__ */
+ dq->dq_unanswered <= 0)
+ return 0;
+ /*
+ * In case clean_nexthop catches one of these non-determinate
+ * neighbor entries...
+ * (May be unnecessary if rtfree() ifa != NULL check is in place.)
+ */
+ if (rt->rt_ifa == NULL)
+ {
+ rt->rt_ifa = (struct ifaddr *)in6_ifaddr;
+ rt->rt_ifa->ifa_refcnt++;
+ }
+ }
+
+ /*
+ * At this point, the route is RTF_HOST, and is either a
+ * force or a legitimate node to be cleaned. Call RTM_DELETE...
+ */
+ DPRINTF(GROSSEVENT,("deleting.\n"));
+
+ return rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);
+}
+
+/*----------------------------------------------------------------------
+ * ipv6_nexthop_timer():
+ * Keeps routing table from getting too filled up with off-net host
+ * routes.
+ *
+ * NOTES:
+ * Later might want to put some intelligence in here like __FreeBSD__ does,
+ * but for now just do an rnh->rnh_walktree() every so often.
+ *
+ * A smarter function might:
+ *
+ * - Keep a count of how many off-net host routes we have.
+ * - Maintain upper bounds on said count.
+ * - Avoid deletion if said count is low.
+ * - Etc.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_nexthop_timer(whocares)
+ void *whocares;
+{
+ struct radix_node_head *rnh = rt_tables[AF_INET6];
+ int s, rc;
+
+ DPRINTF(IDL_EVENT,("Entering ipv6_nexthop_timer().\n"));
+ s = splnet();
+ rc = rnh->rnh_walktree(rnh, ipv6_clean_nexthop, (void *)0);
+ splx(s);
+
+ DDO(IDL_ERROR,if (rc != 0)\
+ printf("walktree rc is %d in nexthop_timer.\n", rc));
+
+ timeout(ipv6_nexthop_timer,NULL,v6d_nexthopclean * hz);
+}
+
+/*----------------------------------------------------------------------
+ * ipv6_discovery_init():
+ * Initializes ND data structures.
+ ----------------------------------------------------------------------*/
+void
+ipv6_discovery_init()
+{
+ dqhead.dq_next = dqhead.dq_prev = &dqhead;
+ defrtr.v6r_next = defrtr.v6r_prev = &defrtr;
+ nondefrtr.v6r_next = nondefrtr.v6r_prev = &nondefrtr;
+
+ in6_allones.sin6_family = AF_INET6;
+ in6_allones.sin6_len = sizeof(in6_allones);
+
+ /* Other fields are bzeroed. */
+ in6_allones.sin6_addr.in6a_words[0] = 0xffffffff;
+ in6_allones.sin6_addr.in6a_words[1] = 0xffffffff;
+ in6_allones.sin6_addr.in6a_words[2] = 0xffffffff;
+ in6_allones.sin6_addr.in6a_words[3] = 0xffffffff;
+
+ timeout(ipv6_nexthop_timer,NULL,hz);
+ timeout(ipv6_neighbor_timer,NULL,hz);
+}
+
+/*----------------------------------------------------------------------
+ * get_discov_cluster():
+ * Allocates a single-cluster mbuf and sets it up for use by ND.
+ *
+ ----------------------------------------------------------------------*/
+
+struct mbuf *
+get_discov_cluster()
+{
+ struct mbuf *rc;
+
+ MGET(rc, M_DONTWAIT, MT_HEADER);
+ if (rc != NULL)
+ {
+ MCLGET(rc,M_DONTWAIT);
+ if ((rc->m_flags & M_EXT) == 0)
+ {
+ m_free(rc);
+ rc = NULL;
+ }
+ }
+
+ /* Make it a pkthdr appropriately. */
+ if (rc)
+ {
+ rc->m_flags |= M_PKTHDR;
+ rc->m_pkthdr.rcvif = NULL;
+ rc->m_pkthdr.len = 0;
+ rc->m_len = 0;
+ }
+ return rc;
+}
+
+/*----------------------------------------------------------------------
+ * send_nsolicit():
+ * Send a neighbor solicit for destination in route entry rt,
+ * across interface pointed by ifp. Use source address in src (see below),
+ * and either unicast or multicast depending on the mcast flag.
+ *
+ * NOTES: The entry pointed to by rt MUST exist.
+ * If the caller has to, it may cruft up a dummy rtentry with at least
+ * a valid rt_key() for the neighbor's IPv6 address, and maybe
+ * a dummy rt_gateway.
+ *
+ * If src points to 0::0 address, set M_DAD flag so ipv6_output() doesn't
+ * try and fill in a source address.
+ ----------------------------------------------------------------------*/
+void
+send_nsolicit(rt,ifp,src,mcast)
+ struct rtentry *rt;
+ struct ifnet *ifp;
+ struct in6_addr *src; /* Source address of invoking packet... */
+ int mcast;
+{
+ struct mbuf *solicit = NULL;
+ struct ipv6 *header;
+ struct ipv6_icmp *icmp;
+ struct ipv6_moptions i6mo,*i6mop = NULL;
+ struct in6_ifaddr *i6a;
+ struct sockaddr_in6 *neighbor = (struct sockaddr_in6 *)rt_key(rt);
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)rt->rt_gateway;
+
+ DPRINTF(IDL_EVENT,("Entering send_nsolicit with\n"));
+ DPRINTF(IDL_EVENT,("rt=0x%lx\n",(unsigned long)rt));
+ DPRINTF(IDL_EVENT,("ifp:\n"));
+ DDO(IDL_EVENT,dump_ifp(ifp));
+ DPRINTF(IDL_EVENT,("src:\n"));
+ DDO(IDL_EVENT,dump_in6_addr(src));
+ DPRINTF(IDL_EVENT,("mcast=%d\n",mcast));
+
+ if ((solicit = get_discov_cluster()) == NULL)
+ {
+ DPRINTF(IDL_ERROR, ("Can't allocate mbuf in send_gsolicit().\n"));
+ return;
+ }
+
+ header = mtod(solicit,struct ipv6 *);
+ icmp = (struct ipv6_icmp *)(header + 1);/* I want the bytes after the hdr. */
+
+ if (mcast)
+ {
+ bzero(&i6mo,sizeof(struct ipv6_moptions));
+ i6mo.i6mo_multicast_ifp = ifp;
+ i6mo.i6mo_multicast_ttl = 255; /* Must set, hoplimit is otherwise
+ overridden! */
+ i6mop = &i6mo;
+
+ header->ipv6_dst.in6a_words[0] = htonl(0xff020000);
+ header->ipv6_dst.in6a_words[1] = 0;
+ header->ipv6_dst.in6a_words[2] = htonl(1);
+ header->ipv6_dst.in6a_words[3] = neighbor->sin6_addr.in6a_words[3] | htonl(0xff000000);
+ }
+ else
+ {
+ if (sdl == NULL || sdl->sdl_alen == 0)
+ {
+ DPRINTF(IDL_ERROR, ("Can't unicast if I don't know address.\n"));
+ m_freem(solicit);
+ return;
+ }
+ header->ipv6_dst = neighbor->sin6_addr;
+ /* If rmx_expire is not already set, set it to avoid chicken-egg loop. */
+ }
+
+ header->ipv6_versfl = htonl(0x6f000000);
+ header->ipv6_hoplimit = 255; /* Guaranteed to be intra-link if arrives with
+ 255. */
+ header->ipv6_nexthdr = IPPROTO_ICMPV6;
+ header->ipv6_length = ICMPV6_NSOLMINLEN; /* For now. */
+
+ /*
+ * Now find source address for solicit packet.
+ *
+ * Rules on src address selection:
+ * if NULL find link-local for i/f.
+ * If no link-local, then use unspec source.
+ * (If unspec source and !mcast, fail)
+ * if UNSPEC src
+ * if not mcast, fail.
+ * if a real address, sanity check to see if it's indeed my address.
+ * Additionally check to see if it matches the
+ * outbound ifp requested.
+ */
+
+ if (src == NULL)
+ {
+ /* Find source link-local or use unspec. */
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ if (i6a->i6a_ifp == ifp && (i6a->i6a_addrflags & I6AF_LINKLOC) &&
+ !(i6a->i6a_addrflags & I6AF_NOTSURE))
+ break;
+ if (i6a == NULL) {
+ if (mcast)
+ header->ipv6_src = in6addr_any;
+ else {
+ DPRINTF(IDL_ERROR, ("send_nsolicit: Unicast solicit w/o any known source address.\n"));
+ m_freem(solicit);
+ return;
+ }
+ } else
+ header->ipv6_src = i6a->i6a_addr.sin6_addr;
+ }
+ else if (IN6_IS_ADDR_UNSPECIFIED(src))
+ if (!mcast) {
+ DPRINTF(IDL_ERROR, ("send_nsolicit: Unicast DAD solicit.\n"));
+ m_freem(solicit);
+ return;
+ } else {
+ DPRINTF(GROSSEVENT, ("Sending DAD solicit.\n"));
+ solicit->m_flags |= M_DAD;
+ header->ipv6_src = in6addr_any;
+ }
+ else
+ {
+ struct in6_ifaddr *llsave = NULL;
+
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ {
+ /*
+ * Might want to put llsave code where it's actually needed.
+ * (i.e. take it out of this loop and put it in i6a == NULL case.)
+ */
+ if (i6a->i6a_ifp == ifp && IN6_IS_ADDR_LINKLOCAL(&i6a->i6a_addr.sin6_addr))
+ llsave = i6a;
+ if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, src))
+ break;
+ }
+ if (i6a == NULL)
+ {
+ /*
+ * This path is entered by a router forwarding packets to a yet
+ * undiscovered neighbor.
+ */
+#ifdef DIAGNOSTIC
+ if (llsave == NULL)
+ panic("No link-local for this address.");
+#endif /* DIAGNOSTIC */
+ header->ipv6_src = llsave->i6a_addr.sin6_addr;
+ }
+ else if (i6a->i6a_ifp != ifp)
+ {
+ /*
+ * Q: Is this a reason to be panicking?
+ * A: For now, no.
+ */
+ DDO(IDL_ERROR,\
+ printf("WARNING: Src addr fubar Addr, ifp, i6a: ");\
+ dump_in6_addr(src);dump_ifp(ifp);\
+ dump_ifa((struct ifaddr *)i6a));
+#ifdef DIAGNOSTIC
+ if (llsave == NULL)
+ panic("No link-local for this address.");
+#endif /* DIAGNOSTIC */
+ header->ipv6_src = llsave->i6a_addr.sin6_addr;
+ }
+ else header->ipv6_src = *src;
+ }
+
+ /* Have source address, now create outbound packet */
+ icmp->icmp_type = ICMPV6_NEIGHBORSOL;
+ icmp->icmp_code = 0;
+ icmp->icmp_unused = 0;
+ icmp->icmp_cksum = 0;
+ icmp->icmp_nsoltarg = neighbor->sin6_addr;
+
+ /*
+ * Put ND extensions here, if any.
+ * This next code fragment could be its own function if there
+ * were enough callers of the fragment to make that sensible.
+ *
+ * This might also want to be its own function because of variations in
+ * link (ifp) type.
+ */
+ if (ifp->if_addrlen != 0 && !IN6_IS_ADDR_UNSPECIFIED(&header->ipv6_src))
+ {
+ struct icmp_exthdr *ext = (struct icmp_exthdr *)&(icmp->icmp_nsolext);
+ struct ifaddr *ifa;
+ struct sockaddr_dl *lsdl;
+
+ ext->ext_id = EXT_SOURCELINK;
+
+ switch (ifp->if_type)
+ {
+ case IFT_ETHER:
+ ext->ext_length = 1 + ((ifp->if_addrlen + 1) >> 3);
+#ifdef __FreeBSD__
+ for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ if (ifa->ifa_addr->sa_family == AF_LINK)
+ break;
+ if (ifa == NULL)
+ {
+ DPRINTF(IDL_ERROR, ("Can't find link addr. in nsolicit().\n"));
+ m_freem(solicit);
+ return;
+ }
+ lsdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ bzero(ext->ext_data,ext->ext_length * 8 - 2);
+ bcopy(LLADDR(lsdl),ext->ext_data,ifp->if_addrlen);
+ break;
+ default:
+ DPRINTF(IDL_ERROR,("DANGER: unk. link type for n. sol.\n"));
+ break;
+ }
+
+ header->ipv6_length += ext->ext_length <<3;
+ }
+
+ solicit->m_len = solicit->m_pkthdr.len =
+ header->ipv6_length + sizeof(*header);
+ icmp->icmp_cksum = in6_cksum(solicit,IPPROTO_ICMPV6,header->ipv6_length,
+ sizeof(struct ipv6));
+
+ /*
+ * NOTE: We pass in a NULL instead of a valid
+ * socket ptr. When ipv6_output() calls ipsec_output_policy(),
+ * this socket ptr will STILL be NULL. Sooo, the security
+ * policy on outbound packets from here will == system security
+ * level (set in ipsec_init()). If your
+ * system security level is paranoid, then you won't move packets
+ * unless you have _preloaded_ keys for at least the ND addresses.
+ * - danmcd rja
+ */
+
+ ipv6_output(solicit, NULL, IPV6_RAWOUTPUT, i6mop, NULL, NULL);
+}
+
+/*----------------------------------------------------------------------
+ * ipv6_nsolicit:
+ * Send an IPv6 Neighbor Solicit message
+ *
+ * NOTES:
+ * State checking is needed so that a neighbor can be declared unreachable.
+ *
+ * newrt == NULL iff this is a virgin packet with no known i/f,
+ * otherwise valid newrt MUST be passed in.
+ *
+ * If ifp == NULL, ipv6_nsolicit() executes potentially multihomed code.
+ * (For now, I guess I should pick an arbitrary interface. For single-homed
+ * nodes, this is the optimal behavior.)
+ * This is called when a neighbor is in INCOMPLETE state.
+ *
+ * The NUD INCOMPLETE and new-neighbor detection state belongs here.
+ *
+ * Not yet clear how will implement duplicate address detection.
+ * (See send_nsolicit.)
+ * Should I splnet() inside here?
+ ----------------------------------------------------------------------*/
+void
+ipv6_nsolicit(outifp,outgoing,newrt)
+ struct ifnet *outifp;
+ struct mbuf *outgoing;
+ struct rtentry *newrt;
+{
+ struct discq *dq;
+ struct ipv6 *ipv6 = (outgoing == NULL) ? NULL :
+ mtod(outgoing,struct ipv6 *);
+
+ /*
+ * ASSERT: The header is pulled up, and has either the
+ * unspecified address or one of MY valid ipv6 addresses.
+ */
+#if __NetBSD__ || __FreeBSD__ || __OpenBSD__
+ struct ifnet *ifp = (outifp == NULL)?ifnet.tqh_first:outifp;
+#else /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */
+ struct ifnet *ifp = (outifp == NULL)?ifnet:outifp;
+#endif /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */
+
+ DPRINTF(IDL_EVENT,("Entering IPV6_NSOLICIT.\n"));
+ DPRINTF(IDL_EVENT,("outifp:\n"));
+ DDO(IDL_EVENT,dump_ifp(outifp));
+ DPRINTF(IDL_EVENT,("outgoing=0x%lx\n",(unsigned long)outgoing));
+ DPRINTF(IDL_EVENT,("newrt:\n"));
+ DDO(IDL_EVENT,dump_rtentry(newrt));
+ DPRINTF(IDL_EVENT,("ifp:\n"));
+ DDO(IDL_EVENT,dump_ifp(ifp));
+
+ if (newrt == NULL)
+ {
+ DPRINTF(IDL_ERROR,
+ ("ipv6_nsolicit() called with newrt == NULL.\n"));
+ m_freem(outgoing);
+ return;
+ }
+
+ /*
+ * Route with RTF_REJECT has dummy ifa assigned to it in
+ * ipv6_neighbor_timer(). We shouldn't need to send out
+ * another solicit if the route is already marked REJECT.
+ */
+ if (newrt->rt_flags & RTF_REJECT)
+ {
+ DPRINTF(ERROR, ("ipv6_nsolicit passed a RTF_REJECT route\n"));
+ m_freem(outgoing);
+ return;
+ }
+
+ dq = (struct discq *)newrt->rt_llinfo;
+ DPRINTF(GROSSEVENT,("dq:\n"));
+ DDO(GROSSEVENT,dump_discq(dq));
+ DDO(IDL_ERROR,if (dq == NULL) \
+ panic("dq == NULL in nsolicit().\n"); \
+ if (dq->dq_rt != newrt)\
+ panic("dq <-> rt mismatch in nsolicit.\n");\
+ {\
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)\
+ newrt->rt_gateway;\
+ \
+ if (sdl->sdl_alen != 0)\
+ panic("sdl_alen not 0 in nsolicit!");
+ });
+ DDO(GROSSEVENT,if (outifp == NULL)
+ printf("nsolicit passed in outifp of NULL.\n"));
+
+ /*
+ * If new entry, set up certain variables.
+ */
+ if (newrt->rt_rmx.rmx_expire == 0)
+ dq->dq_unanswered = 0;
+
+ /*
+ * Currently queue the last packet sent. May want to
+ * queue up last n packets.
+ */
+
+ if (outgoing != NULL)
+ {
+ if (dq->dq_queue != NULL)
+ m_freem(dq->dq_queue);
+ dq->dq_queue = outgoing;
+ }
+
+ /*
+ * We want to rate-limit these, so only send out if the time has
+ * expired.
+ */
+#ifdef __FreeBSD__
+ if (newrt->rt_rmx.rmx_expire < time_second)
+#else /* __FreeBSD__ */
+ if (newrt->rt_rmx.rmx_expire < time.tv_sec)
+#endif /* __FreeBSD__ */
+ {
+ DPRINTF(IDL_EVENT,
+ ("ipv6_nsolict: solicit time expired -- send another one!\n"));
+#ifdef __FreeBSD__
+ newrt->rt_rmx.rmx_expire = time_second + v6d_retranstime;
+#else /* __FreeBSD__ */
+ newrt->rt_rmx.rmx_expire = time.tv_sec + v6d_retranstime;
+#endif /* __FreeBSD__ */
+ dq->dq_unanswered++; /* Overload for "number of m-cast probes sent". */
+ do
+ {
+ if (ifp->if_type != IFT_LOOP && ipv6_enabled(ifp))
+ {
+ if (ipv6 == NULL && dq->dq_queue != NULL) {
+ ipv6 = mtod(dq->dq_queue,struct ipv6 *);
+ DPRINTF(IDL_EVENT,("v6_solicit: grabbing src from dq!\n"));
+ }
+ DPRINTF(IDL_EVENT,("dq b/f calling send_nsolicit:\n"));
+ DDO(IDL_EVENT,dump_discq(dq));
+ DPRINTF(IDL_EVENT,("ipv6 hdr b/f calling send_nsolicit:\n"));
+ DDO(IDL_EVENT,dump_ipv6(ipv6));
+ send_nsolicit(newrt,ifp,(ipv6 == NULL) ? NULL : &ipv6->ipv6_src,
+ 1);
+ }
+
+ if (outifp == NULL)
+#ifdef __FreeBSD__
+ ifp = ifp->if_link.tqe_next;
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ ifp = ifp->if_list.tqe_next;
+#else /* __NetBSD__ || __OpenBSD__ */
+ ifp = ifp->if_next;
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ else ifp = NULL;
+ }
+ while (ifp != NULL);
+ }
+}
+
+/*----------------------------------------------------------------------
+ * ipv6_onlink_query():
+ * If I have no route, and now assume that a destination is on-link,
+ * then I should create a route entry (I'll have to do it using raw
+ * radix code, because I don't know which interface the destination is
+ * for yet.)
+ *
+ * I should probably optimize this for single-homed hosts, such that
+ * the route lookup after doing this will return with an rt_ifa.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_onlink_query(dst)
+ struct sockaddr_in6 *dst; /* Dest. sockaddr_in6 */
+{
+ struct rtentry *rt;
+ struct sockaddr_dl *sdl;
+ struct in6_ifnet *i6ifp = in6_ifnet;
+ struct ifnet *ifp;
+ int s = splnet();
+
+ /*
+ * Pick an interface, trick ipv6_new_neighbor() into adding a route,
+ * then blank out the interface-specific stuff.
+ */
+
+ while (i6ifp != NULL && (i6ifp->i6ifp_ifp->if_flags & IFF_LOOPBACK))
+ i6ifp = i6ifp->i6ifp_next;
+
+ if (i6ifp == NULL)
+ {
+ DPRINTF(IDL_ERROR,("Oooh boy. No non-loopback i6ifp.\n"));
+ splx(s);
+ return;
+ }
+ ifp = i6ifp->i6ifp_ifp;
+
+ rt = ipv6_new_neighbor(dst,ifp);
+ if (rt == NULL)
+ {
+ DPRINTF(IDL_ERROR,("ipv6_new_neighbor failed in onlink_query.\n"));
+ splx(s);
+ return;
+ }
+
+ if (rt->rt_gateway->sa_family != AF_LINK) {
+ DPRINTF(IDL_ERROR,("onlink_query returns route with non AF_LINK gateway.\n"));
+ splx(s);
+ return;
+ }
+
+ sdl = (struct sockaddr_dl *)rt->rt_gateway;
+ sdl->sdl_index = 0;
+ sdl->sdl_nlen = 0;
+ sdl->sdl_alen = 0;
+ sdl->sdl_slen = 0;
+
+ rt->rt_ifp = NULL;
+ rt->rt_ifa->ifa_refcnt--;
+ rt->rt_ifa = NULL;
+ rt->rt_rmx.rmx_mtu = 0;
+
+ /*
+ * I think I'm cool, now. So send a multicast nsolicit.
+ */
+ ipv6_nsolicit(NULL,NULL,rt);
+ splx(s);
+}
+
+/*----------------------------------------------------------------------
+ * ipv6_verify_onlink():
+ * Verify sockaddr_in6 dst has been determined to be a neighbor
+ * or not. Will only work on an output/route lookup caused by
+ * a process that's trapped in the kernel. I do a tsleep of the
+ * current process while neighbor discovery runs its course.
+ ----------------------------------------------------------------------*/
+
+int
+ipv6_verify_onlink(dst)
+ struct sockaddr_in6 *dst;
+{
+ struct rtentry *rt = NULL;
+#if !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)
+ struct proc *p = curproc; /* XXX */
+#else /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
+ struct proc *p = PCPU(curproc); /* XXX */
+#endif /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
+
+ if (!p) /* i.e. if I'm an interrupt-caused output... */
+ return 1;
+
+ do
+ {
+ if (rt != NULL)
+ rt->rt_refcnt--;
+ /*
+ * Sleep this process, then lookup neighbor route to see if it's
+ * been updated.
+ */
+ switch (tsleep((caddr_t)&rt, PCATCH , "mhomed", hz))
+ {
+ case 0:
+ DPRINTF(IDL_ERROR,("How did I awaken?\n"));
+ /* Fallthrough to... */
+ case EWOULDBLOCK:
+ break;
+ case EINTR:
+ case ERESTART:
+ return EINTR;
+ }
+#ifdef __FreeBSD__
+ rt = rtalloc1((struct sockaddr *)dst,0, 0UL);
+#else /* __FreeBSD__ */
+ rt = rtalloc1((struct sockaddr *)dst,0);
+#endif /* __FreeBSD__ */
+ }
+ while (rt && rt->rt_ifa == NULL);
+
+ DPRINTF(FINISHED, ("verify_onlink came up with rt:\n"));
+ DDO(FINISHED, dump_rtentry(rt));
+ if (rt == NULL)
+ return -1;
+ rt->rt_refcnt--;
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ * ipv6_uni_nsolicit():
+ * Send a unicast neighbor solicit to neighbor specified in rt.
+ * Also update the next-probe time.
+ *
+ * NOTES:
+ * This is so small that it might be better inlined.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_uni_nsolicit(rt)
+ struct rtentry *rt;
+{
+ struct discq *dq = (struct discq *)rt->rt_llinfo;
+
+#ifdef __FreeBSD__
+ rt->rt_rmx.rmx_expire = time_second + v6d_retranstime;
+#else /* __FreeBSD__ */
+ rt->rt_rmx.rmx_expire = time.tv_sec + v6d_retranstime;
+#endif /* __FreeBSD__ */
+ /* If ifa->ifa_addr doesn't work, revert to NULL. */
+ send_nsolicit(rt,rt->rt_ifp,&(I6A_SIN(rt->rt_ifa)->sin6_addr),0);
+ dq->dq_unanswered ++;
+}
+
+
+/*----------------------------------------------------------------------
+ *ipv6_nadvert():
+ * Construct an IPv6 neighbor advertisement,
+ * and send it out via either unicast or multicast.
+ *
+ * NOTES:
+ * Might later add a proxy advertisement bit or anycast bit to the flags
+ * parameter.
+ ----------------------------------------------------------------------*/
+void
+ipv6_nadvert(i6a,ifp,dstrt,flags)
+ struct in6_ifaddr *i6a;
+ struct ifnet *ifp;
+ struct rtentry *dstrt; /* If null, send m-cast neighbor advert. */
+ uint32_t flags;
+{
+ struct ipv6_moptions i6mo,*i6mop = NULL;
+ struct mbuf *advert;
+ struct ipv6 *ipv6;
+ struct ipv6_icmp *icmp;
+
+ advert = get_discov_cluster();
+ if (advert == NULL)
+ return;
+
+ ipv6 = mtod(advert,struct ipv6 *);
+ icmp = (struct ipv6_icmp *)&(advert->m_data[sizeof(struct ipv6)]);
+
+ ipv6->ipv6_src = i6a->i6a_addr.sin6_addr; /* May be different for
+ proxy. */
+ ipv6->ipv6_versfl = htonl(0x6f000000);
+ ipv6->ipv6_hoplimit = 255; /* Guaranteed to be intra-link if arrives with
+ 255. */
+ ipv6->ipv6_nexthdr = IPPROTO_ICMPV6;
+ ipv6->ipv6_length = ICMPV6_NADVMINLEN; /* For now */
+ icmp->icmp_type = ICMPV6_NEIGHBORADV;
+ icmp->icmp_code = 0;
+ icmp->icmp_cksum = 0;
+ icmp->icmp_nadvbits = flags;
+
+ /* Flags in ipv6_icmp.h are in endian-specific #ifdefs, so
+ there is no need to HTONL(icmp->icmp_nadvbits); */
+
+ /* If proxy advert, set proxy bits. */
+ icmp->icmp_nadvaddr = i6a->i6a_addr.sin6_addr;
+
+ if (dstrt == NULL)
+ {
+ i6mo.i6mo_multicast_ifp = ifp;
+ i6mo.i6mo_multicast_ttl = 255; /* Must set. */
+ i6mop = &i6mo;
+ SET_IN6_ALLNODES(ipv6->ipv6_dst);
+ SET_IN6_MCASTSCOPE(ipv6->ipv6_dst, IN6_INTRA_LINK);
+ }
+ else ipv6->ipv6_dst = ((struct sockaddr_in6 *)rt_key(dstrt))->sin6_addr;
+
+ /*
+ * Set up extensions (if any)
+ */
+
+ /*
+ * Perhaps create a separate function to look through interface's address
+ * list to find my data link address, but if this would really be called
+ * enough other places...
+ */
+ if (i6a->i6a_ifp->if_addrlen != 0)
+ {
+ struct icmp_exthdr *ext = (struct icmp_exthdr *)&(icmp->icmp_nadvext);
+ struct ifaddr *ifa;
+ struct sockaddr_dl *lsdl; /* Target's Local Link-layer Sockaddr */
+
+ ext->ext_id = EXT_TARGETLINK;
+ switch (i6a->i6a_ifp->if_type)
+ {
+ case IFT_ETHER:
+ ext->ext_length = 1 + ((ifp->if_addrlen +1) >> 3);
+#ifdef __FreeBSD__
+ for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ if (ifa->ifa_addr->sa_family == AF_LINK)
+ break;
+ if (ifa == NULL)
+ {
+ DPRINTF(IDL_ERROR, ("Can't find link addr. in nadvert().\n"));
+ m_freem(advert);
+ return;
+ }
+ lsdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ bzero(ext->ext_data,ext->ext_length*8 - 2);
+ bcopy(LLADDR(lsdl),ext->ext_data,ifp->if_addrlen);
+ break;
+ default:
+ DPRINTF(IDL_ERROR,("DANGER: sending n. adv on unk. link type.\n"));
+ break;
+ }
+ ipv6->ipv6_length += ext->ext_length <<3;
+ }
+
+ advert->m_len = advert->m_pkthdr.len = ipv6->ipv6_length + sizeof(*ipv6);
+ icmp->icmp_cksum = in6_cksum(advert,IPPROTO_ICMPV6,ipv6->ipv6_length,
+ sizeof(struct ipv6));
+ ipv6_output(advert,NULL,IPV6_RAWOUTPUT,i6mop, NULL, NULL);
+}
+
+/*----------------------------------------------------------------------
+ * ipv6_routersol_input():
+ * Handle reception of Router Solicit messages.
+ *
+ ----------------------------------------------------------------------*/
+void
+ipv6_routersol_input(incoming, extra)
+ struct mbuf *incoming;
+ int extra;
+{
+ /*
+ * Return and let user-level process deal with it.
+ */
+}
+
+/* Add a default router in response to a route request (RTM_ADD/RTM_RESOLVE) */
+
+struct v6router *ipv6_add_defrouter_rtrequest(struct rtentry *inrt)
+{
+#if 0
+ struct rtentry *rt, *defrt=NULL;
+ struct v6router *v6r;
+ struct sockaddr_in6 sin6;
+#endif /* 0 */
+
+ DPRINTF(GROSSEVENT, ("ipv6_add_defrouter_rtrequest(inrt=%08x)\n", OSDEP_PCAST(inrt)));
+ DDO(GROSSEVENT, dump_rtentry(inrt));
+
+#if 0
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_port = 0;
+ sin6.sin6_flowinfo = 0;
+
+ if (!(rt = inrt->rt_gwroute))
+#ifdef __FreeBSD__
+ if (!(rt = inrt->rt_gwroute = rtalloc1(inrt->rt_gateway, 1, 0UL)))
+#else /* __FreeBSD__ */
+ if (!(rt = inrt->rt_gwroute = rtalloc1(inrt->rt_gateway, 1)))
+#endif /* __FreeBSD__ */
+ return NULL;
+
+ if (!(v6r = malloc(sizeof(*v6r),M_DISCQ,M_NOWAIT))) {
+ DPRINTF(IDL_ERROR, ("ipv6_add_defrouter_rtrequest: Can't allocate router list entry.\n"));
+ /* Q: Do I want to delete the neighbor entry? */
+ /*rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);*/
+ return NULL;
+ };
+
+ /*
+ * Assertion:
+ * Tunnel default routes are added only by manual
+ * (i.e. user-level) adds and deletes.
+ * If that's true, then the route in the v6r structure is "held" by
+ * the default route. Oooh, I'd better make sure that default router
+ * entries hold their... wait a minute, when I delete neighbors, the
+ * default router neigbors are searched in the list (and nuked...).
+ */
+
+ bzero(v6r,sizeof(*v6r));
+ v6r->v6r_rt = rt;
+ v6r->v6r_children.v6c_next = v6r->v6r_children.v6c_prev = &v6r->v6r_children;
+
+ if (!inrt->rt_gwroute) {
+ /*
+ * First default router added, and not a manual add. Add
+ * default route.
+ */
+ struct sockaddr_in6 mask;
+
+ bzero(&mask,sizeof(mask));
+ mask.sin6_family = AF_INET6;
+ mask.sin6_len = sizeof(struct sockaddr_in6);
+
+ sin6.sin6_addr = in6addr_any;
+
+ DDO(IDL_EVENT,printf("------(Before rtrequest)----------\n");\
+ dump_smart_sockaddr((struct sockaddr *)&sin6);\
+ printf("----------------\n"));
+
+ insque(v6r,&defrtr); /* To prevent double-adds. */
+ if ((rc = rtrequest(RTM_ADD,(struct sockaddr *)&sin6,rt_key(rt),
+ (struct sockaddr *)&mask,RTF_DEFAULT|RTF_GATEWAY,
+ &defrt))) {
+ remque(v6r);
+ DPRINTF(IDL_ERROR, ("ipv6_add_defrouter_rtrequest: Default route add failed (%d).\n",rc));
+ free(v6r, M_DISCQ);
+ /* Q: Do I want do delete the neighbor entry? */
+ /* rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);*/
+ return NULL;
+ }
+ defrt->rt_refcnt--;
+ /*defrt->rt_flags |= RTF_CLONING;*/
+ } else
+ insque(v6r,&defrtr); /* If double-add prevention not needed. */
+
+ return v6r;
+#else /* 0 */
+
+ if (!inrt->rt_gwroute)
+#if __FreeBSD__
+ if (!(inrt->rt_gwroute = rtalloc1(inrt->rt_gateway, 1, 0)))
+#else /* __FreeBSD__ */
+ if (!(inrt->rt_gwroute = rtalloc1(inrt->rt_gateway, 1)))
+#endif /* __FreeBSD__ */
+ return NULL;
+
+ return &defrtr;
+#endif /* 0 */
+};
+
+/* Add a default router in response to a router advertisement.
+ ipv6_add_defrouter_rtrequest ends up getting called underneath because of
+ the rtalloc1(). */
+
+struct v6router *ipv6_add_defrouter_advert(struct in6_addr *addr, int lifetime, struct ifnet *ifp)
+{
+ struct rtentry *rt, *defrt=NULL;
+ struct v6router *v6r;
+ struct sockaddr_in6 sin6;
+ int rc;
+
+ DPRINTF(IDL_EVENT, ("ipv6_add_defrouter_advert(addr=%08x, lifetime=%d, ifp=%08x)\n", OSDEP_PCAST(addr), lifetime, OSDEP_PCAST(ifp)));
+
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_port = 0;
+ sin6.sin6_flowinfo = 0;
+ sin6.sin6_addr = *addr;
+
+ /*
+ * Find it if it already exists. Router adverts come from
+ * link-local addresses, so doing rtalloc1() will be safe.
+ * If this function is called because of a manual add, inrt should
+ * be non-NULL, so this codepath won't be executed.
+ */
+#if __FreeBSD__
+ rt = rtalloc1((struct sockaddr *)&sin6, 0, 0);
+#else /* __FreeBSD__ */
+ rt = rtalloc1((struct sockaddr *)&sin6, 0);
+#endif /* __FreeBSD__ */
+ if (rt == NULL || !(rt->rt_flags & RTF_LLINFO)) {
+ if (rt != NULL)
+ RTFREE(rt);
+ rt = ipv6_new_neighbor(&sin6,ifp);
+ } else
+ rt->rt_refcnt--;
+
+ DDO(GROSSEVENT,printf("After new_neighbor:\n"); dump_smart_sockaddr((struct sockaddr *)&sin6));
+ if (rt == NULL) {
+ DPRINTF(IDL_ERROR, ("ipv6_add_defrouter's new neighbor failed.\n"));
+ return NULL;
+ };
+ rt->rt_flags |= RTF_DEFAULT|RTF_ISAROUTER;
+
+ if (!(v6r = malloc(sizeof(struct v6router), M_DISCQ, M_NOWAIT))) {
+ DPRINTF(IDL_ERROR,("ipv6_add_defrouter_advert: Can't allocate router list entry.\n"));
+ /* Q: Do I want to delete the neighbor entry? */
+ /*rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);*/
+ return NULL;
+ };
+
+ /*
+ * Assertion:
+ * Tunnel default routes are added only by manual
+ * (i.e. user-level) adds and deletes.
+ * If that's true, then the route in the v6r structure is "held" by
+ * the default route. Oooh, I'd better make sure that default router
+ * entries hold their... wait a minute, when I delete neighbors, the
+ * default router neigbors are searched in the list (and nuked...).
+ */
+
+ bzero(v6r,sizeof(*v6r));
+ v6r->v6r_rt = rt;
+ v6r->v6r_children.v6c_next = v6r->v6r_children.v6c_prev = &v6r->v6r_children;
+#ifdef __FreeBSD__
+ v6r->v6r_expire = time_second + lifetime;
+#else /* __FreeBSD__ */
+ v6r->v6r_expire = time.tv_sec + lifetime;
+#endif /* __FreeBSD__ */
+
+ if (defrtr.v6r_next == &defrtr) {
+ /*
+ * First default router added, and not a manual add. Add
+ * default route.
+ */
+ struct sockaddr_in6 mask;
+
+ bzero(&mask,sizeof(mask));
+ mask.sin6_family = AF_INET6;
+ mask.sin6_len = sizeof(struct sockaddr_in6);
+
+ sin6.sin6_addr = in6addr_any;
+
+ DDO(GROSSEVENT,printf("------(Before rtrequest)----------\n");\
+ dump_smart_sockaddr((struct sockaddr *)&sin6);\
+ printf("----------------\n"));
+
+ insque(v6r, &defrtr); /* To prevent double-adds. */
+ if ((rc = rtrequest(RTM_ADD, (struct sockaddr *)&sin6, rt_key(rt),
+ (struct sockaddr *)&mask, RTF_DEFAULT|RTF_GATEWAY,
+ &defrt))) {
+ remque(v6r);
+ DPRINTF(IDL_ERROR, ("ipv6_add_defrouter_advert: Default route add failed (%d).\n", rc));
+ free(v6r, M_DISCQ);
+ /* Q: Do I want do delete the neighbor entry? */
+ /* rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);*/
+ return NULL;
+ };
+ defrt->rt_refcnt--;
+ /*defrt->rt_flags |= RTF_CLONING;*/
+ } else
+ insque(v6r, &defrtr); /* If double-add prevention not needed. */
+
+ return v6r;
+}
+
+/*----------------------------------------------------------------------
+ * Update a default router entry.
+ ----------------------------------------------------------------------*/
+
+static int
+update_defrouter(v6r,icmp)
+ struct v6router *v6r;
+ struct ipv6_icmp *icmp;
+{
+ /* Is this it? What if someone deleted the default route? */
+#ifdef __FreeBSD__
+ v6r->v6r_expire = time_second + ntohs(icmp->icmp_radvlifetime);
+#else /* __FreeBSD__ */
+ v6r->v6r_expire = time.tv_sec + ntohs(icmp->icmp_radvlifetime);
+#endif /* __FreeBSD__ */
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ * Delete a default router entry. This function may even delete the
+ * default route itself.
+ ----------------------------------------------------------------------*/
+
+int
+ipv6_delete_defrouter(v6r)
+ struct v6router *v6r;
+{
+ struct sockaddr_in6 sin6,mask;
+ struct rtentry *rt = v6r->v6r_rt;
+ u_long flags = rt->rt_flags;
+
+ DPRINTF(FINISHED, ("In ipv6_delete_defrouter, v6r = 0x%lx\n", (unsigned long)v6r));
+
+ remque(v6r);
+ rt->rt_flags |= RTF_UP; /* To prevent double-freeing of the route. */
+ while (v6r->v6r_children.v6c_next != &v6r->v6r_children)
+ {
+ DDO(IDL_ERROR,printf("Deleting route (0x%lx):\n",\
+ (unsigned long)v6r->v6r_children.v6c_next->v6c_route);\
+ dump_rtentry(v6r->v6r_children.v6c_next->v6c_route));
+ rtrequest(RTM_DELETE,rt_key(v6r->v6r_children.v6c_next->v6c_route),NULL,
+ NULL,0,NULL);
+ }
+ rt->rt_flags = flags;
+ /*
+ * Do I RTFREE() or do I rtrequest(RTM_DELETE) or do neither?
+ * For now, neither, because this is called from either a r. advert.
+ * advertising 0, or from an RTM_DELETE request for the neighbor.
+ */
+ /*RTFREE(v6r->v6r_rt);*/
+ free(v6r,M_DISCQ);
+
+ /*
+ * What if I'm the last router in the default router list? If so, then
+ * delete the default route.
+ */
+
+ if (defrtr.v6r_next == &defrtr)
+ {
+ DPRINTF(IDL_ERROR,\
+ ("Last router on list deleted. Deleting default route"));
+ bzero(&mask,sizeof(mask));
+ bzero(&sin6,sizeof(sin6));
+ mask.sin6_family = AF_INET6;
+ mask.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ rt->rt_flags |= RTF_UP; /* To prevent double-freeing. */
+ rtrequest(RTM_DELETE,(struct sockaddr *)&sin6,NULL,
+ (struct sockaddr *)&mask,0,NULL);
+ rt->rt_flags = flags;
+ }
+
+ return 1;
+}
+
+/*----------------------------------------------------------------------
+ * Given an advertised prefix, a mask, and a link-local, create the
+ * new address. Write result into "prefix" argument space.
+ * (Should inline this.)
+ ----------------------------------------------------------------------*/
+
+static void
+prefix_concat(prefix,linkloc,mask)
+ struct in6_addr *prefix,*linkloc,*mask;
+{
+ prefix->in6a_words[0] = (prefix->in6a_words[0] & mask->in6a_words[0]) | (linkloc->in6a_words[0] & ~mask->in6a_words[0]);
+ prefix->in6a_words[1] = (prefix->in6a_words[1] & mask->in6a_words[1]) | (linkloc->in6a_words[1] & ~mask->in6a_words[1]);
+ prefix->in6a_words[2] = (prefix->in6a_words[2] & mask->in6a_words[2]) | (linkloc->in6a_words[2] & ~mask->in6a_words[2]);
+ prefix->in6a_words[3] = (prefix->in6a_words[3] & mask->in6a_words[3]) | (linkloc->in6a_words[3] & ~mask->in6a_words[3]);
+}
+
+/*----------------------------------------------------------------------
+ * ipv6_routeradv_input():
+ * Handle reception of Router Advertisement messages.
+ *
+ ----------------------------------------------------------------------*/
+void
+ipv6_routeradv_input(incoming, extra)
+ struct mbuf *incoming;
+ int extra;
+{
+ struct ipv6 *ipv6;
+ struct ipv6_icmp *icmp;
+ struct v6router *v6r;
+ struct icmp_exthdr *ext;
+ int howbig;
+
+ /*
+ * Hmmmm, do want to handle some things down here in the kernel, but
+ * what about other things, like addrconf?
+ */
+
+ DPRINTF(IDL_EVENT, ("OK, got a router advert.\n"));
+ if (!ipv6rsolicit) /* If I'm not soliciting routers, ignore this */
+ return;
+
+ /* Verify that length looks OK */
+ howbig = incoming->m_pkthdr.len - extra;
+ if (howbig < ICMPV6_RADVMINLEN)
+ return;
+
+ /* XXX - Assumes that the entire packet fits within MCLBYTES. */
+ if (incoming->m_len < incoming->m_pkthdr.len)
+ if (incoming = m_pullup2(incoming, incoming->m_pkthdr.len))
+ return;
+
+ ipv6 = mtod(incoming,struct ipv6 *);
+ icmp = (struct ipv6_icmp *)(mtod(incoming, caddr_t) + extra);
+
+ /* Check to see hop count is 255. */
+ if (ipv6->ipv6_hoplimit != 255)
+ {
+ /*
+ * Keep stats on this!
+ */
+ DPRINTF(IDL_ERROR, ("Received Router Advertisement with hoplimit != 255.\n"));
+ return;
+ }
+
+ /* Verify source address is link-local */
+ if (!IN6_IS_ADDR_LINKLOCAL(&ipv6->ipv6_src))
+ {
+ DPRINTF(IDL_ERROR, ("Received Router Advertisement with non link-local source address.\n"));
+ return;
+ }
+
+ /*
+ * I now have a router advertisement.
+ */
+
+ /*
+ * First, find the entry, if available.
+ */
+ for (v6r = defrtr.v6r_next; v6r != &defrtr; v6r = v6r->v6r_next)
+ if (IN6_ARE_ADDR_EQUAL(&ipv6->ipv6_src, &V6R_SIN6(v6r)->sin6_addr))
+ break;
+
+ if (v6r == &defrtr) {
+ /* Not found. */
+ if (icmp->icmp_radvlifetime) {
+ if (!(v6r = ipv6_add_defrouter_advert(&ipv6->ipv6_src, ntohs(icmp->icmp_radvlifetime), incoming->m_pkthdr.rcvif))) {
+ printf("Problems adding to default router list.\n");
+ v6r = &defrtr;
+ }
+ }
+ /* else fallthrough if no lifetime, and not found. */
+ } else {
+ /* Found. */
+ if (icmp->icmp_radvlifetime) {
+ /*
+ * Perhaps do some reality checking here. Was the radv snarfed
+ * off the same interface? Is it the same link address? Is it
+ * marked M_AUTHENTIC or M_DECRYPTED?
+ */
+
+ if (incoming->m_pkthdr.rcvif != v6r->v6r_rt->rt_ifp) {
+ printf("WARNING: radv for router off different interface.\n");
+ v6r = &defrtr;
+ } else
+ if (update_defrouter(v6r,icmp)) {
+ printf("update_defrouter failed on radv_input.\n");
+ v6r = &defrtr;
+ }
+ } else {
+ if (ipv6_delete_defrouter(v6r)) /* XXX */
+ printf("ipv6_delete_defrouter failed on radv_input.\n");
+ v6r = &defrtr;
+ }
+ };
+
+ if (icmp->icmp_radvhop)
+ ipv6_defhoplmt = icmp->icmp_radvhop;
+
+ if (icmp->icmp_radvbits)
+ {
+ /*
+ * Kick DHCP in the pants to do the right thing(s).
+ */
+ }
+
+ if (icmp->icmp_radvreach)
+ v6d_reachtime = max(1,ntohl(icmp->icmp_radvreach)/1000);
+
+ if (icmp->icmp_radvretrans)
+ v6d_retranstime = max(1,ntohl(icmp->icmp_radvretrans)/1000);
+
+ /*
+ * Handle extensions/options.
+ */
+ ext = (struct icmp_exthdr *)icmp->icmp_radvext;
+
+ DPRINTF(IDL_EVENT,\
+ ("Parsing exensions. ext = 0x%lx, icmp = 0x%lx, howbig = %d.\n",\
+ (unsigned long)ext, (unsigned long)icmp,howbig));
+
+ while ((u_long)ext - (u_long)icmp < howbig)
+ {
+ struct ext_prefinfo *pre = (struct ext_prefinfo *)ext;
+
+ DPRINTF(GROSSEVENT,("In loop, ext_id = %d.\n",ext->ext_id));
+
+ switch (ext->ext_id)
+ {
+ case EXT_SOURCELINK:
+ /*
+ * We already have a v6r that may point to the neighbor. If so,
+ * fill in the sockaddr_dl part of it, and set the neighbor state
+ * STALE.
+ */
+ if (v6r != &defrtr)
+ {
+ /*
+ * i.e. if I have a neighbor cache entry...
+ */
+ struct rtentry *rt = v6r->v6r_rt;
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)rt->rt_gateway;
+ struct discq *dq = (struct discq *)rt->rt_llinfo;
+
+ switch (rt->rt_ifp->if_type)
+ {
+ case IFT_ETHER:
+ if (sdl->sdl_alen == 0)
+ {
+ sdl->sdl_alen = 6;
+ bcopy(ext->ext_data,LLADDR(sdl),6);
+ }
+ else if (bcmp(LLADDR(sdl),ext->ext_data,6))
+ {
+ DPRINTF(IDL_ERROR,\
+ ("WARNING: R. adv is saying lladdr is new.\n"));
+ }
+#ifdef __FreeBSD__
+ rt->rt_rmx.rmx_expire = time_second;
+#else /* __FreeBSD__ */
+ rt->rt_rmx.rmx_expire = time.tv_sec;
+#endif /* __FreeBSD__ */
+ dq->dq_unanswered = -1; /* in STALE state.
+ (STALE => expired,
+ but unans = -1. */
+ break;
+ default:
+ DPRINTF(IDL_ERROR,\
+ ("I haven't a clue what if_type this is.\n"));
+ break;
+ }
+ }
+ break;
+ case EXT_PREFIX:
+ {
+ /*
+ * NOTE: We really should handle this section of code off in
+ * ipv6_addrconf.c, but I didn't have time to move it
+ * over.
+ *
+ * 1. If L bit is set, and A bit is not, then simply add an on-link
+ * route for this prefix, and be done with it. (If the on-link
+ * isn't already there, of course!)
+ *
+ * But what if I have an i6a that matches this?
+ *
+ * 2. Okay, if A bit is set, then do the following:
+ * a. If prefix is link-local, bail.
+ * b. Sanity check that preferred <= valid. If not, bail.
+ * c. Check in6_ifaddr list, see if prefix is present. If so,
+ * check if autoconf. (i.e. lifetime != 0). If so, update
+ * lifetimes. (then bail.)
+ * d. Okay, so it's not present this means we have to:
+ * i. If L bit is set:
+ * - If route present (exact prefix match), delete it.
+ * - Call in6_control, just like ifconfig(8) does.
+ * - Look up i6a and set expiration times.
+ * ELSE (no L-bit)
+ * - Set up i6a manually (gag me).
+ * - See if there exists an exact prefix match, if so,
+ * then change its ifa to the new i6a.
+ */
+ struct inet6_aliasreq ifra;
+ struct sockaddr_in6 *dst = &ifra.ifra_addr,*mask = &ifra.ifra_mask;
+
+#if 0
+ u_char *cp,*cp2,*cp3;
+#else /* 0 */
+ u_char *cp;
+#endif /* 0 */
+ int i;
+
+ /* Sanity check prefix length and lifetimes. */
+ if (pre->pre_prefixsize == 0 || pre->pre_prefixsize >= 128)
+ {
+ DPRINTF(IDL_ERROR,("Prefix size failed sanity check.\n"));
+ break;
+ }
+ if (ntohl(pre->pre_valid) < ntohl(pre->pre_preferred))
+ {
+ DPRINTF(IDL_ERROR,("Lifetimes failed sanity check.\n"));
+ break;
+ }
+
+ /*
+ * Set up dst and mask.
+ */
+ bzero(&ifra,sizeof(ifra));
+ dst->sin6_family = AF_INET6;
+ dst->sin6_len = sizeof(*dst);
+ dst->sin6_addr = pre->pre_prefix;
+ mask->sin6_len = sizeof(*mask) - sizeof(struct in6_addr) +
+ ((pre->pre_prefixsize - 1) / 8) + 1;
+ cp = (u_char *)&mask->sin6_addr;
+ for (i = 0; i <= ((pre->pre_prefixsize - 1) / 8) ; i ++)
+ cp[i] = 0xff;
+ cp[--i] <<= (8 - (pre->pre_prefixsize % 8)) % 8;
+
+ DDO(GROSSEVENT,printf("mask and prefix are:\n");\
+ dump_sockaddr_in6(dst);dump_sockaddr_in6(mask));
+
+ DP(GROSSEVENT, pre->pre_bits, d);
+
+ if (pre->pre_bits & ICMPV6_PREFIX_AUTO)
+ {
+ struct in6_ifaddr *i6a;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) {
+ DPRINTF(IDL_ERROR, ("ipv6_routeradv_input: Received router advertisement for link-local prefix\n"));
+ break; /* Bail. */
+ };
+
+ DPRINTF(GROSSEVENT, ("ipv6_routeradv_input: Scanning interface addresses for the received prefix\n"));
+
+#if 0
+ for (i6a = in6_ifaddr; i6a != NULL; i6a = i6a->i6a_next)
+ if (bcmp(&i6a->i6a_sockmask.sin6_addr,&mask->sin6_addr,
+ sizeof(struct in6_addr)) == 0)
+ {
+ cp = (u_char *)&i6a->i6a_addr.sin6_addr;
+ cp2 = (u_char *)&i6a->i6a_sockmask.sin6_addr;
+ cp3 = (u_char *)&dst->sin6_addr;
+ for (i = 0; (i >=0 && i < sizeof(struct in6_addr)); i++)
+ if ((cp[i] ^ cp3[i]) & cp2[i])
+ i = -2;
+
+ if (i >= 0 && i6a->i6a_expire != 0 &&
+ ((i6a->i6a_addrflags & I6AF_NOTSURE) == 0))
+#else /* 0 */
+ for (i6a = in6_ifaddr; i6a != NULL; i6a = i6a->i6a_next) {
+ if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_sockmask.sin6_addr, &mask->sin6_addr))
+ continue;
+ if (
+(i6a->i6a_addr.sin6_addr.in6a_words[0] ^ dst->sin6_addr.in6a_words[0]) & i6a->i6a_sockmask.sin6_addr.in6a_words[0] ||
+(i6a->i6a_addr.sin6_addr.in6a_words[1] ^ dst->sin6_addr.in6a_words[1]) & i6a->i6a_sockmask.sin6_addr.in6a_words[1] ||
+(i6a->i6a_addr.sin6_addr.in6a_words[2] ^ dst->sin6_addr.in6a_words[2]) & i6a->i6a_sockmask.sin6_addr.in6a_words[2] ||
+(i6a->i6a_addr.sin6_addr.in6a_words[3] ^ dst->sin6_addr.in6a_words[3]) & i6a->i6a_sockmask.sin6_addr.in6a_words[3])
+ continue;
+
+ if (i6a->i6a_expire && !(i6a->i6a_addrflags & I6AF_NOTSURE))
+#endif /* 0 */
+ {
+ /*
+ * Found an autoconfigured i6a.
+ *
+ * WARNING: For now, this path will not attempt to
+ * do route table repair/updating.
+ * This means if the link bit was off
+ * before, but on now, you are in trouble.
+ */
+ DDO(FINISHED, printf("Found i6a of:\n");\
+ dump_ifa((struct ifaddr *)i6a));
+
+ if (ntohl(pre->pre_preferred) == 0xffffffff)
+ i6a->i6a_preferred = ~0;
+#ifdef __FreeBSD__
+ else i6a->i6a_preferred = time_second +
+#else /* __FreeBSD__ */
+ else i6a->i6a_preferred = time.tv_sec +
+#endif /* __FreeBSD__ */
+ ntohl(pre->pre_preferred);
+
+ if (ntohl(pre->pre_valid) == 0xffffffff)
+ i6a->i6a_expire = ~0;
+#ifdef __FreeBSD__
+ else i6a->i6a_expire = time_second +
+#else /* __FreeBSD__ */
+ else i6a->i6a_expire = time.tv_sec +
+#endif /* __FreeBSD__ */
+ ntohl(pre->pre_valid);
+ break;
+ }
+ }
+
+ DP(GROSSEVENT, OSDEP_PCAST(i6a), 08x);
+
+ if (i6a == NULL)
+ {
+ struct rtentry *rt;
+ struct socket so;
+#if __NetBSD__ || __FreeBSD__
+ struct proc proc;
+ struct pcred pcred;
+ struct ucred ucred;
+#endif /* __NetBSD__ || __FreeBSD__ */
+ /*
+ * Need to create new one.
+ */
+ DPRINTF(GROSSEVENT,("Creating new i6a.\n"));
+
+#if __NetBSD__ || __FreeBSD__
+ ucred.cr_uid = 0;
+ proc.p_cred = &pcred;
+ proc.p_ucred = &ucred;
+#else /* __NetBSD__ || __FreeBSD__ */
+ /*
+ * Do in6_control, like ifconfig does. If L bit is not
+ * set, delete on-link route.
+ */
+ so.so_state = SS_PRIV;
+#endif /* __NetBSD__ || __FreeBSD__ */
+
+ /*
+ * Construct address.
+ */
+
+ for (i6a = in6_ifaddr; i6a != NULL; i6a = i6a->i6a_next)
+ if (i6a->i6a_ifp == incoming->m_pkthdr.rcvif &&
+ IN6_IS_ADDR_LINKLOCAL(&i6a->i6a_addr.sin6_addr))
+ break;
+ if (i6a == NULL)
+ {
+ DPRINTF(IDL_ERROR,
+ ("Can't find link-local for this if!\n"));
+ break;
+ }
+
+ if (i6a->i6a_preflen != pre->pre_prefixsize
+ || i6a->i6a_preflen == 0)
+ {
+ DPRINTF(IDL_ERROR,\
+ ("Prefix size problems, i6a_preflen = %d, adv. size = %d.\n",\
+ i6a->i6a_preflen,pre->pre_prefixsize));
+ break;
+ }
+
+ prefix_concat(&dst->sin6_addr,&i6a->i6a_addr.sin6_addr,
+ &mask->sin6_addr);
+
+ if (pre->pre_bits & ICMPV6_PREFIX_ONLINK)
+ {
+ /*
+ * If route already exists, delete it.
+ */
+#ifdef __FreeBSD__
+ rt = rtalloc1((struct sockaddr *)dst,0,0UL);
+#else /* __FreeBSD__ */
+ rt = rtalloc1((struct sockaddr *)dst,0);
+#endif /* __FreeBSD__ */
+ if (rt != NULL &&
+ !(rt->rt_flags & (RTF_GATEWAY|RTF_TUNNEL)))
+ if ((rt_mask(rt) && bcmp(&mask->sin6_addr,
+ &((struct sockaddr_in6 *)rt_mask(rt))->sin6_addr,
+ sizeof(struct in6_addr)) == 0) ||
+ (!rt_mask(rt) && (bcmp(&mask->sin6_addr, &in6_allones,
+ sizeof(mask->sin6_addr)) == 0)) )
+ {
+ /*
+ * Making sure the route is THIS ONE is as
+ * simple as checking the masks' equality.
+ */
+ DPRINTF(IDL_EVENT, ("Deleting existing on-link route before autoconfiguring new interface.\n"));
+ rt->rt_refcnt--;
+ rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);
+ }
+ if (rt != NULL)
+ rt->rt_refcnt--;
+ }
+
+#if __NetBSD__ || __FreeBSD__
+ if (in6_control(&so,SIOCAIFADDR_INET6,(caddr_t)&ifra, incoming->m_pkthdr.rcvif,1, &proc))
+#else /* __NetBSD__ || __FreeBSD__ */
+ if (in6_control(&so,SIOCAIFADDR_INET6,(caddr_t)&ifra, incoming->m_pkthdr.rcvif,1))
+#endif /* __NetBSD__ || __FreeBSD__ */
+ {
+ DPRINTF(IDL_ERROR,
+ ("DANGER: in6_control failed.\n"));
+ }
+ else if (!(pre->pre_bits & ICMPV6_PREFIX_ONLINK))
+ {
+ /*
+ * Router advert didn't specify the prefix as
+ * being all on-link, so nuke the route.
+ */
+ rtrequest(RTM_DELETE,(struct sockaddr *)dst,NULL,
+ (struct sockaddr *)mask,0,NULL);
+ }
+ for(i6a = in6_ifaddr;i6a != NULL; i6a = i6a->i6a_next)
+ if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &dst->sin6_addr))
+ break;
+ if (i6a != NULL)
+ {
+ if (ntohl(pre->pre_preferred) == 0xffffffff)
+ i6a->i6a_preferred = 0xffffffff;
+ else i6a->i6a_preferred =
+#ifdef __FreeBSD__
+ time_second + ntohl(pre->pre_preferred);
+#else /* __FreeBSD__ */
+ time.tv_sec + ntohl(pre->pre_preferred);
+#endif /* __FreeBSD__ */
+ if (ntohl(pre->pre_valid) == 0xffffffff)
+ i6a->i6a_expire = 0xffffffff;
+ else i6a->i6a_expire =
+#ifdef __FreeBSD__
+ time_second + ntohl(pre->pre_valid);
+#else /* __FreeBSD__ */
+ time.tv_sec + ntohl(pre->pre_valid);
+#endif /* __FreeBSD__ */
+ }
+ }
+ }
+ else if (pre->pre_bits & ICMPV6_PREFIX_ONLINK)
+ {
+ /*
+ * Construct on-link prefix route and add.
+ *
+ * WARNING: According to the discovery document, prefixes
+ * should be kept in a list, though I only do that
+ * if I have an address on that list.
+ * The falllout from this is that prefixes don't have
+ * their lifetimes enforced.
+ *
+ * Also, what if I have an i6a for this already?
+ */
+ struct sockaddr_dl sdl;
+
+ bzero(&sdl,sizeof(sdl));
+ sdl.sdl_family = AF_LINK;
+ sdl.sdl_len = sizeof(sdl);
+ sdl.sdl_index = incoming->m_pkthdr.rcvif->if_index;
+ rtrequest(RTM_ADD,(struct sockaddr *)dst,
+ (struct sockaddr *)&sdl, (struct sockaddr *)mask,
+ 0,NULL);
+ }
+ }
+ break;
+ case EXT_MTU:
+ /*
+ * I'm going to ignore for now.
+ *
+ * Processing would include:
+ *
+ * 1. Possibly change the ifp->if_mtu
+ * 2. Traversing all IPv6 routes with this MTU and updating.
+ * (This could cause TCP pcb's to be updated too.)
+ */
+ break;
+ default:
+ /*
+ * And I quote:
+ *
+ * Future version of this protocol may define new option types.
+ * Receivers MUST silently ignore any options they do not recognize
+ * and continue processing the message.
+ */
+ break;
+ }
+ (u_long)ext += (ext->ext_length << 3);
+ }
+}
+
+/*----------------------------------------------------------------------
+ * ipv6_new_neighbor():
+ * Return a new neighbor-cache entry for address sin6 on interface ifp.
+ * A front-end for rtrequest(RTM_ADD, ...). This returns NULL
+ * if there is a problem.
+ *
+ * NOTES:
+ * May want to handle case of ifp == NULL.
+ * ipv6_discov_rtrequest() will handle ancillary structure setup.
+ ----------------------------------------------------------------------*/
+
+struct rtentry *
+ipv6_new_neighbor(sin6, ifp)
+ struct sockaddr_in6 *sin6; /* Neighbor's IPv6 address. */
+ struct ifnet *ifp; /* Interface neighbor lies off this. */
+{
+ struct sockaddr_dl lsdl; /* Target's Link-local Address sockaddr */
+ struct sockaddr *dst = (struct sockaddr *)sin6;
+ struct sockaddr *gateway = (struct sockaddr *)&lsdl;
+ struct sockaddr *netmask = (struct sockaddr *)&in6_allones;
+ struct rtentry *newrt = NULL;
+ struct ifaddr *ifa;
+ int flags = RTF_HOST;
+
+#ifdef __FreeBSD__
+ for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ if (ifa->ifa_addr->sa_family == AF_LINK)
+ break;
+ if (ifa == NULL)
+ {
+ DPRINTF(IDL_ERROR,
+ ("ipv6_new_neighbor() can't find AF_LINK for ifp.\n"));
+ return NULL;
+ }
+
+ bcopy(ifa->ifa_addr,&lsdl,min(sizeof(lsdl),ifa->ifa_addr->sa_len));
+ lsdl.sdl_alen = 0;
+ lsdl.sdl_nlen = 0;
+ lsdl.sdl_slen = 0;
+
+#ifdef DIAGNOSTIC
+ if (lsdl.sdl_index == 0)
+ panic("sdl_index is 0 in ipv6_new_neighbor().");
+#endif
+
+ /* ASSUMES: there is enough room for the link address shoved in here */
+ if (rtrequest(RTM_ADD,dst,gateway,netmask,flags,&newrt) == EEXIST) {
+ DPRINTF(FINISHED,("Can't add neighbor that already exists?\n"));
+ DDO(FINISHED, dump_smart_sockaddr(dst));
+ DDO(FINISHED, dump_smart_sockaddr(gateway));
+ DDO(FINISHED, dump_smart_sockaddr(netmask));
+ DP(FINISHED, flags, d);
+ }
+
+ if (newrt != NULL)
+ {
+ /* Fill in any other goodies, especially MTU. */
+ DDO(IDL_EVENT, printf("New route okay, before MTU setup...\n");\
+ dump_rtentry(newrt));
+ newrt->rt_rmx.rmx_mtu = newrt->rt_ifp->if_mtu;
+ newrt->rt_refcnt = 0; /* XXX - should decrement instead? */
+ DDO(IDL_EVENT,printf("New route okay, after MTU setup...\n");\
+ dump_rtentry(newrt));
+ }
+ return newrt;
+}
+
+/*----------------------------------------------------------------------
+ * ipv6_neighborsol_input():
+ * Handle input processing for Neighbor Solicit messages.
+ *
+ ----------------------------------------------------------------------*/
+void
+ipv6_neighborsol_input(incoming,extra)
+ struct mbuf *incoming;
+ int extra;
+{
+ struct ipv6 *ipv6;
+ struct ipv6_icmp *icmp;
+ struct rtentry *rt;
+ struct discq *dq;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_dl *sdl;
+ struct in6_ifaddr *i6a;
+ struct icmp_exthdr *ext = NULL;
+
+ /* Thanks to ipv6_icmp.h, ICMP_NEIGHBORADV_* are already in network order */
+ uint32_t flags = (ipv6forwarding) ? ICMPV6_NEIGHBORADV_RTR : 0;
+
+ if (incoming->m_flags & M_DAD) /* Incoming DAD solicit from me. Ignore. */
+ return;
+
+ /* Verify that length looks OK */
+ if (incoming->m_pkthdr.len - extra < ICMPV6_NSOLMINLEN)
+ return;
+
+ /* XXX - Assumes that the entire packet fits within MCLBYTES. */
+ if (incoming->m_len < incoming->m_pkthdr.len)
+ if (incoming = m_pullup2(incoming, incoming->m_pkthdr.len))
+ return;
+
+ ipv6 = mtod(incoming,struct ipv6 *);
+ icmp = (struct ipv6_icmp *)(mtod(incoming, caddr_t) + extra);
+
+ /* Check to see hop count is 255. */
+ if (ipv6->ipv6_hoplimit != 255)
+ {
+ /*
+ * Keep stats on this!
+ */
+ DPRINTF(IDL_ERROR,
+ ("Received Neighbor Solicit with hoplimit != 255.\n"));
+ return;
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&icmp->icmp_nsoltarg))
+ {
+ DPRINTF(IDL_EVENT, ("Received multicast address in solicit!\n"));
+ return;
+ }
+ /*
+ * Have a Neighbor Solicit message.
+ */
+
+ /* Verify this is for me. */
+ /* Eventually proxy & anycast checking will go here. */
+ for (i6a = in6_ifaddr ; i6a ; i6a = i6a->i6a_next)
+ if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &icmp->icmp_nsoltarg))
+ break;
+ if (i6a == NULL)
+ {
+ /* Not for me. */
+ return;
+ }
+
+ if (i6a->i6a_ifp != incoming->m_pkthdr.rcvif)
+ {
+ DPRINTF(IDL_ERROR,\
+ ("Received off-link Neighbor Solicit for self\n"));
+ return;
+ }
+
+ /*
+ * Can't process solicits for addresses in DAD phase.
+ * Furthermore, if solicit comes from all 0's, (and if I made it this far,
+ * it's not from me), then there's a duplicate.
+ */
+ if (i6a->i6a_addrflags & I6AF_NOTSURE)
+ {
+ if (IN6_IS_ADDR_UNSPECIFIED(&ipv6->ipv6_src))
+ {
+ struct socket so;
+ struct inet6_aliasreq ifra;
+#if __NetBSD__ || __FreeBSD__
+ struct proc proc;
+ struct pcred pcred;
+ struct ucred ucred;
+#endif /* __NetBSD__ || __FreeBSD__ */
+ printf("Duplicate address detected.\n"); /* NEED to print this. */
+ /*
+ * Delete in6_ifaddr.
+ */
+#if __NetBSD__ || __FreeBSD__
+ ucred.cr_uid = 0;
+ proc.p_cred = &pcred;
+ proc.p_ucred = &ucred;
+#else /* __NetBSD__ || __FreeBSD__ */
+ so.so_state = SS_PRIV;
+#endif /* __NetBSD__ || __FreeBSD__ */
+ strncpy(ifra.ifra_name,i6a->i6a_ifp->if_name,IFNAMSIZ);
+ ifra.ifra_addr = i6a->i6a_addr;
+ ifra.ifra_dstaddr = i6a->i6a_dstaddr;
+ ifra.ifra_mask = i6a->i6a_sockmask;
+#if __NetBSD__ || __FreeBSD__
+ in6_control(&so,SIOCDIFADDR_INET6,(caddr_t)&ifra,i6a->i6a_ifp,1, &proc);
+#else /* __NetBSD__ || __FreeBSD__ */
+ in6_control(&so,SIOCDIFADDR_INET6,(caddr_t)&ifra,i6a->i6a_ifp,1);
+#endif /* __NetBSD__ || __FreeBSD__ */
+ }
+ return;
+ }
+
+ /*
+ * Create neighbor cache entry for neighbor to send back advertisement.
+ */
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&ipv6->ipv6_src))
+ {
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_port = 0;
+ sin6.sin6_flowinfo = 0;
+ sin6.sin6_addr = ipv6->ipv6_src;
+
+#ifdef __FreeBSD__
+ rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
+#else /* __FreeBSD */
+ rt = rtalloc1((struct sockaddr *)&sin6, 0);
+#endif /* __FreeBSD */
+
+ /*
+ * I just did a passive route lookup. I'll either get:
+ *
+ * 1. No route, meaning I have to create one.
+ * 2. A host neighbor (RTF_LLINFO) route, meaning I might update it.
+ * 3. A host off-link (RTF_GATEWAY) route, meaning I possibly missed a
+ * redirect.
+ * 4. An on-link prefix (RTF_CLONING, no RTF_GATEWAY) route, meaning
+ * I have to create one, like no route.
+ * 5. The default route (RTF_DEFAULT), meaning the same as no route.
+ * 6. A network route, meaning either a subset of that prefix is on-link,
+ * or my routing table is bogus. I'll create one.
+ *
+ * In any case I actually get one, I should decrement the rt_refcnt.
+ *
+ * Future support for RTF_TUNNEL needed here.
+ */
+
+ DPRINTF(GROSSEVENT,("After rtalloc1().\n"));
+ if (rt == NULL || !(rt->rt_flags & RTF_HOST) )
+ {
+ /*
+ * No available host route, create a new entry.
+ */
+ if (rt != NULL)
+ rt->rt_refcnt--;
+
+ DPRINTF(GROSSEVENT,("Creating new neighbor.\n"));
+ rt = ipv6_new_neighbor(&sin6,incoming->m_pkthdr.rcvif);
+ if (rt == NULL)
+ {
+ DPRINTF(IDL_ERROR,
+ ("Can't allocate soliciting neighbor route.\n"));
+ return;
+ }
+ }
+ else if (rt->rt_flags & RTF_LLINFO)
+ {
+ rt->rt_refcnt--;
+ if (rt->rt_gateway->sa_family != AF_LINK) {
+ DPRINTF(IDL_ERROR, ("LLINFO but gateway != AF_LINK."));
+ return;
+ };
+ }
+ else
+ {
+ /*
+ * Received Neighbor Solicit from an address which I have
+ * an off-net host route for. For now, bail.
+ */
+ DPRINTF(FINISHED,
+ ("Received Neighbor Solicit from unknown target.\n"));
+ return;
+ }
+
+ /*
+ * If new, or inactive, set to probe.
+ */
+
+ /*
+ * All of this data will fit in one mbuf as long as the upper limit
+ * for ICMP message size <= 576 <= MCLBYTES.
+ */
+ if (incoming->m_pkthdr.len > extra + ICMPV6_NSOLMINLEN)
+ {
+ u_char *data = (u_char *)&icmp->icmp_nsolext;
+ u_char *bounds = data + incoming->m_pkthdr.len - extra -
+ ICMPV6_NSOLMINLEN;
+
+ /* Only possible extension (so far) in a neighbor advert
+ is a source link-layer address, but be careful anyway. */
+
+ ext = (struct icmp_exthdr *)data;
+ while (ext->ext_id != EXT_SOURCELINK && (data<bounds))
+ {
+ DPRINTF(FINISHED,("Got extension other than source link.\n"));
+ data += ext->ext_length<<3;
+ ext = (struct icmp_exthdr *)data;
+ }
+ if (data >= bounds) {
+ DPRINTF(IDL_ERROR, ("couldn't find SOURCELINK"));
+ return;
+ };
+ }
+
+ sdl = (struct sockaddr_dl *)rt->rt_gateway;
+ rt->rt_flags &= ~RTF_REJECT;
+ if (sdl->sdl_alen == 0) /* New or inactive */
+ {
+ if (rt->rt_ifa == NULL)
+ {
+ /*
+ * If multihomed and not sure of interface, take ifp and ifa
+ * for "My destination," where it does not have to be link-
+ * local.
+ */
+ rt->rt_ifa = (struct ifaddr *)i6a;
+ rt->rt_ifp = i6a->i6a_ifp;
+ rt->rt_ifa->ifa_refcnt++;
+ sdl->sdl_index = rt->rt_ifp->if_index;
+ sdl->sdl_type = rt->rt_ifp->if_type;
+ sdl->sdl_nlen = strlen(rt->rt_ifp->if_name);
+ bcopy(rt->rt_ifp->if_name,sdl->sdl_data,sdl->sdl_nlen);
+ rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
+ }
+
+#ifdef __FreeBSD__
+ rt->rt_rmx.rmx_expire = time_second;
+#else /* __FreeBSD__ */
+ rt->rt_rmx.rmx_expire = time.tv_sec;
+#endif /* __FreeBSD__ */
+ dq = (struct discq *)rt->rt_llinfo;
+ dq->dq_unanswered = -1; /* In STALE state */
+ /*
+ * Checks for non-broadcast multiple-access (NBMA) links
+ * such as PPP, Frame Relay, and ATM are probably needed here.
+ */
+ if (ext != NULL)
+ switch (rt->rt_ifp->if_type)
+ {
+ case IFT_ETHER:
+ sdl->sdl_alen = 6;
+ bcopy(ext->ext_data,LLADDR(sdl),sdl->sdl_alen);
+ break;
+ default:
+ DPRINTF(IDL_ERROR,("DANGER: Non-ethernet n. adv.\n"));
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * For now, ignore if I already have somewhat valid entry.
+ * Only adverts can affect changes on PROBE or STALE entries.
+ * (And then, only maybe.)
+ */
+ }
+
+ /*
+ * Neighbor Cache is now updated.
+ * Now, send out my unicast advertisement.
+ *
+ * NB: ICMPV6_* symbol was already htonl()'d in the header file.
+ */
+ flags |= ICMPV6_NEIGHBORADV_SOL|ICMPV6_NEIGHBORADV_OVERRIDE;
+ ipv6_nadvert(i6a,i6a->i6a_ifp,rt,flags);
+ }
+ else
+ {
+ /*
+ * Send multicast advertisement rather than unicast advertisement
+ * because the solicit contained the unspecified address.
+ *
+ * DAD code may need to be executed in here as well.
+ */
+ ipv6_nadvert(i6a,incoming->m_pkthdr.rcvif,NULL,flags);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ * ipv6_neighboradv_input():
+ * Handle reception of a Neighbor Advertisement message.
+ *
+ ----------------------------------------------------------------------*/
+void
+ipv6_neighboradv_input(incoming,extra)
+ struct mbuf *incoming;
+ int extra;
+{
+ struct ipv6 *ipv6;
+ struct ipv6_icmp *icmp;
+ struct rtentry *rt;
+ struct sockaddr_in6 sin6;
+ struct in6_ifaddr *i6a;
+ int s;
+
+ /* Verify that incoming length looks plausible */
+ if (incoming->m_pkthdr.len - extra < ICMPV6_NADVMINLEN)
+ return;
+
+ /* XXX - Assumes that the entire packet fits within MCLBYTES. */
+ if (incoming->m_len < incoming->m_pkthdr.len)
+ if (incoming = m_pullup2(incoming, incoming->m_pkthdr.len))
+ return;
+
+ ipv6 = mtod(incoming,struct ipv6 *);
+ icmp = (struct ipv6_icmp *)(mtod(incoming, caddr_t) + extra);
+
+ /* Verify hoplimit == 255 */
+ if (ipv6->ipv6_hoplimit != 255)
+ {
+ /*
+ * Keep stats on this!
+ */
+ DPRINTF(IDL_ERROR,
+ ("Received Neighbor Solicit with hoplimit != 255.\n"));
+ return;
+ }
+
+#if 0
+ /* Verify quickly src/node matching. Causes way too many false alarms. */
+ if (!IN6_ARE_ADDR_EQUAL(&ipv6->ipv6_src, &icmp->icmp_nadvaddr))
+ {
+ DPRINTF(IDL_ERROR, ("WARNING: Possible proxy, addrs are unequal.\n"));
+ DPRINTF(IDL_ERROR, ("src=%08x %08x %08x %08x\n", ntohl(ipv6->ipv6_src.in6a_u.words[0]), ntohl(ipv6->ipv6_src.in6a_u.words[1]), ntohl(ipv6->ipv6_src.in6a_u.words[2]), ntohl(ipv6->ipv6_src.in6a_u.words[3])));
+ DPRINTF(IDL_ERROR, ("nadvaddr=%08x %08x %08x %08x\n", ntohl(icmp->icmp_nadvaddr.in6a_u.words[0]), ntohl(icmp->icmp_nadvaddr.in6a_u.words[1]), ntohl(icmp->icmp_nadvaddr.in6a_u.words[2]), ntohl(icmp->icmp_nadvaddr.in6a_u.words[3])));
+ }
+#endif /* 0 */
+
+ if (IN6_IS_ADDR_MULTICAST(&icmp->icmp_nadvaddr))
+ {
+ DPRINTF(ERROR, ("Received Neighbor Advert with multicast address.\n"));
+ return; /* For now... */
+ }
+
+ /*
+ * Have a Neighbor Advertisement.
+ */
+
+ s = splnet();
+
+ /* Look to see if it's for one of my addresses. */
+ for (i6a = in6_ifaddr ; i6a ; i6a = i6a->i6a_next)
+ if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &icmp->icmp_nadvaddr))
+ break;
+ if (i6a != NULL)
+ {
+ if (i6a->i6a_addrflags & I6AF_NOTSURE)
+ {
+ struct socket so;
+ struct inet6_aliasreq ifra;
+#if __NetBSD__ || __FreeBSD__
+ struct proc proc;
+ struct pcred pcred;
+ struct ucred ucred;
+#endif /* __NetBSD__ || __FreeBSD__ */
+ printf("Duplicate address detected.\n"); /* NEED to print this. */
+ /*
+ * Delete in6_ifaddr.
+ */
+#if __NetBSD__ || __FreeBSD__
+ ucred.cr_uid = 0;
+ proc.p_cred = &pcred;
+ proc.p_ucred = &ucred;
+#else /* __NetBSD__ || __FreeBSD__ */
+ so.so_state = SS_PRIV;
+#endif /* __NetBSD__ || __FreeBSD__ */
+ strncpy(ifra.ifra_name,i6a->i6a_ifp->if_name,IFNAMSIZ);
+ ifra.ifra_addr = i6a->i6a_addr;
+ ifra.ifra_dstaddr = i6a->i6a_dstaddr;
+ ifra.ifra_mask = i6a->i6a_sockmask;
+#if __NetBSD__ || __FreeBSD__
+ in6_control(&so,SIOCDIFADDR_INET6,(caddr_t)&ifra,i6a->i6a_ifp,1, &proc);
+#else /* __NetBSD__ || __FreeBSD__ */
+ in6_control(&so,SIOCDIFADDR_INET6,(caddr_t)&ifra,i6a->i6a_ifp,1);
+#endif /* __NetBSD__ || __FreeBSD__ */
+ }
+ else
+ {
+ /* For now, ignore advert which is for me. */
+ }
+
+ splx(s);
+ return;
+ }
+
+ /* Lookup and see if I have something waiting for it... */
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = icmp->icmp_nadvaddr;
+
+ /* Next 2 lines might not be strictly needed since this is an rtalloc,
+ but they're included to be safe. */
+ sin6.sin6_port = 0;
+ sin6.sin6_flowinfo = 0;
+
+#ifdef __FreeBSD__
+ rt = rtalloc1((struct sockaddr *)&sin6,0,0UL);
+#else /* __FreeBSD__ */
+ rt = rtalloc1((struct sockaddr *)&sin6,0);
+#endif /* __FreeBSD__ */
+
+ if (rt != NULL)
+ rt->rt_refcnt--;
+
+ /*
+ * After passive route lookup, I have either:
+ *
+ * 1. No route, drop the advert.
+ * 2. Route with no RTF_HOST, drop the advert.
+ * 3. Route with no RTF_LLINFO, for now, drop the advert, this could be
+ * a redirect weirdness?
+ * 4. Route with RTF_LLINFO, try and update.
+ */
+
+ if (rt == NULL || !(rt->rt_flags & RTF_HOST))
+ {
+ /* Cases 1 and 2. */
+ splx(s);
+ return;
+ }
+
+ if (rt->rt_flags & RTF_LLINFO)
+ {
+ /* Case 4. */
+ struct sockaddr_dl *sdl;
+ struct icmp_exthdr *liext = NULL;
+
+ /*
+ * Possibly update the link-layer address, and maybe change to
+ * REACHABLE state.
+ */
+ rt->rt_flags &= ~RTF_REJECT; /* That neighbor talked to me! */
+
+ if (incoming->m_pkthdr.len - extra > ICMPV6_NADVMINLEN)
+ {
+ u_char *data = (u_char *)&icmp->icmp_nadvext;
+ u_char *bounds = data + incoming->m_pkthdr.len - extra -
+ ICMPV6_NADVMINLEN;
+ struct icmp_exthdr *ext = (struct icmp_exthdr *)data;
+
+ /* Only possible extension (so far) in a neighbor advert is a
+ source link-layer address, but be careful anyway. */
+
+ while (ext->ext_id != EXT_TARGETLINK && (data < bounds))
+ {
+ DPRINTF(IDL_EVENT,("Got extension other than source link.\n"));
+ data += ext->ext_length<<3;
+ ext = (struct icmp_exthdr *)data;
+ }
+ if (data >= bounds) {
+ DPRINTF(IDL_ERROR,("Received neighbor advertisement with no source link layer address.\n"));
+ splx(s);
+ return;
+ }
+ liext = ext;
+ }
+
+ sdl = (struct sockaddr_dl *)rt->rt_gateway;
+
+ if (liext != NULL)
+ {
+ if (rt->rt_ifa == NULL)
+ {
+ struct in6_ifaddr *i6a;
+
+ /*
+ * ifa and ifp for this address are null, because it's an
+ * on-link brute force discovery. Use link-local as source.
+ */
+ rt->rt_ifp = incoming->m_pkthdr.rcvif;
+ for (i6a = in6_ifaddr ; i6a != NULL; i6a = i6a->i6a_next)
+ if (i6a->i6a_ifp == rt->rt_ifp &&
+ (i6a->i6a_flags & I6AF_LINKLOC))
+ break;
+
+ if (i6a == NULL)
+ {
+ DPRINTF(IDL_ERROR,
+ ("Got advert from interface with no link-local.\n"));
+ splx(s);
+ return;
+ }
+ rt->rt_ifa = (struct ifaddr *)i6a;
+ rt->rt_ifa->ifa_refcnt++;
+ sdl->sdl_index = rt->rt_ifp->if_index;
+ sdl->sdl_type = rt->rt_ifp->if_type;
+ sdl->sdl_nlen = strlen(rt->rt_ifp->if_name);
+ bcopy(rt->rt_ifp->if_name,sdl->sdl_data,sdl->sdl_nlen);
+ rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
+ }
+
+ /*
+ * Update neighbor cache link address.
+ * Should make this code more general than just for Ethernet,
+ * so we switch off the interface type assigned to the route.
+ */
+ switch (rt->rt_ifp->if_type)
+ {
+ case IFT_ETHER:
+ if (sdl->sdl_alen == 0 ||
+ (icmp->icmp_nadvbits & ICMPV6_NEIGHBORADV_OVERRIDE))
+ {
+ sdl->sdl_alen = 6;
+ bcopy(&liext->ext_data,LLADDR(sdl),sdl->sdl_alen);
+ }
+ else if (bcmp(LLADDR(sdl),&liext->ext_data,6))
+ {
+ /*
+ * Override bit not set, and have link address already.
+ * Discard.
+ */
+ DPRINTF(IDL_ERROR,("Danger, got non-override with different link-local address.\n"));
+ splx(s);
+ return;
+ }
+ break;
+ }
+ /* The ICMP_NEIGHBORADV_* bits are already machine-specific.
+ So no HTONLs/NTOHLs need to be done here. */
+
+ /* Now in REACHABLE or STALE state, depending on
+ ICMP_NEIGHBORADV_SOL bit. */
+ {
+ struct discq *dq = (struct discq *)rt->rt_llinfo;
+
+#ifdef __FreeBSD__
+ rt->rt_rmx.rmx_expire = time_second +
+#else /* __FreeBSD__ */
+ rt->rt_rmx.rmx_expire = time.tv_sec +
+#endif /* __FreeBSD__ */
+ ((icmp->icmp_nadvbits & ICMPV6_NEIGHBORADV_SOL)?v6d_reachtime:0);
+ dq->dq_unanswered = -1;
+ if (dq->dq_queue != NULL)
+ {
+ rt->rt_ifp->if_output(rt->rt_ifp,dq->dq_queue, rt_key(rt),
+ rt);
+ dq->dq_queue = NULL;
+ }
+ }
+ }
+
+ /*
+ * Check for routers becoming hosts, and vice-versa.
+ */
+ if (icmp->icmp_nadvbits & ICMPV6_NEIGHBORADV_RTR)
+ rt->rt_flags |= RTF_ISAROUTER;
+ else if (rt->rt_flags & RTF_ISAROUTER)
+ {
+ /*
+ * Deal with router becoming host.
+ */
+ }
+ }
+ else
+ {
+ /* Case 3. */
+ /* Should consider adding a counter for these */
+ DPRINTF(ERROR,
+ ("Received Neighbor Advert for off-link host.\n"));
+ }
+ splx(s);
+}
+
+/*----------------------------------------------------------------------
+ * ipv6_redirect_input():
+ * Handle reception of a Redirect message.
+ *
+ ----------------------------------------------------------------------*/
+void
+ipv6_redirect_input(incoming,extra)
+ struct mbuf *incoming;
+ int extra;
+{
+ struct ipv6 *ipv6;
+ struct ipv6_icmp *icmp;
+ struct rtentry *rt;
+ struct sockaddr_in6 dst6, gate6, src6;
+#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
+ extern int icmp_redirtimeout;
+#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+
+ /* XXX - Assumes that the entire packet fits within MCLBYTES. */
+ if (incoming->m_len < incoming->m_pkthdr.len)
+ if (incoming = m_pullup2(incoming, incoming->m_pkthdr.len))
+ return;
+
+ ipv6 = mtod(incoming,struct ipv6 *);
+ icmp = (struct ipv6_icmp *)(mtod(incoming, caddr_t) + extra);
+
+ /* Verify source address is link-local */
+ if (!IN6_IS_ADDR_LINKLOCAL(&ipv6->ipv6_src)) {
+ DPRINTF(IDL_ERROR, ("Received Redirect with non link-local source address.\n"));
+ return;
+ };
+
+ /* Verify hoplimit == 255 */
+ if (ipv6->ipv6_hoplimit != 255) {
+ /*
+ * Keep stats on this!
+ */
+ DPRINTF(IDL_ERROR, ("Received IPv6 Redirect with hoplimit != 255.\n"));
+ return;
+ };
+
+ /* Verify that incoming length looks plausible */
+ if (incoming->m_pkthdr.len - extra < ICMPV6_REDIRMINLEN) {
+ DPRINTF(IDL_ERROR, ("Received IPv6 Redirect without enough data.\n"));
+ return;
+ };
+
+ /* Verify current next hop == IPv6 src addr */
+ bzero(&dst6, sizeof(struct sockaddr_in6));
+ dst6.sin6_len = sizeof(struct sockaddr_in6);
+ dst6.sin6_family = AF_INET6;
+ dst6.sin6_addr = icmp->icmp_redirdest;
+
+#ifdef __FreeBSD__
+ if (!(rt = rtalloc1((struct sockaddr *)&dst6, 0, 0UL))) {
+#else /* __FreeBSD__ */
+ if (!(rt = rtalloc1((struct sockaddr *)&dst6, 0))) {
+#endif /* __FreeBSD__ */
+ DPRINTF(IDL_ERROR, ("Received IPv6 Redirect for unreachable host.\n"));
+ return;
+ };
+
+ if ((rt->rt_gateway->sa_family != AF_INET6) || !IN6_ARE_ADDR_EQUAL(&ipv6->ipv6_src, &((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr)) {
+ DPRINTF(IDL_ERROR, ("Received IPv6 Redirect from wrong source.\n"));
+ return;
+ };
+
+ /* No redirects for multicast packets! */
+ if (IN6_IS_ADDR_MULTICAST(&icmp->icmp_redirdest)) {
+ DPRINTF(IDL_ERROR, ("Received Redirect with multicast address.\n"));
+ return; /* For now... (?) */
+ };
+
+ /* Target must be link local, or same as destination */
+ if (!IN6_IS_ADDR_LINKLOCAL(&icmp->icmp_redirtarg) && !IN6_ARE_ADDR_EQUAL(&icmp->icmp_redirtarg, &icmp->icmp_redirdest)) {
+ DPRINTF(IDL_ERROR, ("Received Redirect with non link-local target addr != dest addr.\n"));
+ return;
+ };
+
+ /*
+ * We have a valid Redirect.
+ */
+
+ bzero(&gate6, sizeof(struct sockaddr_in6));
+ gate6.sin6_len = sizeof(struct sockaddr_in6);
+ gate6.sin6_family = AF_INET6;
+ gate6.sin6_addr = icmp->icmp_redirtarg;
+
+ bzero(&src6, sizeof(struct sockaddr_in6));
+ src6.sin6_len = sizeof(struct sockaddr_in6);
+ src6.sin6_family = AF_INET6;
+ src6.sin6_addr = ipv6->ipv6_src;
+
+ rtredirect((struct sockaddr *)&dst6, (struct sockaddr *)&gate6,
+ NULL, RTF_DONE|RTF_GATEWAY|RTF_HOST, (struct sockaddr *)&src6,
+#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
+ icmp_redirtimeout);
+#else /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+ (struct rtentry **)0);
+#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+ return;
+};
+
+/*----------------------------------------------------------------------
+ * ipv6_discov_resolve():
+ * Called by LAN output routine. Will either queue outgoing packet
+ * on the discovery structure that hangs off the route and return 0,
+ * OR copies LAN MAC address into edst and returns 1.
+ *
+ * NOTES:
+ * Neighbor Unreachable Detection (NUD) should be done here.
+ * LAN output routine should handle IPv6 multicast -> link multicast
+ * mapping, unlike arp_resolve, this function doesn't handle multicast.
+ ----------------------------------------------------------------------*/
+
+int ipv6_discov_resolve(ifp, rt, outgoing, dst, edst)
+ struct ifnet *ifp;
+ struct rtentry *rt;
+ struct mbuf *outgoing;
+ struct sockaddr *dst;
+ u_char *edst;
+{
+ struct discq *dq = NULL;
+ struct sockaddr_dl *sdl;
+ struct ipv6 *ipv6;
+
+ DPRINTF(IDL_EVENT,("Entering ipv6_discov_resolve().\n"));
+ if (rt)
+ dq = (struct discq *)rt->rt_llinfo;
+ else
+ {
+#ifdef __FreeBSD__
+ rt = rtalloc1(dst, 0, 0UL);
+#else /* __FreeBSD__ */
+ rt = rtalloc1(dst, 0);
+#endif /* __FreeBSD__ */
+
+ /*
+ * I just did a passive route lookup. I'll either get:
+ *
+ * 1. No route, meaning I have to create one.
+ * 2. A host neighbor (RTF_LLINFO) route, meaning I'm good to go.
+ * 3. A host off-link route, meaning I possibly missed a redirect.
+ * 4. An on-link prefix (RTF_CLONING, no RTF_GATEWAY) route, meaning
+ * I have to create one, like no route.
+ * 5. The default route (RTF_DEFAULT), meaning the same as no route.
+ * 6. A network route, meaning either a subset of that prefix is on-link,
+ * or my routing table is invalid. I'll create a new route entry.
+ */
+
+ if (rt == NULL || !(rt->rt_flags & RTF_HOST))
+ {
+ /*
+ * No available host route, create a new entry.
+ * (Cases 1, 4, 5, 6.)
+ */
+ if (rt != NULL)
+ rt->rt_refcnt--;
+
+ rt = ipv6_new_neighbor((struct sockaddr_in6 *)dst,ifp);
+ if (rt == NULL)
+ {
+ DPRINTF(IDL_ERROR,
+ ("Can't allocate soliciting neighbor route.\n"));
+ m_freem(outgoing);
+ return 0;
+ }
+ }
+ else if (rt->rt_flags & RTF_LLINFO)
+ {
+ /* (Case 2) */
+ rt->rt_refcnt--;
+ if (rt->rt_gateway->sa_family != AF_LINK) {
+ DPRINTF(IDL_ERROR, ("LLINFO but gateway != AF_LINK."));
+ m_freem(outgoing);
+ return 0;
+ }
+ }
+ else
+ {
+ /*
+ * I just got a neighbor solicit from an address which I have
+ * an off-net host route for. For now, bail. (Case 3.)
+ */
+ DPRINTF(ERROR,
+ ("Received Neighbor Solicit from unknown target.\n"));
+ return 0;
+ }
+
+ dq = (struct discq *)rt->rt_llinfo;
+ }
+
+ if (dq == NULL)
+ {
+ DPRINTF(IDL_ERROR, ("No discq structure hanging off route.\n"));
+ m_freem(outgoing);
+ return 0;
+ }
+ if (dq->dq_rt != rt) {
+ DPRINTF(IDL_ERROR, ("discov_resolve route passed in (rt) != dq->dq_rt\n"));
+ m_freem(outgoing);
+ return 0;
+ };
+
+ sdl = (struct sockaddr_dl *)rt->rt_gateway;
+ if (sdl->sdl_family != AF_LINK) {
+ DPRINTF(IDL_ERROR, ("ipv6_discov_resolve called with rt->rt_gateway->sa_family == %d.\n", sdl->sdl_family));
+ m_freem(outgoing);
+ return 0;
+ };
+
+ if (sdl->sdl_alen == 0)
+ {
+ /*
+ * I'm in INCOMPLETE mode or a new entry.
+ *
+ * Also, if this is a LINK-LOCAL address, (or there is some other
+ * reason that it isn't clear which interface the address is on)
+ * I might want to send the solicit out all interfaces.
+ */
+
+ rt->rt_flags &= ~RTF_REJECT; /* Clear RTF_REJECT in case LAN output
+ routine caught expiration before
+ the timer did. */
+ ipv6_nsolicit(ifp,outgoing,rt);
+ return 0;
+ }
+
+#ifdef __FreeBSD__
+ if (dq->dq_unanswered < 0 && time_second >= rt->rt_rmx.rmx_expire)
+#else /* __FreeBSD__ */
+ if (dq->dq_unanswered < 0 && time.tv_sec >= rt->rt_rmx.rmx_expire)
+#endif /* __FreeBSD__ */
+ {
+ /*
+ * Timeout on REACHABLE entry. Process accordingly.
+ * Change the timed out REACHABLE entry into PROBE state.
+ * ( REACHABLE -> PROBE )
+ * PROBE state handling is the job of ipv6_discovery_timer().
+ */
+#ifdef __FreeBSD__
+ rt->rt_rmx.rmx_expire = time_second + v6d_delfirstprobe;
+#else /* __FreeBSD__ */
+ rt->rt_rmx.rmx_expire = time.tv_sec + v6d_delfirstprobe;
+#endif /* __FreeBSD__ */
+ dq->dq_unanswered = 0;
+ ipv6 = mtod(outgoing,struct ipv6 *);
+ }
+
+ DPRINTF(GROSSEVENT,("ipv6_discov_resolve() returning 1.\n"));
+ /*
+ * Right now, just trust sdl is set up right. May need to change this
+ * later.
+ */
+ bcopy(LLADDR(sdl),edst, sdl->sdl_alen);
+ return 1;
+}
+
+
+/*----------------------------------------------------------------------
+ * tunnel_parent():
+ * Set up tunnel state for a network (cloning?) tunnel route.
+ * Right now, there is no tunnel state unless an IPv6 secure tunnel
+ * (rt->rt_gateway->sa_family == AF_INET6 && (RTF_CRYPT || RTF_AUTH))
+ ----------------------------------------------------------------------*/
+
+void tunnel_parent(rt)
+ register struct rtentry *rt;
+{
+ struct rtentry *chaser = rt;
+
+ DPRINTF(GROSSEVENT,("ipv6_tunnel_parent():0000-Starting.\n"));
+ DDO(GROSSEVENT,printf(" rt_flags = 0x%x\n",(unsigned int)rt->rt_flags));
+
+ /*
+ * For now, set up master tunnel MTU. Chase rt_gwroute until no more, and
+ * see if there is either rmx_mtu or ifp->mtu to transfer. This should
+ * work on both cloning and non-cloning tunnel routes.
+ *
+ * Q: Do I want to chase it all the way? Or just to the next one?
+ * A: For now go to the next one. Change the following "if" to a
+ * "while" if you want to switch.
+ *
+ * Q2: For non-gateway tunnels (i.e. node-to-host tunnels), I may
+ * need to undo some braindamage. How?
+ */
+
+ /* Change "if" to "while" for all-the-way chasing. */
+ while (chaser->rt_gwroute != NULL)
+ chaser = chaser->rt_gwroute;
+
+ DDO(GROSSEVENT,printf("Last route in gwroute chain is:\n");\
+ dump_rtentry(rt));
+
+ if (chaser == rt)
+ {
+ /*
+ * If non-gateway tunnel, find a route for the gateway address.
+ */
+#ifdef __FreeBSD__
+ chaser = rtalloc1(rt->rt_gateway,0,0UL);
+#else /* __FreeBSD__ */
+ chaser = rtalloc1(rt->rt_gateway,0);
+#endif /* __FreeBSD__ */
+ if (chaser == NULL)
+ /*
+ * Oooh boy, you're on your own, kid!
+ */
+ chaser = rt;
+ else
+ {
+ chaser->rt_refcnt--;
+ /* else do I want to do that while loop again? */
+ }
+ }
+
+ if (chaser->rt_rmx.rmx_mtu != 0)
+ {
+ DPRINTF(GROSSEVENT,("Chaser's route MTU (%d) is set.\n",\
+ (int)chaser->rt_rmx.rmx_mtu));
+ rt->rt_rmx.rmx_mtu = chaser->rt_rmx.rmx_mtu;
+ }
+ else
+ {
+ DPRINTF(GROSSEVENT,("Chaser's route MTU is not set. "));
+ DPRINTF(GROSSEVENT,("Attempting ifp check.\n"));
+ if (chaser->rt_ifp == NULL)
+ {
+ DPRINTF(IDL_ERROR,\
+ ("Can't find ifp. Using IPV6_MINMTU (%d).\n",IPV6_MINMTU));
+ rt->rt_rmx.rmx_mtu = IPV6_MINMTU;
+ }
+ else
+ {
+ DPRINTF(FINISHED,("Found ifp with mtu of (%d).\n",\
+ (int)chaser->rt_ifp->if_mtu));
+ rt->rt_rmx.rmx_mtu = chaser->rt_ifp->if_mtu;
+ }
+ }
+
+ if (chaser->rt_ifp != rt->rt_ifp)
+ {
+ /*
+ * Somewhere along the way, things got messed up.
+ * (IPv4 tends to confuse ifa_ifwithroute(), and loopback happens.)
+ *
+ * For tunnels, set the rt_ifp to the interface the chaser finds.
+ * Hopefully ipv6_setrtifa() will do the right thing with the ifa.
+ */
+ rt->rt_ifp = chaser->rt_ifp;
+ }
+
+ /*
+ * Adjust based on any known encapsulations:
+ */
+
+ /* Use the rt->rt_gateway->sa_family to determine kind of tunnel */
+ if (rt->rt_gateway->sa_family == AF_INET) /* v6 in v4 tunnel */
+ {
+ /* WARNING: v6-in-v4 tunnel may also have secure bits set. */
+ rt->rt_rmx.rmx_mtu -= sizeof(struct ip);
+ }
+ else if (rt->rt_gateway->sa_family == AF_INET6)
+ {
+ /* Perhaps need to ensure that the rt_gateway is a neighbor. */
+ rt->rt_rmx.rmx_mtu -= sizeof(struct ipv6);
+ }
+
+#ifdef IPSEC
+ if (rt->rt_flags & (RTF_CRYPT|RTF_AUTH))
+ {
+ struct socket *so;
+
+ DPRINTF(GROSSEVENT,("tunnel_parent: Setting up secure tunnel state.\n"));
+
+ if (!(rt->rt_netproc = malloc(sizeof(*so), M_SOCKET, M_NOWAIT))) {
+ DPRINTF(IDL_ERROR, ("tunnel_parent: can't allocate fake socket\n"));
+ return;
+ }
+
+ so = (struct socket *)rt->rt_netproc;
+ bzero(so, sizeof(*so));
+ so->so_oobmark = 1; /* really the refcount */
+
+ if (netproc_alloc(so)) {
+ DPRINTF(IDL_ERROR, ("tunnel_parent: can't allocate netproc state\n"));
+ return;
+ }
+
+ {
+ struct netproc_requestandstate *r =
+ &((struct netproc_socketdata *)so->so_netproc)->requests[0];
+ void *p;
+
+ r->requestlen = ((rt->rt_flags & RTF_AUTH) ? sizeof(fixedauth) : 0)
+ + ((rt->rt_flags & RTF_CRYPT) ? sizeof(fixedencrypt) : 0);
+
+ if (!(r->request = malloc(r->requestlen, M_SOOPTS, M_NOWAIT))) {
+ DPRINTF(IDL_ERROR,("tunnel_child: can't allocate netproc request\n"));
+ r->requestlen = 0;
+ return;
+ }
+
+ p = r->request;
+
+ /* XXX - should be determined at runtime */
+ if (rt->rt_flags & RTF_AUTH) {
+ memcpy(p, &fixedauth, sizeof(fixedauth));
+ p += sizeof(fixedauth);
+ rt->rt_rmx.rmx_mtu -= IPSEC_AH_WORSTPREOVERHEAD +
+ IPSEC_AH_WORSTPOSTOVERHEAD;
+ }
+
+ if (rt->rt_flags & RTF_CRYPT) {
+ memcpy(p, &fixedencrypt, sizeof(fixedencrypt));
+ rt->rt_rmx.rmx_mtu -= IPSEC_ESP_WORSTPREOVERHEAD;
+ if (rt->rt_flags & RTF_AUTH)
+ rt->rt_rmx.rmx_mtu -= IPSEC_ESP_WORSTPOSTOVERHEAD_PLAIN;
+ else
+ rt->rt_rmx.rmx_mtu -= IPSEC_ESP_WORSTPOSTOVERHEAD_COMBINED;
+ }
+ }
+ }
+#endif /* IPSEC */
+ DPRINTF(GROSSEVENT,("ipv6_tunnel_parent():1000-Done.\n"));
+}
+
+/*----------------------------------------------------------------------
+ * tunnel_parent_clean():
+ * Frees dummy socket of secure tunnel.
+ * Any other tunnel state should also be cleaned up here for the parent.
+ * NOTE: Children won't be cleaned up.
+ ----------------------------------------------------------------------*/
+void tunnel_parent_clean(rt)
+ register struct rtentry *rt;
+{
+ DPRINTF(GROSSEVENT,("tunnel_parent_clean():0000-Starting.\n"));
+#ifdef IPSEC
+ if (rt->rt_flags & (RTF_CRYPT|RTF_AUTH))
+ {
+ struct socket *so = (struct socket *)rt->rt_netproc;
+
+ DPRINTF(GROSSEVENT,("tunnel_parent_clean():0500-It's RTF_CRYPT|RTF_AUTH; now removing.\n"));
+ if (so == NULL)
+ {
+ DPRINTF(IDL_ERROR,("WARNING: Secure tunnel w/o dummy socket.\n"));
+ return;
+ }
+
+ if (so->so_netproc)
+ if (--so->so_oobmark) /* Refcnt != 0 */
+ return;
+ else
+ netproc_free(so);
+
+ free(so, M_SOCKET);
+ rt->rt_netproc = NULL;
+ }
+#endif /* IPSEC */
+ DPRINTF(GROSSEVENT,("tunnel_parent_clean():1000-Finished.\n"));
+}
+
+/*----------------------------------------------------------------------
+ * tunnel_child():
+ * Set up tunnel state for a host or cloned tunnel route.
+ * Right now, there is no tunnel state except for secure tunnels
+ * ((RTF_CRYPT || RTF_AUTH))
+ ----------------------------------------------------------------------*/
+
+void tunnel_child(rt)
+ register struct rtentry *rt;
+{
+ struct sockaddr_in6 *dst = (struct sockaddr_in6 *)rt_key(rt);
+ struct sockaddr_in *sin;
+
+ DPRINTF(GROSSEVENT,("tunnel_child():0000-Starting.\n"));
+
+ /* Turn off STATIC flag only if cloned. */
+ if (rt->rt_parent != NULL)
+ rt->rt_flags &= ~RTF_STATIC;
+
+ /*
+ * If additional tunnel state were needed, it could be hung off rt_llinfo.
+ * August 14, 1996: Actually, we're now using rt_tunsec to hold the
+ * tunnel security state information. If we need to
+ * carry more state information, we'll have to rework
+ * this.
+ */
+
+ if (!(rt->rt_flags & RTF_GATEWAY) && rt->rt_parent != NULL)
+ {
+ /*
+ * If not a gateway route, and cloned, I'll have to do some transforms.
+ */
+ switch (rt->rt_gateway->sa_family)
+ {
+ case AF_INET:
+ if (IN6_IS_ADDR_V4COMPAT(&dst->sin6_addr))
+ {
+ /* Create new self-tunneling IPv6 over IPv4 route. */
+ /* DANGER: If original setgate doesn't work properly, this
+ could be trouble. */
+ sin = (struct sockaddr_in *)rt->rt_gateway;
+ sin->sin_addr.s_addr = dst->sin6_addr.in6a_words[3];
+ if (rt_setgate(rt,rt_key(rt),rt->rt_gateway))
+ {
+ DPRINTF(GROSSEVENT,
+ ("rt_setgate failed in tunnel_child().\n"));
+ rt->rt_rmx.rmx_mtu = 0;
+ /* More should probably be done here. */
+ }
+ }
+ /* else we're in BIG trouble. */
+ break;
+ }
+ }
+
+#ifdef IPSEC
+ if (rt->rt_flags & (RTF_CRYPT|RTF_AUTH))
+ {
+ u_long flagsformtu;
+
+ DPRINTF(GROSSEVENT,("tunnel_child():0300-Setting up secure host tunnel route.\n"));
+ if (rt->rt_parent == NULL)
+ {
+ struct socket *so;
+
+ DPRINTF(GROSSEVENT,("tunnel_child():400-Need to create a fresh socket.\n"));
+
+ if (!(rt->rt_netproc = malloc(sizeof(*so), M_SOCKET, M_NOWAIT))) {
+ DPRINTF(IDL_ERROR, ("tunnel_child: can't allocate fake socket\n"));
+ return;
+ }
+
+ so = (struct socket *)rt->rt_netproc;
+ bzero(so, sizeof(*so));
+ so->so_oobmark = 1; /* really the refcount */
+
+ if (netproc_alloc(so)) {
+ DPRINTF(IDL_ERROR, ("tunnel_child: can't allocate netproc state\n"));
+ return;
+ }
+
+ {
+ struct netproc_requestandstate *r =
+ &((struct netproc_socketdata *)so->so_netproc)->requests[0];
+ void *p;
+
+ r->requestlen = ((rt->rt_flags & RTF_AUTH) ? sizeof(fixedauth) : 0)
+ + ((rt->rt_flags & RTF_CRYPT) ? sizeof(fixedencrypt) : 0);
+
+ if (!(r->request = malloc(r->requestlen, M_SOOPTS, M_NOWAIT))) {
+ DPRINTF(IDL_ERROR,("tunnel_child: can't allocate netproc request\n"));
+ r->requestlen = 0;
+ return;
+ }
+
+ p = r->request;
+
+ if (rt->rt_flags & RTF_AUTH) {
+ memcpy(p, &fixedauth, sizeof(fixedauth));
+ p += sizeof(fixedauth);
+ }
+
+ if (rt->rt_flags & RTF_CRYPT)
+ memcpy(p, &fixedencrypt, sizeof(fixedencrypt));
+ }
+
+ flagsformtu = rt->rt_flags;
+ } else {
+ struct socket *so = (struct socket *)rt->rt_parent->rt_netproc;
+
+ DPRINTF(GROSSEVENT,("tunnel_child: Using parent's socket.\n"));
+ if (!so) {
+ DPRINTF(ERROR, ("tunnel_child: No socket in parent!\n"));
+ return;
+ }
+
+ rt->rt_netproc = so;
+
+ /* XXX - Bump refcount. This will break if you have more than about
+ four million children, but you'll have bigger problems way before
+ that ever happens - cmetz */
+ so->so_oobmark++;
+
+ flagsformtu = rt->rt_parent->rt_flags;
+ }
+
+ /* XXX - This block of code was a reasonable hack at the time, but isn't
+ a good idea anymore. - cmetz */
+ /* Security Level should be more configurable, possibly using
+ a sysctl or by calling the IPsec security policy engine */
+ if (flagsformtu & RTF_AUTH)
+ rt->rt_rmx.rmx_mtu -= IPSEC_AH_WORSTPREOVERHEAD +
+ IPSEC_AH_WORSTPOSTOVERHEAD;
+ if (flagsformtu & RTF_CRYPT) {
+ rt->rt_rmx.rmx_mtu -= IPSEC_ESP_WORSTPREOVERHEAD;
+ if (rt->rt_flags & RTF_AUTH)
+ rt->rt_rmx.rmx_mtu -= IPSEC_ESP_WORSTPOSTOVERHEAD_PLAIN;
+ else
+ rt->rt_rmx.rmx_mtu -= IPSEC_ESP_WORSTPOSTOVERHEAD_COMBINED;
+ }
+ }
+#endif /* IPSEC */
+
+ if (rt->rt_gwroute != NULL)
+ {
+ if (rt->rt_gwroute->rt_ifp != rt->rt_ifp)
+ rt->rt_ifp = rt->rt_gwroute->rt_ifp;
+ /* Should also handle Path MTU increases. */
+ }
+ /* else assume ifp is good to go. */
+ DPRINTF(GROSSEVENT,("tunnel_child():1000-Finished.\n"));
+}
+
+/*----------------------------------------------------------------------
+ * tunnel_child_clean():
+ * probably does something like tunnel_parent_clean...
+ ----------------------------------------------------------------------*/
+void tunnel_child_clean(rt)
+ register struct rtentry *rt;
+{
+ DPRINTF(GROSSEVENT,("tunnel_child_clean():0000-Starting.\n"));
+
+#ifdef IPSEC
+ if (rt->rt_flags & (RTF_CRYPT|RTF_AUTH))
+ {
+ struct socket *so = (struct socket *)rt->rt_netproc;
+
+ DPRINTF(GROSSEVENT,("tunnel_child_clean():-Removing security state.\n"));
+ if (so == NULL)
+ {
+ DPRINTF(IDL_ERROR,("WARNING: tunnel_child_clean() Secure tunnel w/o dummy socket.\n"));
+ return;
+ }
+
+ if (so->so_netproc)
+ if (--so->so_oobmark) /* Refcnt != 0 */
+ return;
+ else
+ netproc_free(so);
+
+ free(so, M_SOCKET);
+ rt->rt_netproc = NULL;
+ }
+#endif /* IPSEC */
+ DPRINTF(GROSSEVENT,("tunnel_child_clean():1000-Finished.\n"));
+}
diff --git a/sys/netinet6/ipv6_icmp.c b/sys/netinet6/ipv6_icmp.c
new file mode 100644
index 00000000000..843fc2ddcd7
--- /dev/null
+++ b/sys/netinet6/ipv6_icmp.c
@@ -0,0 +1,1077 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+
+#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 <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+
+#include <netinet6/in6_var.h>
+#include <netinet6/ipv6.h>
+#include <netinet6/ipv6_var.h>
+#include <netinet6/ipv6_icmp.h>
+#include <netinet6/icmpv6_var.h>
+
+#if __OpenBSD__ && defined(NRL_IPSEC)
+#define IPSEC 1
+#endif /* __OpenBSD__ && defined(NRL_IPSEC) */
+
+#ifdef IPSEC
+#include <sys/osdep.h>
+#include <net/netproc.h>
+#include <net/netproc_var.h>
+
+extern struct netproc_security fixedencrypt;
+extern struct netproc_auth fixedauth;
+#endif /* IPSEC */
+
+#include <sys/debug.h>
+
+#if __FreeBSD__
+#include <sys/sysctl.h>
+#endif /* __FreeBSD__ */
+
+/*
+ * Globals.
+ */
+
+static struct sockaddr_in6 icmpsrc = { sizeof (struct sockaddr_in6),
+ AF_INET6 };
+static struct sockaddr_in6 icmpdst = { sizeof (struct sockaddr_in6),
+ AF_INET6 };
+struct icmpv6stat icmpv6stat;
+
+/*
+ * External globals.
+ */
+
+extern struct in6_ifaddr *in6_ifaddr;
+extern u_char ipv6_protox[];
+extern struct protosw inet6sw[];
+
+/*
+ * Functions and macros (and one global that needs to be near the function).
+ */
+
+uint8_t ipv6_saved_routing[384]; /* If a routing header has been processed,
+ then it will have been bcopy()d into
+ this buffer. If not, the first byte
+ will be set to zero (which would be
+ a nexthdr of hop-by-hop, and is not
+ valid). */
+
+struct mbuf *ipv6_srcrt __P((void));
+void ipv6_icmp_reflect __P((struct mbuf *, int));
+void update_pathmtu __P((struct in6_addr *, uint32_t));
+
+/* This is broken as of getting rid of hdrindex and the mbuf structure it
+ creates. While this needs to be fixed, it's a Real Shame that source
+ route reflection couldn't be reimplemented for this release... - cmetz */
+#if 0
+/*----------------------------------------------------------------------
+ * Reverse a saved IPv6 source route, for possible use on replies.
+ ----------------------------------------------------------------------*/
+
+struct mbuf *ipv6_srcrt()
+{
+ struct ipv6_srcroute0 *sr, *osr = (struct ipv6_srcroute0 *)ipv6_saved_routing;
+ struct in6_addr *sra, *osra;
+ struct mbuf *srm;
+ int i, j;
+
+ if (!osr->i6sr_nexthdr)
+ return NULL;
+ if (osr->i6sr_type)
+ return NULL;
+ if (!(srm = m_get(M_DONTWAIT, MT_DATA)))
+ return NULL;
+
+ sr = mtod(srm, struct ipv6_srcroute0 *);
+ bzero(sr, sizeof(struct ipv6_srcroute0));
+ sr->i6sr_nexthdr = IPPROTO_ICMPV6;
+ sr->i6sr_len = osr->i6sr_len;
+ j = sr->i6sr_left = sr->i6sr_len/2;
+/* We probably should reverse the bit mask, but it's painful, and defaulting
+ to loose source routing might be preferable anyway. */
+ sra = (struct in6_addr *)((caddr_t)sr + sizeof(struct ipv6_srcroute0));
+ osra = (struct in6_addr *)((caddr_t)osr + sizeof(struct ipv6_srcroute0));
+ srm->m_len = sizeof(struct ipv6_srcroute0) + sizeof(struct in6_addr) * j;
+ for (i = 0; i < sr->i6sr_len/2; sra[i++] = osra[j--]);
+ return srm;
+}
+#endif /* 0 */
+
+/*----------------------------------------------------------------------
+ * Reflect an IPv6 ICMP packet back to the source.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_icmp_reflect(m, extra)
+ struct mbuf *m;
+ int extra;
+{
+ struct in6_addr tmp;
+ struct ipv6 *ipv6;
+ struct in6_ifaddr *i6a;
+ struct ipv6_icmp *icmp;
+#if 0
+ struct mbuf *routing = NULL;
+#endif /* 0 */
+#ifdef IPSEC
+ struct socket *socket, fake;
+#endif /* IPSEC */
+
+ /*
+ * Hmmm, we potentially have authentication, routing, and hop-by-hop
+ * headers behind this. OUCH. For now, however, assume only IPv6
+ * header, followed by ICMP.
+ */
+
+ DP(FINISHED, extra, d);
+
+ ipv6 = mtod(m, struct ipv6 *);
+ icmp = (struct ipv6_icmp *)(mtod(m, caddr_t) + extra);
+
+ tmp = ipv6->ipv6_dst;
+ ipv6->ipv6_dst = ipv6->ipv6_src;
+
+ /*
+ * If the incoming packet was addressed directly to us,
+ * use dst as the src for the reply. Otherwise (multicast
+ * or anonymous), use the address which corresponds
+ * to the incoming interface.
+ */
+
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ {
+ /* Find first (non-local if possible) address for
+ source usage. If multiple locals, use last one found. */
+
+ if (IN6_ARE_ADDR_EQUAL(&tmp, &I6A_SIN(i6a)->sin6_addr))
+ break;
+ }
+ icmpdst.sin6_addr = tmp;
+
+ if (i6a == NULL && m->m_pkthdr.rcvif != NULL)
+ i6a = (struct in6_ifaddr *)ifaof_ifpforaddr((struct sockaddr *)&icmpdst,
+ m->m_pkthdr.rcvif);
+
+ if (i6a == NULL)
+ {
+ /* Want globally-routable if I can help it. */
+ i6a = in6_ifaddr;
+ }
+
+ ipv6->ipv6_src = I6A_SIN(i6a)->sin6_addr;
+
+ ipv6->ipv6_nexthdr = IPPROTO_ICMPV6;
+
+ if (extra > sizeof(struct ipv6)) {
+ DP(FINISHED, extra, 08x);
+ DP(FINISHED, m->m_pkthdr.len, 08x);
+ ipv6_stripoptions(m, extra);
+ DP(FINISHED, m->m_pkthdr.len, 08x);
+ extra = sizeof(struct ipv6);
+#if 0
+ if ((routing = ipv6_srcrt())) {
+ ipv6->ipv6_nexthdr = IPPROTO_ROUTING;
+ mtod(routing, struct ipv6_srcroute0 *)->i6sr_nexthdr = IPPROTO_ICMPV6;
+ routing->m_next = m->m_next;
+ m->m_next = routing;
+ extra += routing->m_len;
+ } else
+ DPRINTF(IDL_ERROR, ("icmp_reflect() got options but can't strip them\n"));
+#endif /* 0 */
+ }
+
+ m->m_flags &= ~(M_BCAST|M_MCAST);
+
+ DP(FINISHED, m->m_pkthdr.len, d);
+
+ /* For errors, anything over the 576 byte mark we discard. */
+ if (!(ICMPV6_INFOTYPE(icmp->icmp_type)))
+ if (m->m_pkthdr.len > ICMPV6_MAXLEN)
+ m_adj(m, -(m->m_pkthdr.len - ICMPV6_MAXLEN));
+
+ DP(FINISHED, m->m_pkthdr.len - extra, d);
+
+ icmp->icmp_cksum = 0;
+ DPRINTF(IDL_EVENT,("ipv6_icmp_reflect() calling in6_cksum().\n"));
+ icmp->icmp_cksum = in6_cksum(m,IPPROTO_ICMPV6, m->m_pkthdr.len - extra,
+ extra);
+ DP(FINISHED, icmp->icmp_cksum, 04x);
+
+ ipv6->ipv6_hoplimit = 255;
+ /* Version 6, priority 12 (info) or 15 (error), flow zero */
+ ipv6->ipv6_versfl = htonl(((6) << 28) |
+ ((ICMPV6_INFOTYPE(icmp->icmp_type) ? 12 : 15) << 24));
+
+#ifdef IPSEC
+ /*
+ * Packet sent should be authenticated/encrypted if responding to
+ * received packet that was authenticated/encrypted.
+ */
+ if (m->m_flags & (M_AUTHENTIC | M_DECRYPTED)) {
+ struct netproc_requestandstate *r;
+
+ bzero(&fake, sizeof(struct socket));
+
+ if (netproc_alloc(&fake)) {
+ DPRINTF(ERROR, ("icmp_send: netproc_alloc failed\n"));
+ m_freem(m);
+ return;
+ }
+
+ r = &((struct netproc_socketdata *)fake.so_netproc)->requests[0];
+ r->requestlen = ((m->m_flags & M_AUTHENTIC) ?
+ sizeof(struct netproc_auth) : 0) +
+ ((m->m_flags & M_DECRYPTED) ?
+ sizeof(struct netproc_security) : 0);
+ if (!(r->request = OSDEP_MALLOC(r->requestlen))) {
+ DPRINTF(ERROR, ("icmp_send: malloc(%d) failed\n",
+ r->requestlen));
+ netproc_free(&fake);
+ m_freem(m);
+ return;
+ }
+
+ {
+ void *p = r->request;
+
+ if (m->m_flags & M_AUTHENTIC) {
+ memcpy(p, &fixedauth, sizeof(struct netproc_auth));
+ p += sizeof(fixedauth);
+ }
+ if (m->m_flags & M_DECRYPTED)
+ memcpy(p, &fixedencrypt, sizeof(struct netproc_security));
+ }
+
+ socket = &fake;
+ } else
+ socket = NULL;
+#endif /* IPSEC */
+
+ icmpv6stat.icps_outhist[icmp->icmp_type]++;
+
+#ifdef IPSEC
+ ipv6_output(m, NULL, IPV6_RAWOUTPUT, NULL, NULL, socket);
+
+ if (socket)
+ netproc_free(socket);
+#else /* IPSEC */
+ ipv6_output(m, NULL, IPV6_RAWOUTPUT, NULL, NULL, NULL);
+#endif /* IPSEC */
+}
+
+/*----------------------------------------------------------------------
+ * Given a bad packet (badpack), generate an ICMP error packet in response.
+ * We assume that ipv6_preparse() has been run over badpack.
+ *
+ * Add rate-limiting code to this function, on a timer basis.
+ * (i.e. if t(current) - t(lastsent) < limit, then don't send a message.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_icmp_error(badpack, type, code, paramptr)
+ struct mbuf *badpack;
+ int type,code;
+ uint32_t paramptr;
+{
+ struct ipv6 *oipv6;
+ int divpoint = sizeof(struct ipv6);
+ struct ipv6_icmp *icmp;
+ struct mbuf *outgoing;
+
+ if ((badpack->m_flags & M_MCAST) /*&& type != ICMPV6_TOOBIG */) {
+ m_freem(badpack);
+ return;
+ }
+
+ /*if (type != ICMPV6_REDIRECT)*/
+ icmpv6stat.icps_error++;
+
+ /*
+ * Since MTU and max ICMP packet size is less than a cluster (so far...)
+ * pull the offending packet into a single cluster.
+ *
+ * If option-stripping becomes required, here might be the place to do it.
+ * (The current design decision is to not strip options. Besides, one of
+ * the callers of this function is ipv6_hop(), which does hop-by-hop
+ * option processing.)
+ */
+
+ oipv6 = mtod(badpack,struct ipv6 *);
+
+ DDO(GROSSEVENT,printf("oipv6 (0x%lx) is:\n",(unsigned long)oipv6);dump_ipv6(oipv6));
+
+ DP(FINISHED, badpack->m_pkthdr.len, d);
+
+ /*
+ * Get a new cluster mbuf for ICMP error message. Since IPv6 ICMP messages
+ * have a length limit that should be less than MCLBYTES, one cluster should
+ * work nicely.
+ */
+
+ if (!(outgoing = m_gethdr(M_DONTWAIT,MT_HEADER))) {
+ m_freem(badpack);
+ return;
+ };
+
+ MCLGET(outgoing, M_DONTWAIT);
+ if (!(outgoing->m_flags & M_EXT)) {
+ m_freem(badpack);
+ m_free(outgoing);
+ return;
+ };
+
+ outgoing->m_len = sizeof(struct ipv6) + ICMPV6_MINLEN;
+ bcopy(oipv6, mtod(outgoing, caddr_t), sizeof(struct ipv6));
+ icmp = (struct ipv6_icmp *)(mtod(outgoing, caddr_t) + sizeof(struct ipv6));
+
+ {
+ int i = badpack->m_pkthdr.len;
+
+ if (i > ICMPV6_MAXLEN - sizeof(struct ipv6) - sizeof(struct icmpv6hdr))
+ i = ICMPV6_MAXLEN - sizeof(struct ipv6) - sizeof(struct icmpv6hdr);
+
+ outgoing->m_len += i;
+ /* Copies are expensive, but linear buffers are nice. Luckily, the data
+ is bounded, short, and ICMP errors aren't that performance critical. */
+ m_copydata(badpack, 0, i, mtod(outgoing, caddr_t) +
+ sizeof(struct ipv6) + sizeof(struct icmpv6hdr));
+ }
+
+ /* Need rcvif to do source address selection later */
+ outgoing->m_pkthdr.rcvif = badpack->m_pkthdr.rcvif;
+
+ outgoing->m_pkthdr.len = outgoing->m_len;
+
+#if defined(IPSEC) || defined(NRL_IPSEC)
+ /*
+ * Copy over the DECRYPTED and AUTHENTIC flag.
+ * NB: If the inbound packet was sent to us in an authenticated
+ * or encrypted tunnel, these flags are cleared when we get here.
+ * We don't have a way to preserve tunnel state at the moment.
+ * This is a hole we need to fix soon.
+ */
+ outgoing->m_flags |= badpack->m_flags & (M_DECRYPTED | M_AUTHENTIC);
+ DDO(IDL_ERROR,if (outgoing->m_flags & M_AUTHENTIC) printf("icmpv6_error processing authentic pkt\n"));
+ DDO(IDL_ERROR,if (outgoing->m_flags & M_DECRYPTED) printf("icmpv6_error processing encrypted pkt\n"));
+#endif /* defined(IPSEC) || defined(NRL_IPSEC) */
+
+ m_freem(badpack);
+
+ icmp->icmp_type = type;
+ icmp->icmp_code = code;
+ icmp->icmp_unused = 0;
+ if (type == ICMPV6_PARAMPROB || type == ICMPV6_TOOBIG)
+ icmp->icmp_paramptr = htonl(paramptr);
+
+ ipv6_icmp_reflect(outgoing, divpoint);
+}
+
+/*----------------------------------------------------------------------
+ * Update path MTU for an IPv6 destination. This function may have to go
+ * scan for TCP control blocks and give them hints to scale down.
+ * There is a small denial-of-service attack if MTU messages are
+ * unauthenticated. I can lower MTU to 576.
+ ----------------------------------------------------------------------*/
+
+void
+update_pathmtu(dst, newmtu)
+ struct in6_addr *dst;
+ uint32_t newmtu; /* ntohl()'ed by caller. */
+{
+ int s = splnet();
+ struct rtentry *rt;
+ struct sockaddr_in6 sin6;
+
+ DDO(IDL_EVENT,printf("Entering update_pathmtu with:\n");\
+ dump_in6_addr(dst);printf("And newmtu of %d.\n",(unsigned int)newmtu));
+
+ if (IN6_IS_ADDR_MULTICAST(dst))
+ {
+ /* Multicast MTU Discovery not yet implemented */
+ DPRINTF(IDL_ERROR, ("Multicast MTU too big message.\n"));
+ splx(s);
+ return;
+ }
+
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_addr = *dst;
+
+ /*
+ * Since I'm doing a rtalloc, no need to zero-out the port and flowlabel.
+ */
+#ifdef __FreeBSD__
+ if ((rt = rtalloc1((struct sockaddr *)&sin6,0,0UL)) != NULL)
+#else /* __FreeBSD__ */
+ if ((rt = rtalloc1((struct sockaddr *)&sin6,0)) != NULL)
+#endif /* __FreeBSD__ */
+ {
+ if (rt->rt_flags & RTF_HOST)
+ {
+ if (rt->rt_rmx.rmx_mtu < newmtu)
+ DPRINTF(IDL_ERROR,
+ ("DANGER: New MTU message is LARGER than current MTU.\n"));
+
+ rt->rt_rmx.rmx_mtu = newmtu; /* This should be enough for HLP's to
+ know MTU has changed, IMHO. */
+ rt->rt_refcnt--;
+ }
+ else
+ {
+ DPRINTF(IDL_ERROR,
+ ("Got path MTU message for non-cloned destination route.\n"));
+ }
+ /*
+ * Find all active tcp connections, and indicate they need path MTU
+ * updating as well.
+ *
+ * Also, find RTF_TUNNEL routes that point to this updated route,
+ * because they need their path MTU lowered. Perhaps decapsulating
+ * the message, and sending TOOBIG messages back.
+ */
+ }
+
+ splx(s);
+ return;
+}
+
+/*----------------------------------------------------------------------
+ * ICMPv6 input routine. Handles inbound ICMPv6 packets, including
+ * direct handling of some packets.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_icmp_input(incoming, extra)
+ register struct mbuf *incoming;
+ int extra;
+{
+ struct ipv6_icmp *icmp;
+ struct ipv6 *ipv6;
+ int icmplen,code;
+ void (*ctlfunc) __P((int, struct sockaddr *, struct ipv6 *, struct mbuf *));
+
+ /*
+ * Q: Any address validity checks beyond those in ipv6_input()?
+ */
+
+ DPRINTF(FINISHED, ("ipv6_icmp_input -- pkthdr.len = %d, extra = %d\n",
+ incoming->m_pkthdr.len, extra));
+
+ DDO(FINISHED, dump_mbuf_tcpdump(incoming));
+
+ icmplen = incoming->m_pkthdr.len - extra;
+ if (icmplen < ICMPV6_MINLEN)
+ {
+ /* Not enough for full ICMP packet. */
+ icmpv6stat.icps_tooshort++;
+ m_freem(incoming);
+ return;
+ }
+
+ if (incoming->m_len < extra + ICMPV6_MINLEN)
+ if (!(incoming = m_pullup2(incoming, extra + ICMPV6_MINLEN)))
+ return;
+
+ DDO(FINISHED, dump_mbuf_tcpdump(incoming));
+
+ ipv6 = mtod(incoming, struct ipv6 *);
+ icmp = (struct ipv6_icmp *)(mtod(incoming, caddr_t) + extra);
+
+ /*
+ * Verify checksum with IPv6 header at the top of this chain.
+ */
+
+ DPRINTF(IDL_EVENT,("ipv6_icmp_input() calling in6_cksum().\n"));
+ DPRINTF(IDL_EVENT,("icmplen = %d\n", icmplen));
+ {
+ unsigned int cksum;
+
+ if (cksum = in6_cksum(incoming, IPPROTO_ICMPV6, icmplen, extra))
+ {
+ DPRINTF(IDL_ERROR,("ipv6_icmp_input() -- checksum returned %08x.\n", cksum));
+ m_freem(incoming);
+ icmpv6stat.icps_checksum++;
+ return;
+ }
+ }
+
+#ifdef IPSEC
+ /* Perform input-side policy check. Drop packet if policy says to drop it. */
+ {
+ struct sockaddr_in6 srcsa, dstsa;
+
+ bzero(&srcsa, sizeof(struct sockaddr_in6));
+ srcsa.sin6_family = AF_INET6;
+ srcsa.sin6_len = sizeof(struct sockaddr_in6);
+ srcsa.sin6_addr = ipv6->ipv6_src;
+
+ bzero(&dstsa, sizeof(struct sockaddr_in6));
+ dstsa.sin6_family = AF_INET6;
+ dstsa.sin6_len = sizeof(struct sockaddr_in6);
+ dstsa.sin6_addr = ipv6->ipv6_dst;
+
+ /* XXX - state arg should NOT be NULL, it should be the netproc state
+ carried up the stack - cmetz */
+ if (netproc_inputpolicy(NULL, (struct sockaddr *)&srcsa,
+ (struct sockaddr *)&dstsa, IPPROTO_ICMPV6,
+ incoming, NULL, NULL)) {
+ m_freem(incoming);
+ return;
+ }
+ }
+#endif /* IPSEC */
+
+ code = icmp->icmp_code;
+ DPRINTF(IDL_EVENT, ("icmp->icmp_type = %d\n", icmp->icmp_type));
+
+ if (icmp->icmp_type < ICMPV6_MAXTYPE + 1)
+ {
+ icmpv6stat.icps_inhist[icmp->icmp_type]++;
+
+ /*
+ * Deal with the appropriate ICMPv6 message type/code.
+ */
+ switch(icmp->icmp_type)
+ {
+ case ICMPV6_ECHO:
+ icmp->icmp_type = ICMPV6_ECHOREPLY;
+ icmpv6stat.icps_reflect++;
+ ipv6_icmp_reflect(incoming, extra);
+ return;
+ case ICMPV6_UNREACH:
+ /*
+ * The pair of <type,code> should map into a PRC_*
+ * value such that I don't have to rewrite in_pcb.c.
+ */
+ switch (code)
+ {
+ case ICMPV6_UNREACH_NOROUTE:
+ code = PRC_UNREACH_NET;
+ break;
+ case ICMPV6_UNREACH_ADMIN:
+ /* Subject to change */
+ code = PRC_UNREACH_HOST;
+ break;
+ case ICMPV6_UNREACH_NOTNEIGHBOR:
+ /* Subject to change */
+ code = PRC_UNREACH_HOST;
+ break;
+ case ICMPV6_UNREACH_ADDRESS:
+ code = PRC_HOSTDEAD;
+ break;
+ case ICMPV6_UNREACH_PORT:
+ code = PRC_UNREACH_PORT;
+ break;
+ default:
+ goto badcode;
+ }
+ goto deliver;
+ break;
+ case ICMPV6_TIMXCEED:
+ if (code >1)
+ goto badcode;
+ code += PRC_TIMXCEED_INTRANS;
+ goto deliver;
+
+ case ICMPV6_PARAMPROB:
+ if (code >2)
+ goto badcode;
+ code = PRC_PARAMPROB;
+
+ case ICMPV6_TOOBIG:
+ deliver:
+ /*
+ * Problem with datagram, advice HLP's.
+ */
+ DPRINTF(IDL_EVENT, ("delivering\n"));
+ if (icmplen < ICMPV6_HLPMINLEN)
+ {
+ icmpv6stat.icps_badlen++;
+ m_freem(incoming);
+ return;
+ }
+
+ /* May want to pullup more than this */
+ if (!(incoming = m_pullup2(incoming, extra + ICMPV6_HLPMINLEN)))
+ return;
+
+ /*
+ * If cannot determine HLP, discard packet.
+ *
+ * For now, I assume that ICMP messages will be generated such that
+ * the enclosed header contains only IPv6+<HLP header>. This is not
+ * a good assumption to make in light of all sorts of options between
+ * IPv6 and the relevant place to deliver this message.
+ */
+ {
+ struct ipv6 *ipv6 = (struct ipv6 *)(mtod(incoming, caddr_t) + extra + ICMPV6_MINLEN);
+ icmpsrc.sin6_addr = ipv6->ipv6_dst;
+
+ if (icmp->icmp_type == ICMPV6_TOOBIG)
+ {
+ update_pathmtu(&ipv6->ipv6_dst,htonl(icmp->icmp_nexthopmtu));
+ /* If I want to deliver to HLP, remove the break, and
+ set code accordingly. */
+ break;
+ }
+ DPRINTF(IDL_EVENT, ("Finding control function for %d\n",
+ ipv6->ipv6_nexthdr));
+ if ((ctlfunc = (void *)inet6sw[ipv6_protox[ipv6->ipv6_nexthdr]].pr_ctlinput)) {
+ DPRINTF(IDL_EVENT, ("Calling control function for %d\n",
+ ipv6->ipv6_nexthdr));
+ (*ctlfunc)(code, (struct sockaddr *)&icmpsrc, ipv6,incoming);
+ }
+ }
+ break;
+
+ badcode:
+ DPRINTF(IDL_EVENT, ("Bad code!\n"));
+ icmpv6stat.icps_badcode++;
+ break;
+
+ /*
+ * IPv6 multicast group messages.
+ */
+ case ICMPV6_GRPQUERY:
+ case ICMPV6_GRPREPORT:
+ case ICMPV6_GRPTERM:
+ break;
+
+ /*
+ * IPv6 discovery messages.
+ */
+ case ICMPV6_ROUTERSOL:
+ ipv6_routersol_input(incoming, extra);
+ break;
+ case ICMPV6_ROUTERADV:
+ ipv6_routeradv_input(incoming, extra);
+ break;
+ case ICMPV6_NEIGHBORSOL:
+ ipv6_neighborsol_input(incoming, extra);
+ break;
+ case ICMPV6_NEIGHBORADV:
+ ipv6_neighboradv_input(incoming, extra);
+ break;
+ case ICMPV6_REDIRECT:
+ ipv6_redirect_input(incoming, extra);
+ break;
+ default:
+ /* Allow delivery to raw socket. */
+ break;
+ }
+ }
+ DPRINTF(IDL_EVENT, ("Delivering ICMPv6 to raw socket\n"));
+ DP(FINISHED, incoming->m_pkthdr.len, d);
+
+ ripv6_input(incoming, extra); /* Deliver to raw socket. */
+}
+
+
+/*
+ * The following functions attempt to address ICMP deficiencies in IPv6.
+ * Mostly these are part of a hack to keep the user-level program from
+ * computing checksums. :-P
+ */
+
+/*----------------------------------------------------------------------
+ * This function should never be called.
+ ----------------------------------------------------------------------*/
+
+int
+ipv6_icmp_output(m, so, dst)
+ struct mbuf *m;
+ struct socket *so;
+ struct in6_addr *dst;
+{
+ DPRINTF(IDL_ERROR,
+ ("ipv6_icmp_output() was called and shouldn't have been.\n"));
+
+ return ripv6_output(m,so,dst,NULL);
+}
+
+#if !__FreeBSD__
+/*----------------------------------------------------------------------
+ * Prepend IPv6 header, and compute IPv6 checksum for PRU_SEND, otherwise,
+ * redirect call to ripv6_usrreq().
+ ----------------------------------------------------------------------*/
+int
+#if __NetBSD__
+ipv6_icmp_usrreq(so, req, m, nam, control, p)
+#else /* __NetBSD__ */
+ipv6_icmp_usrreq(so, req, m, nam, control)
+#endif /* __NetBSD__ */
+ struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+#if __NetBSD__
+ struct proc *p;
+#endif /* __NetBSD__ */
+{
+ register struct inpcb *inp = sotoinpcb(so);
+
+ DPRINTF(IDL_EVENT,("Entering ipv6_icmp_usrreq(), req == %d\n",req));
+
+ /*
+ * If not sending, or sending with the header included (which IMHO means
+ * the user filled in the src & dest on his/her own), do normal raw
+ * IPv6 user request.
+ */
+
+ DPRINTF(IDL_EVENT,("Before check for ripv6_usrreq().\n"));
+ if (req != PRU_SEND || inp->inp_flags & INP_HDRINCL)
+#if __NetBSD__
+ return ripv6_usrreq(so,req,m,nam,control,p);
+#else /* __NetBSD__ */
+ return ripv6_usrreq(so,req,m,nam,control);
+#endif /* __NetBSD__ */
+
+ {
+ struct sockaddr *sa;
+
+ if (nam)
+ sa = mtod(nam, struct sockaddr *);
+ else
+ sa = NULL;
+
+#if __NetBSD__
+ return ipv6_icmp_send(so, req, m, sa, control, p);
+#else /* __NetBSD__ */
+ return ipv6_icmp_send(so, req, m, sa, control, NULL);
+#endif /* __NetBSD__ */
+ };
+}
+#endif /* !__FreeBSD__ */
+
+int ipv6_icmp_send(struct socket *so, int req, struct mbuf *m,
+ struct sockaddr *addr, struct mbuf *control,
+ struct proc *p)
+{
+ register struct inpcb *inp = sotoinpcb(so);
+ register struct ipv6 *ipv6;
+ register struct ipv6_icmp *icp;
+ struct in6_addr *dst;
+ int tflags = 0, len, rc;
+ struct in6_ifaddr *i6a;
+
+ /*
+ * redundant check, but necessary since we don't know if we are coming from
+ * icmp_usrreq or not.
+ */
+ if (inp->inp_flags & INP_HDRINCL)
+#if __NetBSD__ || __FreeBSD__
+ return ripv6_usrreq_send(so, req, m, addr, control, p);
+#else /* __NetBSD__ || __FreeBSD__ */
+ return ripv6_usrreq_send(so, req, m, addr, control);
+#endif /* __NetBSD__ || __FreeBSD__ */
+
+ if (in6_ifaddr == NULL)
+ {
+ m_freem(m);
+ return EADDRNOTAVAIL;
+ }
+ len = m->m_pkthdr.len;
+
+ /*
+ * If we get here, req == PRU_SEND and flags do not have INP_HDRINCL set.
+ * What that means in English is that a user process is sending an ICMPv6
+ * datagram without constructing an IPv6 header.
+ * We will construct an IPv6 header, fill it in completely, THEN COMPUTE
+ * THE ICMPv6 CHECKSUM and tell ipv6_output() that we are raw.
+ */
+
+ if (so->so_state & SS_ISCONNECTED)
+ {
+ if (addr)
+ {
+ m_freem(m);
+ return EISCONN;
+ }
+ dst = &(inp->inp_faddr6);
+ i6a = (struct in6_ifaddr *)inp->inp_route6.ro_rt->rt_ifa;
+ }
+ else /* Not connected */
+ {
+ if (addr == NULL)
+ {
+ m_freem(m);
+ return ENOTCONN;
+ }
+ DPRINTF(GROSSEVENT,("Sockaddr in nam is:\n"));
+ DDO(GROSSEVENT,dump_smart_sockaddr(addr));
+ dst = &(((struct sockaddr_in6 *) addr)->sin6_addr);
+ inp->inp_route6.ro_dst = *((struct sockaddr_in6 *)addr);
+ DPRINTF(EVENT,("In icmpv6_usrreq, Route is:\n"));
+ DDO(EVENT, dump_rtentry(inp->inp_route6.ro_rt));
+ if (so->so_options & SO_DONTROUTE)
+ {
+#define ifatoi6a(x) ((struct in6_ifaddr *)x)
+ if ((i6a =
+ ifatoi6a(ifa_ifwithdstaddr((struct sockaddr *)addr))) == 0
+ &&
+ (i6a =
+ ifatoi6a(ifa_ifwithnet((struct sockaddr *)addr))) == 0)
+ {
+ m_freem(m);
+ return ENETUNREACH;
+ }
+ inp->inp_route.ro_rt = NULL;
+ }
+ else
+ {
+ struct route *ro = &inp->inp_route;
+
+ /*
+ * If there is no route, consider sending out a neighbor advert
+ * across all the nodes. This is like the ipv6_onlink_query()
+ * call in ipv6_output.c.
+ */
+ if (ro->ro_rt == NULL)
+ rtalloc(ro);
+ if (ro->ro_rt == NULL)
+ {
+ /*
+ * No route of any kind, so spray neighbor solicits out all
+ * interfaces, unless it's a multicast address.
+ */
+ DPRINTF(IDL_FINISHED,("Icmpv6 spraying neigbor solicits.\n"));
+ if (IN6_IS_ADDR_MULTICAST(dst))
+ {
+ /*
+ * Select source address for multicast, for now
+ * return an error.
+ */
+ m_freem(m);
+ return ENETUNREACH;
+ }
+ ipv6_onlink_query((struct sockaddr_in6 *)&ro->ro_dst);
+ rtalloc(ro);
+ }
+ if (ro->ro_rt == NULL)
+ {
+ m_freem(m);
+ return ENETUNREACH;
+ }
+ DPRINTF(IDL_EVENT,("Route after ipv6_onlink_query:\n"));
+ DDO(IDL_EVENT,dump_route(ro));
+ DDO(IDL_EVENT,if (ro) dump_rtentry(ro->ro_rt));
+ if (ro->ro_rt->rt_ifa == NULL)
+ {
+ /*
+ * We have a route where we don't quite know which interface
+ * the neighbor belongs to yet. If I get here, I know that this
+ * route is not pre-allocated (such as done by in6_pcbconnect()),
+ * because those pre-allocators will do the same
+ * ipv6_onlink_query() and ipv6_verify_onlink() in advance.
+ *
+ * I can therefore free the route, and get it again.
+ */
+ int error;
+
+ DPRINTF(IDL_EVENT,("Okay...rt_ifa==NULL!\n"));
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = NULL;
+ switch (error = ipv6_verify_onlink((struct sockaddr_in6 *)&ro->ro_dst))
+ {
+ case 0:
+ break;
+ case -1:
+ error = ENETUNREACH;
+ default:
+ m_freem(m);
+ return error;
+ }
+ rtalloc((struct route *)ro);
+ if (ro->ro_rt == NULL || ro->ro_rt->rt_ifa == NULL)
+ panic("Oops3, I'm forgetting something after verify_onlink().");
+ DPRINTF(IDL_EVENT,("Route after ipv6_verify_onlink:\n"));
+ DDO(IDL_EVENT,dump_route(ro));
+ DDO(IDL_EVENT,if (ro) dump_rtentry(ro->ro_rt));
+ }
+
+ i6a = (struct in6_ifaddr *)ro->ro_rt->rt_ifa;
+ DPRINTF(IDL_EVENT,("Okay we got an interface for ipv6_icmp:\n"));
+ DDO(IDL_EVENT,dump_ifa((struct ifaddr *)i6a));
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(dst) && inp->inp_moptions6 &&
+ inp->inp_moptions6->i6mo_multicast_ifp &&
+ inp->inp_moptions6->i6mo_multicast_ifp != i6a->i6a_ifp)
+ {
+ struct ifaddr *ifa;
+
+#if __FreeBSD__
+ for (ifa = inp->inp_moptions6->i6mo_multicast_ifp->if_addrhead.tqh_first;
+ ifa != NULL; ifa = ifa->ifa_link.tqe_next)
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (ifa = inp->inp_moptions6->i6mo_multicast_ifp->if_addrlist.tqh_first;
+ ifa != NULL; ifa = ifa->ifa_list.tqe_next)
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (ifa = inp->inp_moptions6->i6mo_multicast_ifp->if_addrlist;
+ ifa != NULL; ifa = ifa->ifa_next)
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ if (ifa->ifa_addr->sa_family == AF_INET6)
+ {
+ i6a = (struct in6_ifaddr *)ifa;
+ break;
+ }
+ if (ifa == NULL)
+ {
+ DPRINTF(IDL_ERROR,("mcast inconsitency in icmp PRU_SEND.\n"));
+ }
+ }
+ }
+
+ /*
+ * If PCB has options hanging off of it, insert them here.
+ */
+
+ DPRINTF(GROSSEVENT,("ipv6_icmp_usrreq(): dst is "));
+ DDO(GROSSEVENT,dump_in6_addr(dst));
+
+ M_PREPEND(m,sizeof(struct ipv6),M_WAIT);
+ if (m == NULL)
+ panic("M_PREPEND died in ipv6_icmp_usrreq().\n");
+
+ DPRINTF(EVENT,("Before m_pullup() for %d bytes.\n",\
+ sizeof(struct ipv6) + ICMPV6_MINLEN));
+ if ((m = m_pullup(m,sizeof(struct ipv6) + ICMPV6_MINLEN)) == NULL)
+ {
+ DPRINTF(IDL_ERROR,("m_pullup in ipv6_icmp_usrreq() failed.\n"));
+ return ENOBUFS; /* Any better ideas? */
+ }
+
+ ipv6 = mtod(m,struct ipv6 *);
+ ipv6->ipv6_length = len;
+ ipv6->ipv6_nexthdr = IPPROTO_ICMPV6;
+ ipv6->ipv6_hoplimit = 255;
+ ipv6->ipv6_dst = *dst;
+ ipv6->ipv6_versfl = htonl(0x60000000); /* Plus flow label stuff. */
+ /*
+ * i6a pointer should be checked here.
+ */
+ ipv6->ipv6_src = i6a->i6a_addr.sin6_addr;
+
+ icp = (struct ipv6_icmp *)(m->m_data + sizeof(struct ipv6));
+ if (!(sotoinpcb(so)->inp_csumoffset))
+ sotoinpcb(so)->inp_csumoffset = 2;
+
+ DPRINTF(GROSSEVENT,("ipv6_icmp_usrreq(): Headers are\n"));
+ DDO(GROSSEVENT,dump_ipv6(ipv6));
+ DDO(GROSSEVENT,dump_ipv6_icmp(icp));
+
+ /*
+ * After this comment block you'd probably insert options,
+ * and adjust lengths accordingly.
+ */
+
+ /*
+ * Temporarily tweak INP_HDRINCL to fool ripv6_output(). I still don't
+ * know how a user who sets INP_HDRINCL for real will prepare ICMP packets.
+ * Also, set up data structures for callback routine in ipv6_output().
+ */
+
+ if (!(sotoinpcb(so)->inp_flags & INP_HDRINCL))
+ {
+ sotoinpcb(so)->inp_flags |= INP_HDRINCL;
+ tflags = 1;
+ }
+ rc = ripv6_output(m,so,dst,control);
+ if (!(so->so_state & SS_ISCONNECTED) && !(so->so_options & SO_DONTROUTE))
+ {
+ RTFREE(inp->inp_route.ro_rt);
+ inp->inp_route.ro_rt = NULL;
+ }
+ if (tflags)
+ sotoinpcb(so)->inp_flags &= ~INP_HDRINCL;
+
+ return rc;
+}
+
+#if __FreeBSD__
+#if __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ && !__FreeBSD__
+#define MAYBEINLINE __inline__
+#else /* __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ && !__FreeBSD__ */
+#define MAYBEINLINE
+#endif /* __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ && !__FreeBSD__ */
+
+#if 0
+MAYBEINLINE int ripv6_usrreq_attach(struct socket *, int , struct proc *);
+MAYBEINLINE int ripv6_usrreq_detach(struct socket *);
+MAYBEINLINE int ripv6_usrreq_abort(struct socket *);
+MAYBEINLINE int ripv6_usrreq_bind(struct socket *, struct sockaddr *, struct proc *);
+MAYBEINLINE int ripv6_usrreq_connect(struct socket *, struct sockaddr *, struct proc *);
+MAYBEINLINE int ripv6_usrreq_shutdown(struct socket *so);
+MAYBEINLINE int ripv6_usrreq_control(struct socket *, u_long, caddr_t,
+ struct ifnet *, struct proc *);
+MAYBEINLINE int ripv6_usrreq_sense(struct socket *, struct stat *);
+MAYBEINLINE int ripv6_usrreq_sockaddr(struct socket *, struct sockaddr **);
+MAYBEINLINE int ripv6_usrreq_peeraddr(struct socket *, struct sockaddr **);
+#endif /* 0 */
+
+struct pr_usrreqs ipv6_icmp_usrreqs = {
+ ripv6_usrreq_abort, pru_accept_notsupp, ripv6_usrreq_attach,
+ ripv6_usrreq_bind, ripv6_usrreq_connect, pru_connect2_notsupp,
+ ripv6_usrreq_control, ripv6_usrreq_detach, ripv6_usrreq_detach,
+ pru_listen_notsupp, ripv6_usrreq_peeraddr, pru_rcvd_notsupp,
+ pru_rcvoob_notsupp, ipv6_icmp_send, ripv6_usrreq_sense,
+ ripv6_usrreq_shutdown, ripv6_usrreq_sockaddr, sosend, soreceive, sopoll
+};
+#endif /* __FreeBSD__ */
+
+int *icmpv6_sysvars[] = ICMPV6CTL_VARS;
+
+#if __FreeBSD__
+SYSCTL_STRUCT(_net_inet_icmpv6, ICMPV6CTL_STATS, icmpv6stat, CTLFLAG_RD, &icmpv6stat, icmpv6stat, "");
+#else /* __FreeBSD__ */
+int
+ipv6_icmp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+{
+ if (name[0] >= ICMPV6CTL_MAXID)
+ return (EOPNOTSUPP);
+ switch (name[0]) {
+#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
+ case ICMPV6CTL_STATS:
+ return sysctl_rdtrunc(oldp, oldlenp, newp, &icmpv6stat, sizeof(icmpv6stat));
+ default:
+ return (sysctl_int_arr(icmpv6_sysvars, name, namelen, oldp, oldlenp, newp, newlen));
+#else /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+ default:
+ return EOPNOTSUPP;
+#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+ }
+};
+#endif /* __FreeBSD__ */
diff --git a/sys/netinet6/ipv6_icmp.h b/sys/netinet6/ipv6_icmp.h
new file mode 100644
index 00000000000..ed8ddd950f1
--- /dev/null
+++ b/sys/netinet6/ipv6_icmp.h
@@ -0,0 +1,257 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+#ifndef _NETINET6_IPV6_ICMP_H
+#define _NETINET6_IPV6_ICMP_H 1
+
+/*
+ * ICMPv6 header.
+ */
+
+struct ipv6_icmp
+{
+ uint8_t icmp_type;
+ uint8_t icmp_code;
+ uint16_t icmp_cksum;
+ union
+ {
+ uint32_t ih_reserved;
+ struct
+ {
+ uint16_t ihs_id;
+ uint16_t ihs_seq;
+ } ih_idseq;
+ struct
+ {
+ uint8_t ihr_hoplimit;
+ uint8_t ihr_bits;
+ uint16_t ihr_lifetime;
+ } ih_radv;
+ } icmp_hun;
+#define icmp_unused icmp_hun.ih_reserved
+#define icmp_nexthopmtu icmp_hun.ih_reserved
+#define icmp_paramptr icmp_hun.ih_reserved
+#define icmp_echoid icmp_hun.ih_idseq.ihs_id
+#define icmp_echoseq icmp_hun.ih_idseq.ihs_seq
+#define icmp_grpdelay icmp_hun.ih_idseq.ihs_id
+#define icmp_grpunused icmp_hun.ih_idseq.ihs_seq
+#define icmp_nadvbits icmp_hun.ih_reserved
+#define icmp_radvhop icmp_hun.ih_radv.ihr_hoplimit
+#define icmp_radvbits icmp_hun.ih_radv.ihr_bits
+#define icmp_radvlifetime icmp_hun.ih_radv.ihr_lifetime
+ union
+ {
+ struct
+ {
+ struct ipv6 ido_ipv6;
+ uint8_t ido_remaining[1];
+ } id_offending;
+ uint8_t id_data[1];
+ struct
+ {
+ struct in6_addr idn_addr;
+ uint8_t idn_ext[1];
+ } id_neighbor;
+ struct
+ {
+ struct in6_addr idr_addr1;
+ struct in6_addr idr_addr2;
+ uint8_t idr_ext[1];
+ } id_redirect;
+ struct
+ {
+ uint32_t ida_reachable;
+ uint32_t ida_retrans;
+ uint8_t ida_opt[1];
+ } id_radv;
+ } icmp_dun;
+#define icmp_offending icmp_dun.id_offending
+#define icmp_ipv6 icmp_dun.id_offending.ido_ipv6
+
+#define icmp_echodata icmp_dun.id_data
+
+#define icmp_grpaddr icmp_dun.id_neighbor.idn_addr
+
+#define icmp_radvreach icmp_dun.id_radv.ida_reachable
+#define icmp_radvretrans icmp_dun.id_radv.ida_retrans
+#define icmp_radvext icmp_dun.id_radv.ida_opt
+
+#define icmp_nsoltarg icmp_dun.id_neighbor.idn_addr
+#define icmp_nsolext icmp_dun.id_neighbor.idn_ext
+#define icmp_nadvaddr icmp_dun.id_neighbor.idn_addr
+#define icmp_nadvext icmp_dun.id_neighbor.idn_ext
+
+#define icmp_redirtarg icmp_dun.id_redirect.idr_addr1
+#define icmp_redirdest icmp_dun.id_redirect.idr_addr2
+#define icmp_redirext icmp_dun.id_redirect.idr_ext
+};
+
+/*
+ * ICMPv6 extension constants.
+ */
+
+#define EXT_SOURCELINK 1
+#define EXT_TARGETLINK 2
+#define EXT_PREFIX 3
+#define EXT_REDIR 4
+#define EXT_MTU 5
+
+/*
+ * Extension structures for IPv6 discovery messages.
+ */
+
+struct icmp_exthdr /* Generic extension */
+{
+ uint8_t ext_id;
+ uint8_t ext_length; /* Length is 8 * this field, 0 is invalid. */
+ uint8_t ext_data[6]; /* Padded to 8 bytes. */
+};
+
+struct ext_prefinfo /* Prefix information */
+{
+ uint8_t pre_extid;
+ uint8_t pre_length;
+
+ uint8_t pre_prefixsize;
+ uint8_t pre_bits;
+
+ uint32_t pre_valid;
+ uint32_t pre_preferred;
+ uint32_t pre_reserved;
+
+ struct in6_addr pre_prefix;
+};
+
+/*
+ * Values for pre_bits
+ */
+#define ICMPV6_PREFIX_ONLINK 0x80
+#define ICMPV6_PREFIX_AUTO 0x40
+
+struct ext_redir /* Redirected header */
+{
+ uint8_t rd_extid;
+ uint8_t rd_length;
+ uint8_t rd_reserved[6];
+ struct ipv6 rd_header;
+};
+
+struct ext_mtu /* Recommended link MTU. */
+{
+ uint8_t mtu_extid;
+ uint8_t mtu_length;
+ uint16_t mtu_reserved;
+ uint32_t mtu_mtu;
+};
+
+/*
+ * Constants
+ */
+
+/*
+ * Lower bounds on packet lengths for various types.
+ * For the error advice packets must first insure that the
+ * packet is large enought to contain the returned ip header.
+ * Only then can we do the check to see if enough bits of packet
+ * data have been returned, since we need to check the returned
+ * ipv6 header length.
+ */
+#define ICMPV6_MINLEN 8 /* abs minimum */
+#define ICMPV6_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */
+#define ICMPV6_NADVMINLEN 24 /* min neighbor advertisement */
+#define ICMPV6_NSOLMINLEN 24 /* min neighbor solicit */
+#define ICMPV6_RADVMINLEN 16 /* min router advertisement */
+#define ICMPV6_RSOLMINLEN 8 /* min router solicit */
+#define ICMPV6_REDIRMINLEN 40 /* min redirect */
+#define ICMPV6_HLPMINLEN (8 + sizeof(struct ipv6) + 8) /* HLP demux len. */
+#define ICMPV6_MAXLEN 576 /* This should be whatever IPV6_MINMTU
+ will be. I take this to be the WHOLE
+ packet, including IPv6 header, and any
+ IPv6 options before the ICMP message. */
+
+/*
+ * Definition of type and code field values.
+ * ICMPv6 fixes things so that info messages are >= 128.
+ */
+
+/* Error messages and codes. */
+
+#define ICMPV6_UNREACH 1 /* dest unreachable, codes: */
+#define ICMPV6_UNREACH_NOROUTE 0 /* No route to dest. */
+#define ICMPV6_UNREACH_ADMIN 1 /* Admin. prohibited */
+#define ICMPV6_UNREACH_NOTNEIGHBOR 2 /* For strict source
+ routing. */
+#define ICMPV6_UNREACH_ADDRESS 3 /* Address unreach. */
+#define ICMPV6_UNREACH_PORT 4 /* Port unreachable */
+#define ICMPV6_TOOBIG 2 /* Packet too big. */
+#define ICMPV6_TIMXCEED 3 /* time exceeded, code: */
+#define ICMPV6_TIMXCEED_INTRANS 0 /* ttl==0 in transit */
+#define ICMPV6_TIMXCEED_REASS 1 /* Reassembly t.o. */
+#define ICMPV6_PARAMPROB 4 /* ip header bad */
+#define ICMPV6_PARAMPROB_PROB 0 /* Actual incorrect
+ parameter. */
+#define ICMPV6_PARAMPROB_NEXTHDR 1 /* Bad next hdr. */
+#define ICMPV6_PARAMPROB_BADOPT 2 /* Unrec. option */
+
+/* Info messages. */
+
+#define ICMPV6_ECHO 128 /* echo service */
+#define ICMPV6_ECHOREPLY 129 /* echo reply */
+#define ICMPV6_GRPQUERY 130 /* Query group membership. */
+#define ICMPV6_GRPREPORT 131 /* Join mcast group. */
+#define ICMPV6_GRPTERM 132 /* Leave mcast group. */
+
+#define ICMPV6_ROUTERSOL 133 /* Router solicit. */
+#define ICMPV6_ROUTERADV 134 /* Router advertisement. */
+#define ICMPV6_NEIGHBORSOL 135 /* Neighbor solicit. */
+#define ICMPV6_NEIGHBORADV 136 /* Neighbor advertisement. */
+
+#define ICMPV6_REDIRECT 137 /* ICMPv6 redirect. */
+
+/* Defined this way to save some HTONL cycles on little-endian boxes. */
+#if BYTE_ORDER == BIG_ENDIAN
+#define ICMPV6_NEIGHBORADV_RTR 0x80000000 /* Router flag. */
+#define ICMPV6_NEIGHBORADV_SOL 0x40000000 /* Solicited flag. */
+#define ICMPV6_NEIGHBORADV_OVERRIDE 0x20000000 /* Override flag. */
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+#define ICMPV6_NEIGHBORADV_RTR 0x80 /* Router flag. */
+#define ICMPV6_NEIGHBORADV_SOL 0x40 /* Solicited flag. */
+#define ICMPV6_NEIGHBORADV_OVERRIDE 0x20 /* Override flag. */
+#endif
+
+#define ICMPV6_MAXTYPE 137
+
+#define ICMPV6_INFOTYPE(type) ((type) >= 128)
+
+#ifdef KERNEL
+#include <netinet6/ipv6_var.h>
+
+/* Function prototypes */
+void ipv6_icmp_error(struct mbuf *, int, int, uint32_t);
+void ipv6_icmp_input(struct mbuf *, int);
+void ipv6_gsolicit(struct ifnet *, struct mbuf *, struct rtentry *);
+void ipv6_rtrequest(int, struct rtentry *, struct sockaddr *);
+int ipv6_icmp_output(struct mbuf *,struct socket *, struct in6_addr *);
+int ipv6_icmp_sysctl(int *, u_int, void *, size_t *, void *, size_t);
+#if __NetBSD__ || __FreeBSD__
+int ipv6_icmp_usrreq(struct socket *,int, struct mbuf *,struct mbuf *, struct mbuf *, struct proc *);
+#else /* __NetBSD__ || __FreeBSD__ */
+int ipv6_icmp_usrreq(struct socket *,int, struct mbuf *,struct mbuf *, struct mbuf *);
+#endif /* __NetBSD__ || __FreeBSD__ */
+
+void ipv6_routersol_input(struct mbuf *, int);
+void ipv6_routeradv_input(struct mbuf *, int);
+void ipv6_neighborsol_input(struct mbuf *, int);
+void ipv6_neighboradv_input(struct mbuf *, int);
+void ipv6_redirect_input(struct mbuf *, int);
+#endif
+
+#endif /* _NETINET6_IPV6_ICMP_H */
diff --git a/sys/netinet6/ipv6_input.c b/sys/netinet6/ipv6_input.c
new file mode 100644
index 00000000000..c1fa3f20371
--- /dev/null
+++ b/sys/netinet6/ipv6_input.c
@@ -0,0 +1,1414 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+
+#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>
+#ifdef __FreeBSD__
+#include <net/netisr.h>
+#endif /* __FreeBSD__ */
+
+#include <netinet/in.h>
+
+#include <netinet6/in6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ipv6.h>
+#include <netinet6/ipv6_var.h>
+#include <netinet6/ipv6_icmp.h>
+#include <netinet6/ipv6_addrconf.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+
+#if __OpenBSD__ && defined(NRL_IPSEC)
+#define IPSEC 1
+#endif /* __OpenBSD__ && defined(NRL_IPSEC) */
+
+#ifdef IPSEC
+#include <sys/osdep.h>
+#include <net/netproc.h>
+#include <net/netproc_var.h>
+
+#include <netsec/ipsec.h>
+#endif /* IPSEC */
+
+#if __FreeBSD__
+#include <sys/sysctl.h>
+#endif /* __FreeBSD__ */
+
+#include <sys/debug.h>
+
+/*
+ * Globals
+ */
+
+u_char ipv6_protox[IPPROTO_MAX]; /* For easy demuxing to HLP's. */
+struct ipv6stat ipv6stat; /* Stats. */
+struct in6_ifaddr *in6_ifaddr = NULL; /* List of IPv6 addresses. */
+struct in6_ifnet *in6_ifnet = NULL; /* List of IPv6 interfaces. */
+struct ipv6_fragment *ipv6_fragmentq = NULL; /* Fragment reassembly queue */
+struct ifqueue ipv6intrq;
+struct route6 ipv6forward_rt;
+
+/*
+ * External globals
+ */
+
+extern int ipv6forwarding; /* See in6_proto.c */
+extern int ipv6rsolicit; /* See in6_proto.c */
+extern int ipv6_defhoplmt; /* See in6_proto.c */
+extern int ipv6qmaxlen; /* See in6_proto.c */
+extern struct protosw inet6sw[]; /* See in6_proto.c */
+extern struct domain inet6domain; /* See in6_proto.c */
+extern struct discq dqhead; /* See ipv6_discovery.c */
+
+/*
+ * Funct. prototypes.
+ */
+
+void ipv6_forward __P((struct mbuf *));
+void ipv6_discovery_init __P((void));
+extern int ipv6_clean_nexthop __P((struct radix_node *,void *));
+#if !__FreeBSD__
+int sysctl_int __P((void *, size_t *, void *, size_t, int *));
+/* For reasons somewhat unknown, <sys/mbuf.h> doesn't prototype this */
+struct mbuf *m_split __P((register struct mbuf *, int, int));
+#endif /* !__FreeBSD__ */
+
+void ipv6intr __P((void));
+int ipv6_enabled __P((struct ifnet *));
+
+static struct mbuf *ipv6_saveopt(caddr_t p, int size, int type, int level);
+#if 0
+static struct mbuf *ipv6_savebag(struct mbuf *m, int level);
+#endif /* 0 */
+struct mbuf *ipv6_headertocontrol(struct mbuf *m, int extra, int inp_flags);
+
+/*----------------------------------------------------------------------
+ * IPv6 initialization function.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_init()
+{
+ register struct protosw *pr;
+ register int i;
+
+ DPRINTF(GROSSEVENT,("IPv6 initializing..."));
+
+ bzero(&ipv6stat,sizeof(struct ipv6stat));
+
+ pr = pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW);
+ if (pr == 0)
+ panic("ipv6_init");
+
+ /*
+ * To call the right IPv6 next header function off next header type, have
+ * next header numbers index the protocol switch, like protocols in IP
+ * (default the switch for this). Otherwise, just switch off into normal
+ * (TCP,UDP) stuff.
+ *
+ * Initialize ipv6_protox[].
+ */
+
+ for (i = 0; i < IPPROTO_MAX; i++)
+ ipv6_protox[i] = pr - inet6sw;
+ for (pr = inet6domain.dom_protosw;
+ pr < inet6domain.dom_protoswNPROTOSW; pr++)
+ if (pr->pr_domain->dom_family == PF_INET6
+ && pr->pr_protocol != IPPROTO_RAW )
+ ipv6_protox[pr->pr_protocol] = pr - inet6sw;
+
+ /*
+ * Initialize discovery stuff.
+ */
+
+ ipv6_discovery_init();
+
+ /*
+ * Initialize addrconf stuff.
+ */
+
+ addrconf_init();
+
+ /*
+ * Initialize IPv6 i/f queue.
+ */
+
+ ipv6intrq.ifq_maxlen = ipv6qmaxlen;
+
+#ifdef IPSEC
+ /*
+ * Initialise IPsec
+ */
+ ipsec_init();
+#endif /* IPSEC */
+
+ DPRINTF(GROSSEVENT,("...done\n"));
+}
+
+/*----------------------------------------------------------------------
+ * IPv6 input queue interrupt handler.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6intr()
+{
+ struct mbuf *m;
+ int s;
+
+ while (1) /* Keep yanking off packets until I hit... */
+ {
+ s = splimp();
+ IF_DEQUEUE(&ipv6intrq, m);
+ splx(s);
+
+ if (m == NULL)
+ {
+ return; /* ...HERE. THIS is how I exit this endless loop. */
+ }
+
+#ifdef IPSEC
+ m->m_flags &= ~(M_AUTHENTIC | M_DECRYPTED);
+#endif /* IPSEC */
+
+ ipv6_input(m, 0);
+ }
+}
+
+#if __FreeBSD__
+NETISR_SET(NETISR_IPV6, ipv6intr);
+#endif /* __FreeBSD__ */
+
+/*----------------------------------------------------------------------
+ * Actual inbound (up the protocol graph from the device to the user)
+ * IPv6 processing.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_input(incoming, extra)
+ struct mbuf *incoming;
+ int extra;
+{
+ struct ipv6 *header;
+ struct in6_ifaddr *i6a = NULL;
+ struct in6_multi *in6m = NULL;
+ int jumbogram = 0;
+ uint8_t nexthdr;
+ int payload_len;
+
+ DPRINTF(GROSSEVENT,("ipv6_input(struct mbuf *incoming=%08lx, int extra=%x)\n", (unsigned long)incoming, extra));
+ DPRINTF(IDL_FINISHED,("incoming->m_data = %08lx, & 3 = %lx\n", (unsigned long)incoming->m_data, (unsigned long)incoming->m_data & 3));
+
+ /*
+ * Can't do anything until at least an interface is marked as
+ * ready for IPv6.
+ */
+
+ if (in6_ifaddr == NULL)
+ {
+ m_freem(incoming);
+ return;
+ }
+
+ ipv6stat.ips_total++;
+
+ /*
+ * IPv6 inside something else. Discard encapsulating header(s) and
+ * (maybe try to) make it look like virgin IPv6 packet.
+ *
+ * As with other stupid mbuf tricks, there probably is a better way.
+ */
+
+ if (extra) {
+ struct mbuf *newpacket;
+
+ DP(FINISHED, extra, d);
+
+#if IPSEC
+ /* Perform input-side policy check. Drop packet if policy says to drop
+ it.
+
+ Note that the gist of this check is that every decapsulation
+ requires a trip to input policy. For packets that end up locally,
+ this is probably bad. For packets that go off-host, this is probably
+ good.
+
+ Right now, we err on the side of security and always check. -cmetz*/
+ {
+ struct sockaddr_in6 srcsa, dstsa;
+
+ bzero(&srcsa, sizeof(struct sockaddr_in6));
+ srcsa.sin6_family = AF_INET6;
+ srcsa.sin6_len = sizeof(struct sockaddr_in6);
+ srcsa.sin6_addr = mtod(incoming, struct ipv6 *)->ipv6_src;
+
+ bzero(&dstsa, sizeof(struct sockaddr_in6));
+ dstsa.sin6_family = AF_INET6;
+ dstsa.sin6_len = sizeof(struct sockaddr_in6);
+ dstsa.sin6_addr = mtod(incoming, struct ipv6 *)->ipv6_dst;
+
+ /* XXX - state arg should NOT be NULL, it should be the netproc state
+ carried up the stack - cmetz */
+ if (netproc_inputpolicy(NULL, (struct sockaddr *)&srcsa,
+ (struct sockaddr *)&dstsa, IPPROTO_IPV6,
+ incoming, NULL, NULL)) {
+ m_freem(incoming);
+ return;
+ }
+ }
+#endif /* IPSEC */
+
+ /*
+ * Split packet into what I need, and what was encapsulating what I
+ * need. Discard the encapsulating portion.
+ */
+
+ if (!(newpacket = m_split(incoming, extra, M_DONTWAIT))) {
+ printf("ipv6_input() couldn't trim extra off.\n");
+ m_freem(incoming);
+ return;
+ }
+ newpacket->m_flags |= incoming->m_flags;
+ m_freem(incoming);
+ incoming = newpacket;
+ extra = 0;
+ }
+
+ /*
+ * Even before preparsing (see vitriolic comments later),
+ * I need to have the whole IPv6 header at my disposal.
+ */
+
+
+ if (incoming->m_len < sizeof (struct ipv6) &&
+ (incoming = m_pullup(incoming, sizeof(struct ipv6))) == NULL)
+ {
+ ipv6stat.ips_toosmall++;
+ return;
+ }
+
+ /*
+ * Check version bits immediately.
+ */
+
+ if ((incoming->m_data[0] >> 4) != IPV6VERSION)
+ {
+ ipv6stat.ips_badvers++;
+ m_freem(incoming);
+ return;
+ }
+
+ header = mtod(incoming, struct ipv6 *);
+
+ DDO(IDL_FINISHED,dump_ipv6(header));
+
+ /*
+ * Save off payload_len because of munging later...
+ */
+ payload_len = ntohs(header->ipv6_length);
+
+ /*
+ * 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 (incoming->m_pkthdr.len < payload_len + sizeof(struct ipv6))
+ {
+ ipv6stat.ips_tooshort++;
+ m_freem(incoming);
+ return;
+ }
+
+ if (incoming->m_pkthdr.len > payload_len + sizeof(struct ipv6))
+ if (!payload_len)
+ jumbogram = 1; /* We might have a jumbogram here! */
+ else if (incoming->m_len == incoming->m_pkthdr.len)
+ {
+ incoming->m_len = payload_len + sizeof(struct ipv6);
+ incoming->m_pkthdr.len = payload_len + sizeof(struct ipv6);
+ }
+ else m_adj(incoming,
+ (payload_len + sizeof(struct ipv6)) - incoming->m_pkthdr.len );
+
+ /*
+ * See if it's for me by checking list of i6a's. I may want to convert
+ * this into a routing lookup and see if rt->rt_ifp is loopback.
+ * (Might be quicker. :)
+ */
+
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ if (IN6_ARE_ADDR_EQUAL(&I6A_SIN(i6a)->sin6_addr, &header->ipv6_dst))
+ break;
+ /*
+ * Check for inbound multicast datagram is for us.
+ */
+
+ if (i6a == NULL && IN6_IS_ADDR_MULTICAST(&header->ipv6_dst))
+ {
+#ifdef MROUTING
+ /*
+ * Do IPv6 mcast routing stuff here. This means even if I'm in the
+ * m-cast group, I may have to forward the packet too. Hence, I put
+ * the m-cast routing stuff HERE.
+ */
+#endif
+ DPRINTF(IDL_EVENT,("In multicast case. Looking up..."));
+ IN6_LOOKUP_MULTI(&header->ipv6_dst,incoming->m_pkthdr.rcvif,in6m);
+ if (!in6m) {
+ DPRINTF(IDL_EVENT,("mcast lookup failed.\n"));
+ DDO(IDL_EVENT, dump_ipv6(header));
+ ipv6stat.ips_cantforward++;
+ m_freem(incoming);
+ return;
+ }
+ DPRINTF(IDL_EVENT,("succeeded.\n"));
+ }
+
+ /* If this datagram is for me, I'll either have one of my addresses in
+ i6a, or one of my multicast groups in in6m. From the code given
+ above, the condition (in6m && i6a) is always false. */
+
+ if (i6a || in6m)
+ {
+ if (i6a) {
+#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
+ /* Don't increment stats for encapsulated pkt */
+ if (!extra) {
+ i6a->i6a_ifa.ifa_ipackets++;
+ i6a->i6a_ifa.ifa_ibytes += incoming->m_pkthdr.len;
+ };
+#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+
+ if (i6a->i6a_addrflags & I6AF_NOTSURE) {
+ /*
+ * Don't want to unicast receive packets for me unless I know this
+ * is a verified unique address.
+ *
+ * Multicast packets will still come in and be handled.
+ */
+ m_freem(incoming);
+ return;
+ }
+ };
+
+ nexthdr = (mtod(incoming, struct ipv6 *))->ipv6_nexthdr;
+
+ DPRINTF(IDL_EVENT, ("nexthdr = %d.\n", nexthdr));
+
+ DPRINTF(IDL_EVENT, ("Calling protoswitch for %d\n", nexthdr));
+
+ ipv6stat.ips_delivered++;
+ (*inet6sw[ipv6_protox[nexthdr]].pr_input)
+ (incoming, extra + sizeof(struct ipv6));
+
+ DPRINTF(IDL_EVENT, ("Back from protoswitch for %d\n", nexthdr));
+ return;
+ }
+
+ DDO(IDL_EVENT,printf("Might forward for dest: ");\
+ dump_in6_addr(&header->ipv6_dst););
+
+ if (ipv6forwarding == 0)
+ {
+ DPRINTF(IDL_ERROR,("ipv6_input: Would call ipv6_forward if on.\n"));
+ ipv6stat.ips_cantforward++;
+ m_freem(incoming);
+ return;
+ }
+ else
+ {
+ /*
+ * Perform hop-by-hop options, if present. Dest opt. bags, and source
+ * routes SHOULD be handled by previous protosw call.
+ */
+ DPRINTF(IDL_FINISHED,("Calling ipv6_forward.\n"));
+ ipv6_forward(incoming);
+ }
+}
+
+/*----------------------------------------------------------------------
+ * IPv6 reassembly code. Returns an mbuf chain if the fragment completes
+ * a message, otherwise it returns NULL.
+ *
+ * Assumptions:
+ * * The IPv6 header is at the beginning of the incoming structure
+ * and has already been pulled up. I don't expect this to be
+ * a problem.
+ * * Fragments don't overlap (or, if they do, we can discard them).
+ * They can be duplicates (which we drop), but they cannot overlap.
+ * If we have IPv4->IPv6 header translation, this assumption could
+ * be incorrect because IPv4 has intermediate fragmentation (which
+ * you have to have in order for fragments to overlap). This is yet
+ * another reason why header translation is a Bad Thing.
+ * * The slowtimeo()-based routine that frees fragments will not
+ * get called during the middle of this routine. This assumption
+ * seems to be made in the IPv4 reassembly code. How this is so
+ * actually so I have yet to discover. Requiring splnet() or some
+ * sort of resource arbitration to handle this assumption being
+ * incorrect could be a major hassle.
+ *
+ * Other comments:
+ *
+ * * m_split(), a valuable call that gets no press, does much magic here.
+ * * Security against denial-of-service attacks on reassembly cannot be
+ * provided.
+ * * frag_id is more or less used as a 32-bit cookie. We don't need to
+ * htonl() it, because it's either equal or it's not, no matter what
+ * byte order you use.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_reasm(incoming, extra)
+ struct mbuf *incoming;
+ int extra;
+{
+ struct ipv6_fraghdr *fraghdr;
+ struct ipv6_fragment *fragment, **pfragment;
+
+ if (incoming->m_len < extra + sizeof(struct ipv6_fraghdr))
+ if (!(incoming = m_pullup(incoming, extra + sizeof(struct ipv6_fraghdr))))
+ return;
+
+ DP(FINISHED, OSDEP_PCAST(incoming), 08x);
+
+ fraghdr = (struct ipv6_fraghdr *)(mtod(incoming, caddr_t) + extra);
+ fraghdr->frag_bitsoffset = htons(fraghdr->frag_bitsoffset);
+
+ /*
+ * Locate reassembly queue for incoming fragment.
+ */
+ {
+ struct ipv6 *ipv6i, *ipv6f;
+
+ ipv6i = mtod(incoming, struct ipv6 *);
+
+ for (pfragment = &ipv6_fragmentq; *pfragment;
+ pfragment = &((*pfragment)->next)) {
+
+ ipv6f = mtod((*pfragment)->prefix, struct ipv6 *);
+
+ if (IN6_ARE_ADDR_EQUAL(&ipv6i->ipv6_src, &ipv6f->ipv6_src)) {
+ if (IN6_ARE_ADDR_EQUAL(&ipv6i->ipv6_dst, &ipv6f->ipv6_dst)) {
+ register struct ipv6_fraghdr *fhf = mtod((*pfragment)->data,
+ struct ipv6_fraghdr *);
+
+ if ((fraghdr->frag_id == fhf->frag_id) &&
+ (fraghdr->frag_nexthdr == fhf->frag_nexthdr)) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ fragment = *pfragment;
+
+ if (!fragment) {
+ /*
+ * Create a new fragment queue entry - this one looks new.
+ *
+ * Notice that the order of insertion is such that the newest queues
+ * are at the head of the one-way list. The entry aging code takes
+ * advantage of this.
+ */
+ MALLOC(fragment, struct ipv6_fragment *, sizeof(*fragment), M_FRAGQ,
+ M_NOWAIT);
+ if (!fragment) {
+ DPRINTF(IDL_ERROR, ("MALLOC fragment queue entry failed.\n"));
+ goto reasm_cleanup;
+ };
+ fragment->ttl = 60; /* 30 seconds */
+ if (!(fragment->data = m_split(incoming, extra, M_DONTWAIT))) {
+ free(fragment, M_FRAGQ);
+ goto reasm_cleanup;
+ }
+
+ fragment->prefix = incoming;
+
+ DP(FINISHED, OSDEP_PCAST(fragment->prefix), 08x);
+ DP(FINISHED, OSDEP_PCAST(fragment->data), 08x);
+
+ fragment->flags = (~fraghdr->frag_bitsoffset) & 1;
+
+ fragment->next = ipv6_fragmentq;
+ ipv6_fragmentq = fragment;
+ return;
+ }
+
+ /*
+ * If two packets have claimed to be the beginning or the end, we don't
+ * know which is right. The easiest solution is to drop this packet.
+ */
+
+ if (!(fraghdr->frag_bitsoffset & 1))
+ if (fragment->flags & 1) /* i.e. we already have the end... */
+ {
+ /*
+ * Duplicate (ending) fragment.
+ */
+ DPRINTF(IDL_FINISHED, ("Got a dupe/overlap fragment"));
+ goto reasm_cleanup;
+ }
+ else
+ fragment->flags |= 1;
+
+ if (!(fraghdr->frag_bitsoffset & 0xFFF8)) {
+ /*
+ * We want to end up with the part before the frag header for the packet
+ * at offset zero. (RFC1123?)
+ */
+ if (!(mtod(fragment->data, struct ipv6_fraghdr *)->frag_bitsoffset & 0xFFF8)) {
+ /*
+ * Duplicate (initial) packet.
+ */
+ DPRINTF(IDL_FINISHED, ("Got a dupe/overlap fragment"));
+ goto reasm_cleanup;
+ } else {
+ struct mbuf *mbuf;
+
+ DP(FINISHED, OSDEP_PCAST(fragment->prefix), 08x);
+ DP(FINISHED, OSDEP_PCAST(fragment->data), 08x);
+
+ m_freem(fragment->prefix);
+
+ /* Save everything before the frag header */
+ if (!(mbuf = m_split(incoming, extra, M_DONTWAIT))) {
+ /* should probably toss whole fragment queue */
+ m_freem(incoming);
+ return;
+ }
+
+ fragment->prefix = incoming;
+ incoming = mbuf;
+
+ DP(FINISHED, OSDEP_PCAST(fragment->prefix), 08x);
+ DP(FINISHED, OSDEP_PCAST(fragment->data), 08x);
+ }
+ } else
+ m_adj(incoming, extra); /* Discard everything before the frag header */
+
+ {
+ struct mbuf *hm[3];
+ int i;
+
+ {
+ struct mbuf *m, *pm;
+
+ /*
+ * Find where this fragment fits in.
+ */
+ for (pm = NULL, m = fragment->data; m; pm = m, m = m->m_nextpkt) {
+ if (((mtod(m, struct ipv6_fraghdr *))->frag_bitsoffset & 0xFFF8) >
+ (fraghdr->frag_bitsoffset & 0xFFF8)) {
+ break;
+ }
+
+ if ((mtod(m, struct ipv6_fraghdr *))->frag_bitsoffset ==
+ fraghdr->frag_bitsoffset) {
+ /*
+ * Duplicate fragment.
+ */
+ DPRINTF(IDL_FINISHED, ("Got a dupe/overlap fragment"));
+ goto reasm_cleanup;
+ }
+ }
+
+ /*
+ * Right here, pm will contain the preceeding fragment to the incoming
+ * one, and m will contain the succeeding fragment to the incoming one.
+ *
+ * This is somewhat non-obvious. hm[] is a vector of pointers to the
+ * mbufs containing ipv6_fraghdrs and dm[] is a vector of pointers to
+ * the mbufs at the head of each of their associated data lists. [0]
+ * is the mbuf in the main chain to the immediate left of where our
+ * new data should go, [1] is our new data, and [2] is the mbuf in
+ * the main chain to the immediate right of where our new data should
+ * go. One of [0] or [2] may be NULL.
+ *
+ * Each dm[n]->m_nextpkt will have that fragment's length stored.
+ *
+ * The reason why we do this is so we can bubble together the [0] and
+ * [1] elements if possible, make the [1] = [0] if we do, then we can
+ * bubble the [1] and the [2] together and it'll do the right thing.
+ * We could theoretically do this for the rest of the list except that
+ * it is made deliberately unecessary (we bubble on insertion until we
+ * have a known-done big bubble so we don't have to do a O(N/2)
+ * rescan of the list every time just to figure out whether or not
+ * we're done.
+ *
+ * This seems really ugly, but it does the job and it may even be
+ * somewhat efficient.
+ */
+
+ hm[0] = pm;
+ hm[1] = incoming;
+ hm[2] = m;
+
+ if (!pm) {
+ incoming->m_nextpkt = fragment->data;
+ fragment->data = incoming;
+ } else {
+ pm->m_nextpkt = incoming;
+ incoming->m_nextpkt = m;
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (!hm[i] || !hm[i+1]) {
+ DP(FINISHED, i, d);
+ continue;
+ }
+
+ if ((((mtod(hm[i], struct ipv6_fraghdr *))->frag_bitsoffset
+ & 0xFFF8) + hm[i]->m_pkthdr.len - sizeof(struct ipv6_fraghdr)) >
+ ((mtod(hm[i+1], struct ipv6_fraghdr *))->frag_bitsoffset
+ & 0xFFF8)) {
+ /*
+ * Overlapping fragment.
+ */
+ DPRINTF(IDL_FINISHED, ("Got a dupe/overlap fragment"));
+ goto reasm_cleanup;
+ }
+
+ if ((((mtod(hm[i], struct ipv6_fraghdr *))->frag_bitsoffset
+ & 0xFFF8) + hm[i]->m_pkthdr.len - sizeof(struct ipv6_fraghdr)) ==
+ ((mtod(hm[i+1], struct ipv6_fraghdr *))->frag_bitsoffset
+ & 0xFFF8)) {
+ /*
+ * If the fragments are contiguous, bubble them together:
+ * Combine the data chains and increase the appropriate
+ * chain data length. The second fragment header is now
+ * unnecessary.
+ */
+ if (!((mtod(hm[i+1], struct ipv6_fraghdr *))->frag_bitsoffset & 1)){
+ (mtod(hm[i], struct ipv6_fraghdr *))->frag_bitsoffset &= (~1);
+ }
+
+ DDO(FINISHED, dump_mchain(hm[i]));
+ DDO(FINISHED, dump_mchain(hm[i+1]));
+
+ /* Trim the second fragment header */
+ m_adj(hm[i+1], sizeof(struct ipv6_fraghdr));
+ /* Append the second fragment's data */
+ m_cat(hm[i], hm[i+1]);
+ /* And update the first fragment's length */
+ hm[i]->m_pkthdr.len += hm[i+1]->m_pkthdr.len;
+
+ hm[i]->m_nextpkt = hm[i+1]->m_nextpkt;
+
+ DDO(FINISHED, dump_mchain(hm[i]));
+
+ /* Hack to make the bubble happen with the m found above if we
+ just bubbled with the pm found above */
+ if (!i)
+ hm[i+1] = hm[i];
+ }
+ }
+ }
+
+ /*
+ * Now, the moment of truth. Do we have a complete packet?
+ * To be done, we have to have only one packet left in the queue now that
+ * we've bubbled together (i.e., one complete packet), have it at offset
+ * zero (i.e., there's nothing before it), and not have its more bit set
+ * (i.e., there's nothing after it). If we meet these conditions, we are
+ * DONE!
+ *
+ * Remember when we htons()ed frag_bitsoffset? If we're done, it contains
+ * a zero. I don't know of any architecture in which a zero in network
+ * byte order isn't a zero in host byte order, do you?
+ */
+
+ if (!fragment->data->m_nextpkt &&
+ !(mtod(fragment->data, struct ipv6_fraghdr *))->frag_bitsoffset) {
+ uint8_t nexthdr = mtod(fragment->data, struct ipv6_fraghdr *)->frag_nexthdr;
+
+ if (*pfragment)
+ *pfragment = fragment->next;
+
+ /*
+ * Pain time.
+ *
+ * The fragmentation header must be removed. This requires me to rescan
+ * prefix, going through each header until I figure out where the last
+ * header before the fragmentation header is. Then I set that header's
+ * next header to the fragmentation header's next header.
+ *
+ * N.B. that this means people adding new random header processing code
+ * to this IPv6 implementation need to make the appropriate mods below.
+ * Failure to do so will really hose you if your header appears before
+ * a fragment header. It is a Good Thing to mod the code below even if
+ * you don't *think* it will ever appear before a fragment header, just
+ * because it *could*.
+ *
+ * This is really annoying and somewhat expensive. On the other hand,
+ * it might prove itself to be yet another reason for higher level
+ * protocols to work at avoiding fragmentation where possible.
+ */
+
+ /*
+ Since we start out by doing a m_pullup() of extra + sizeof(struct
+ ipv6_fraghdr, we can treat this as a straight linear buffer.
+
+ This could easily be implemented as a fast and slow path, but
+ reassembly is an inherently slow path anyway. -cmetz
+ */
+
+ DDO(FINISHED, dump_mchain(fragment->prefix));
+ DDO(FINISHED, dump_mchain(fragment->data));
+
+ {
+ caddr_t data;
+ uint8_t *type;
+
+ DDO(FINISHED, dump_ipv6(mtod(fragment->prefix, struct ipv6 *)));
+
+ data = mtod(fragment->prefix, caddr_t) + sizeof(struct ipv6);
+ type = &(mtod(fragment->prefix, struct ipv6 *)->ipv6_nexthdr);
+
+ while(*type != IPPROTO_FRAGMENT) {
+ switch(*type) {
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ {
+ struct ipv6_opthdr *ipv6_opthdr = (struct ipv6_opthdr *)data;
+
+ type = &(ipv6_opthdr->oh_nexthdr);
+ data += sizeof(struct ipv6_opthdr) +
+ ipv6_opthdr->oh_extlen * sizeof(uint64_t);
+ };
+ break;
+ case IPPROTO_ROUTING:
+ {
+ struct ipv6_srcroute0 *ipv6_srcroute0 =
+ (struct ipv6_srcroute0 *)data;
+
+ type = &(ipv6_srcroute0->i6sr_nexthdr);
+ data += sizeof(struct ipv6_srcroute0) +
+ ipv6_srcroute0->i6sr_len * sizeof(uint64_t);
+ };
+ break;
+ default:
+ DPRINTF(ERROR, ("ipv6_reasm: Received a header (%d) that isn't allowed before a fragment header", *type));
+
+ DP(FINISHED, OSDEP_PCAST(fragment->data), 08x);
+ DP(FINISHED, OSDEP_PCAST(fragment->prefix), 08x);
+
+ m_freem(fragment->data);
+ m_freem(fragment->prefix);
+ FREE(fragment, M_FRAGQ);
+
+ return;
+ }
+ }
+ *type = nexthdr;
+ }
+
+ extra = fragment->prefix->m_pkthdr.len;
+
+ incoming = fragment->prefix;
+ m_adj(fragment->data, sizeof(struct ipv6_fraghdr));
+ m_cat(incoming, fragment->data);
+ incoming->m_pkthdr.len += fragment->data->m_pkthdr.len;
+
+ FREE(fragment, M_FRAGQ);
+
+ /* Can't reassemble into a jumbogram */
+ if (incoming->m_pkthdr.len > 0xffff) {
+ m_freem(incoming);
+ return; /* no other cleanup needed */
+ }
+
+ /* Dummy up length */
+ (mtod(incoming, struct ipv6 *))->ipv6_length =
+ htons(incoming->m_pkthdr.len - sizeof(struct ipv6));
+
+ DDO(FINISHED, dump_mchain(incoming));
+
+ (*inet6sw[ipv6_protox[nexthdr]].pr_input)(incoming, extra);
+ }
+ return;
+
+reasm_cleanup:
+ if (incoming)
+ m_freem(incoming);
+
+ return;
+};
+
+/*----------------------------------------------------------------------
+ * IPv6 forwarding engine.
+ *
+ * Assumes IPv6 header is already pulled up and ready-to-read.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_forward(outbound)
+ struct mbuf *outbound;
+{
+ struct ipv6 *ipv6 = mtod(outbound, struct ipv6 *);
+ struct sockaddr_in6 *sin6;
+ struct rtentry *rt;
+ struct mbuf *ocopy;
+ int type = 0, code = 0, pptr = 0, error;
+
+ /*
+ * Check hop limit.
+ */
+
+ if (ipv6->ipv6_hoplimit <= 1)
+ {
+ ipv6_icmp_error(outbound,ICMPV6_TIMXCEED,ICMPV6_TIMXCEED_INTRANS,0);
+ return;
+ }
+
+ /*
+ * Check link-local nature of source and dest. (Thanks to rja!)
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&ipv6->ipv6_src) || IN6_IS_ADDR_LINKLOCAL(&ipv6->ipv6_dst))
+ {
+ printf("Can't forward packet with link-locals in it!\n");
+ m_freem(outbound);
+ return;
+ }
+ ipv6->ipv6_hoplimit--;
+ sin6 = &ipv6forward_rt.ro_dst;
+ if ((rt = ipv6forward_rt.ro_rt) == NULL ||
+ !IN6_ARE_ADDR_EQUAL(&ipv6->ipv6_dst, &sin6->sin6_addr))
+ {
+ if (ipv6forward_rt.ro_rt != NULL)
+ {
+ RTFREE(ipv6forward_rt.ro_rt);
+ ipv6forward_rt.ro_rt = NULL;
+ }
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_addr = ipv6->ipv6_dst;
+
+ rtalloc_noclone((struct route *)&ipv6forward_rt,ONNET_CLONING);
+ if (ipv6forward_rt.ro_rt == NULL)
+ {
+ ipv6_icmp_error(outbound,ICMPV6_UNREACH, ICMPV6_UNREACH_NOROUTE, 0);
+ return;
+ }
+ rt = ipv6forward_rt.ro_rt;
+ }
+
+ /*
+ * Save 576 bytes of the packet in case we need to generate and ICMPv6
+ * message to the sender.
+ */
+
+ ocopy = m_copy(outbound,0, min(ntohs(ipv6->ipv6_length),IPV6_MINMTU));
+
+ /*
+ * ip_forward() keeps some statistics here if GATEWAY is defined. We
+ * skip that for now.
+ */
+
+ if (rt->rt_ifp == outbound->m_pkthdr.rcvif && rt_mask(rt)
+ && rt_mask(rt)->sa_len
+ && (rt->rt_flags & (RTF_TUNNEL|RTF_DYNAMIC|RTF_MODIFIED)) == 0 )
+ {
+ DPRINTF(IDL_EVENT, ("WARNING: May be cause for a redirect in ipv6_forward().\n"));
+ }
+
+ /*
+ * Perform hop-by-hop options since we're ready to go!
+ */
+
+ if (!ipv6->ipv6_nexthdr)
+ {
+ /*
+ * If bad hop-by-hop, return.
+ */
+ printf("Hop-by-hop options present in packet to be forwarded!\n");
+ }
+
+ error = ipv6_output(outbound, &ipv6forward_rt, IPV6_FORWARDING, NULL, NULL, NULL);
+
+ if (error)
+ ipv6stat.ips_cantforward++;
+ else
+ {
+ /*
+ * Check for redirect flags that should've been set in the redirect
+ * code, otherwise...
+ */
+ m_freem(ocopy);
+ return;
+ }
+
+ switch (error)
+ {
+ case 0: /* type and code should've been set by redirect code. */
+ break;
+
+ case ENETUNREACH: /* These two should've been checked above. */
+ case EHOSTUNREACH:
+ type = ICMPV6_UNREACH;
+ code = ICMPV6_UNREACH_NOROUTE;
+ break;
+
+ case EHOSTDOWN:
+ type = ICMPV6_UNREACH;
+ code = ICMPV6_UNREACH_ADDRESS;
+ break;
+
+ case EMSGSIZE:
+ type = ICMPV6_TOOBIG;
+ code = 0;
+ ipv6stat.ips_cantfrag++;
+ pptr = rt->rt_rmx.rmx_mtu;
+ break;
+ }
+
+ ipv6_icmp_error(ocopy, type, code, pptr);
+}
+
+/*----------------------------------------------------------------------
+ * IPv6 hop-by-hop option handler.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_hop(incoming, extra)
+ struct mbuf *incoming;
+ int extra;
+{
+ struct ipv6 *header;
+ struct ipv6_opthdr *hopopt;
+ struct ipv6_option *option;
+ uint8_t *tmp;
+
+ if (incoming->m_len < extra + sizeof(struct ipv6_opthdr))
+ if (!(incoming = m_pullup2(incoming, extra + sizeof(struct ipv6_opthdr))))
+ return;
+
+ hopopt = (struct ipv6_opthdr *)(mtod(incoming, caddr_t) + extra);
+
+ if (incoming->m_len < extra + sizeof(struct ipv6_opthdr) +
+ (hopopt->oh_extlen * sizeof(uint64_t))) {
+ if (!(incoming = m_pullup2(incoming, extra + sizeof(struct ipv6_opthdr) +
+ (hopopt->oh_extlen * sizeof(uint64_t)))))
+ return;
+
+ hopopt = (struct ipv6_opthdr *)(mtod(incoming, caddr_t) + extra);
+ }
+
+ header = mtod(incoming, struct ipv6 *);
+
+ tmp = hopopt->oh_data;
+ /*
+ * Slide the char pointer tmp along, parsing each option in the "bag" of
+ * hop-by-hop options.
+ */
+ while (tmp < hopopt->oh_data + (hopopt->oh_extlen << 2))
+ {
+ option = (struct ipv6_option *)tmp;
+ switch (option->opt_type)
+ {
+ case OPT_PAD1:
+ tmp++;
+ break;
+ case OPT_PADN:
+ tmp += option->opt_datalen + 2;
+ break;
+ case OPT_JUMBO:
+ tmp += 2;
+ /*
+ * Confirm that the packet header field matches the jumbogram size.
+ */
+ if (incoming->m_pkthdr.len != ntohl(*(uint32_t *)tmp) + sizeof(*header))
+ {
+ /*
+ * For now, bail. Add instrumenting code here, too.
+ */
+ m_freem(incoming);
+ return;
+ }
+ break;
+ default:
+ /*
+ * Handle unknown option by taking appropriate action based on
+ * high bit values. With this code, this first non-skipping
+ * unknown option will cause the packet to be dropped.
+ */
+ switch (option->opt_type & 0xC0)
+ {
+ case 0: /* Skip over */
+ tmp += option->opt_datalen + 2;
+ break;
+ case 0xC0: /* Only if not multicast... */
+ if (IN6_IS_ADDR_MULTICAST(&header->ipv6_dst))
+ /* FALLTHROUGH */
+ case 0x80: /* Send ICMP Unrecognized type. */
+ {
+ /* Issue ICMP parameter problem. */
+ ipv6_icmp_error(incoming,ICMPV6_PARAMPROB,
+ ICMPV6_PARAMPROB_BADOPT,
+#ifdef __alpha__
+ (u_long)option - (u_long)hopopt->oh_data +
+#else
+ (uint32_t)option - (uint32_t)hopopt->oh_data +
+#endif
+ sizeof(struct ipv6));
+ }
+ return; /* incoming has already been freed. */
+
+ case 0x40: /* Discard packet */
+ m_freem(incoming);
+ return;
+ }
+ }
+ }
+
+ DPRINTF(GROSSEVENT, ("ipv6_hop calling protoswitch for %d\n", \
+ hopopt->oh_nexthdr));
+
+ (*inet6sw[ipv6_protox[hopopt->oh_nexthdr]].pr_input)
+ (incoming, extra + sizeof(struct ipv6_opthdr) +
+ (hopopt->oh_extlen * sizeof(uint64_t)));
+
+ DPRINTF(GROSSEVENT, ("Leaving ipv6_hop\n"));
+}
+
+
+/*----------------------------------------------------------------------
+ * If timers expires on reassembly queues, discard them.
+ * Also update any discovery queues.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_slowtimo()
+{
+ struct ipv6_fragment *fragment, *fragmentprev;
+ int s = splnet();
+
+ /*
+ * Age reasssembly fragments.
+ *
+ * (Since fragments are inserted at the beginning of the queue, once we've
+ * found the first timed-out fragment, we know that everything beyond is
+ * also timed-out since it must be older.)
+ *
+ */
+ for (fragmentprev = NULL, fragment = ipv6_fragmentq; fragment;
+ fragmentprev = fragment, fragment = fragment->next)
+ if (fragment->ttl <= 1)
+ break;
+ else
+ fragment->ttl--;
+
+ if (fragment) {
+ struct mbuf *m, *m2;
+
+ if (fragmentprev)
+ fragmentprev->next = NULL;
+ else
+ ipv6_fragmentq = NULL;
+
+ /*
+ * This loop does most of the work and doesn't require splnet()...?
+ */
+ splx(s);
+
+ while(fragment) {
+ /*
+ * We "should" (says the spec) send an ICMP time exceeded here.
+ * However, among other headaches, we may not actually have a copy
+ * of the real packet sent to us (if we bubbled, we now have a frag
+ * header that never really came from the sender). The solution taken
+ * for now is to continue the BSD tradition of not bothering to send
+ * these messages.
+ */
+ m = fragment->data;
+ ipv6stat.ips_fragtimeout++;
+ while(m) {
+ m2 = m;
+ m = m->m_nextpkt;
+ m_freem(m2);
+ }
+ m_freem(fragment->prefix);
+ fragmentprev = fragment;
+ fragment = fragment->next;
+ FREE(fragmentprev, M_FRAGQ);
+ }
+ } else
+ splx(s);
+}
+
+/*----------------------------------------------------------------------
+ * Drain all fragments & possibly discovery structures.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_drain()
+{
+ struct ipv6_fragment *totrash;
+ struct mbuf *m,*m2;
+ struct radix_node_head *rnh = rt_tables[AF_INET6];
+
+ while (ipv6_fragmentq != NULL)
+ {
+ ipv6stat.ips_fragdropped++;
+ totrash = ipv6_fragmentq;
+ ipv6_fragmentq = totrash->next;
+ m = totrash->data;
+ while (m)
+ {
+ m2 = m;
+ m = m->m_nextpkt;
+ m_freem(m2);
+ }
+ if (totrash->prefix)
+ m_freem(totrash->prefix);
+ FREE(totrash, M_FRAGQ);
+ }
+
+ /*
+ * Might want to delete all off-net host routes,
+ * and maybe even on-net host routes.
+ *
+ * For now, do only the off-net host routes.
+ */
+ (void) rnh->rnh_walktree(rnh, ipv6_clean_nexthop, (void *)1);
+}
+
+/*----------------------------------------------------------------------
+ * sysctl(2) handler for IPv6. Not yet implemented.
+ ----------------------------------------------------------------------*/
+int *ipv6_sysvars[] = IPV6CTL_VARS;
+
+#if __FreeBSD__
+SYSCTL_STRUCT(_net_inet_ipv6, IPV6CTL_STATS, ipv6stats, CTLFLAG_RD, &ipv6stat, ipv6stat, "");
+#else /* __FreeBSD__ */
+int
+ipv6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+ int *name;
+ uint namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+{
+ if (namelen != 1)
+ return ENOTDIR;
+
+ switch (name[0])
+ {
+#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
+ case IPV6CTL_STATS:
+ return sysctl_rdtrunc(oldp, oldlenp, newp, &ipv6stat, sizeof(ipv6stat));
+ default:
+ return (sysctl_int_arr(ipv6_sysvars, name, namelen, oldp, oldlenp, newp, newlen));
+#else /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+ default:
+ return EOPNOTSUPP;
+#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+ }
+}
+#endif __FreeBSD__
+
+/*----------------------------------------------------------------------
+ * Should be a macro, this function determines if IPv6 is running on a
+ * given interface.
+ ----------------------------------------------------------------------*/
+
+int
+ipv6_enabled(ifp)
+ struct ifnet *ifp;
+{
+ struct in6_ifaddr *i6a;
+
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ if (i6a->i6a_ifp == ifp)
+ return 1;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ * Strips IPv6 options for TCP or UDP.
+ *
+ * This function assumes that the input chain (incoming) has been
+ * munged by ipv6_preparse() and starts with an IPv6 header.
+ * The header index is invalid after this call.
+ * The IPv6 header is not updated EXCEPT for the length, which must be
+ * in HOST order.
+ * Note that the API for this function is NOT THE SAME as its IPv4
+ * counterpart.
+ *
+ * Often called like:
+ *
+ * ipv6_stripoptions(incoming, extra, nexthdr);
+ *
+ * Where ihi and ihioff are the header index arrays, passed up after
+ * ipv6_preparse(). If preparse is eliminated or altered, this code will
+ * be too.
+ ----------------------------------------------------------------------*/
+
+void ipv6_stripoptions(incoming, extra)
+register struct mbuf *incoming;
+int extra;
+{
+ struct mbuf *optm, *m;
+ int optlen;
+
+ if (extra == sizeof(struct ipv6)) {
+ /* i.e. If there are no options... */
+ optm = NULL;
+ optlen = 0;
+ return;
+ }
+
+ if (!(optm = m_split(incoming, sizeof(struct ipv6), M_DONTWAIT)))
+ return;
+
+ if (!(m = m_split(optm, extra - sizeof(struct ipv6), M_DONTWAIT))) {
+ m_cat(incoming, optm);
+ return;
+ }
+
+ m_cat(incoming, m);
+ incoming->m_pkthdr.len -= (extra - sizeof(struct ipv6));
+
+ (mtod(incoming, struct ipv6 *))->ipv6_length =
+ (incoming->m_pkthdr.len - sizeof(struct ipv6) > 0xffff) ? 0 :
+ htons(incoming->m_pkthdr.len - sizeof(struct ipv6));
+
+ /*
+ * XXX - We should be saving these stripped options somewhere...
+ */
+ m_freem(optm);
+}
+
+static struct mbuf *ipv6_saveopt(caddr_t p, int size, int type, int level)
+{
+ register struct cmsghdr *cp;
+ struct mbuf *m;
+
+ if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL)
+ return ((struct mbuf *) NULL);
+ cp = (struct cmsghdr *) mtod(m, struct cmsghdr *);
+ bcopy(p, CMSG_DATA(cp), size);
+ size += sizeof(*cp);
+ m->m_len = size;
+ cp->cmsg_len = size;
+ cp->cmsg_level = level;
+ cp->cmsg_type = type;
+ return m;
+}
+
+#if 0
+static struct mbuf *ipv6_savebag(struct mbuf *m, int level)
+{
+ uint8_t *p = mtod(m, uint8_t *) + 1;
+ int len = (*(p++) << 3) + 6;
+ struct mbuf *opts = NULL, **mp = &opts;
+
+ while(len > 0) {
+ if (!*p) {
+ p++; len--;
+ continue;
+ }
+
+ if (len <= 1)
+ return opts;
+
+ if (*p != 1)
+ if ((*mp = ipv6_saveopt((caddr_t)(p + 2), *(p + 1), *p, level)))
+ mp = &(*mp)->m_next;
+
+ len -= *(p + 1) + 2;
+ p += *(p + 1) + 2;
+ };
+
+ return opts;
+};
+#endif /* 0 */
+
+struct mbuf *ipv6_headertocontrol(struct mbuf *m, int extra, int inp_flags)
+{
+ struct mbuf *opts = NULL, **mp = &opts;
+
+ if (inp_flags & INP_RXINFO) {
+ struct in6_pktinfo pktinfo;
+
+ if (m->m_pkthdr.rcvif) {
+ pktinfo.ipi6_ifindex = m->m_pkthdr.rcvif->if_index;
+ } else {
+ DPRINTF(IDL_ERROR, ("ipv6_controldata: m->m_pkthdr.rcvif = NULL!\n"));
+ pktinfo.ipi6_ifindex = 0;
+ }
+
+ bcopy(&(mtod(m, struct ipv6 *)->ipv6_dst), &pktinfo.ipi6_addr, sizeof(struct in6_addr));
+ if ((*mp = ipv6_saveopt((caddr_t)&pktinfo, sizeof(struct in6_pktinfo), IPV6_PKTINFO, IPPROTO_IPV6)))
+ mp = &(*mp)->m_next;
+ };
+
+ if (inp_flags & INP_HOPLIMIT) {
+ int hoplimit = mtod(m, struct ipv6 *)->ipv6_hoplimit;
+ if ((*mp = ipv6_saveopt((caddr_t)&hoplimit, sizeof(int), IPV6_HOPLIMIT, IPPROTO_IPV6)))
+ mp = &(*mp)->m_next;
+ };
+
+#if 0
+ /* Since there's not any immediate need for these options anyway, we'll
+ worry about reimplementing them later. - cmetz */
+
+ if (inp_flags & INP_RXHOPOPTS) {
+ int i;
+ for (i = 1; i < ihioff; i++)
+ if (ihi[i].ihi_nexthdr == IPPROTO_HOPOPTS) {
+ if ((*mp = ipv6_savebag(ihi[i].ihi_mbuf, IPPROTO_HOPOPTS)))
+ mp = &(*mp)->m_next;
+ break;
+ };
+ };
+
+ if (inp_flags & INP_RXDSTOPTS) {
+ int i;
+ for (i = 1; i < ihioff; i++)
+ if (ihi[i].ihi_nexthdr == IPPROTO_DSTOPTS) {
+ if ((*mp = ipv6_savebag(ihi[i].ihi_mbuf, IPPROTO_DSTOPTS)))
+ mp = &(*mp)->m_next;
+ break;
+ };
+ };
+
+ if (inp_flags & INP_RXSRCRT) {
+ int i;
+ for (i = 1; i < ihioff; i++)
+ if (ihi[i].ihi_nexthdr == IPPROTO_ROUTING) {
+ if ((*mp = ipv6_saveopt((caddr_t)(mtod(ihi[i].ihi_mbuf, uint8_t *) + 3), (*mtod(ihi[i].ihi_mbuf, uint8_t *) << 3) + 5, *(mtod(ihi[i].ihi_mbuf, uint8_t *) + 2), IPPROTO_ROUTING)))
+ mp = &(*mp)->m_next;
+ break;
+ };
+ };
+#endif /* 0 */
+
+ return opts;
+};
diff --git a/sys/netinet6/ipv6_output.c b/sys/netinet6/ipv6_output.c
new file mode 100644
index 00000000000..cd845d99324
--- /dev/null
+++ b/sys/netinet6/ipv6_output.c
@@ -0,0 +1,2078 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+
+#include <sys/param.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ipv6.h>
+#include <netinet6/ipv6_var.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_var.h>
+
+#if __OpenBSD__ && NRL_IPSEC
+#define IPSEC 1
+#endif /* __OpenBSD__ && NRL_IPSEC */
+
+#if IPSEC
+#include <sys/osdep.h>
+#include <net/netproc.h>
+#include <net/netproc_var.h>
+#include <sys/nbuf.h>
+#endif /* IPSEC */
+
+#include <sys/debug.h>
+
+/*
+ * Globals and function definitions.
+ */
+
+uint32_t outfragid = 0; /* Outbound fragment groups have unique id's. */
+struct mbuf *ipv6_fragment __P((struct mbuf *,int));
+
+/*
+ * External globals.
+ */
+
+extern struct ipv6stat ipv6stat;
+extern struct in6_ifaddr *in6_ifaddr;
+extern struct in6_ifnet *in6_ifnet;
+extern struct ifnet *mcastdefault;
+extern int ipv6_defhoplmt;
+
+#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
+extern struct ifnet *loifp;
+#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+
+void ipv6_nsolicit __P((struct ifnet *, struct mbuf *, struct rtentry *));
+int ipv6_tunnel_output __P((struct mbuf *, struct sockaddr_in6 *, struct rtentry *));
+
+int ipv6_setmoptions __P((int, struct inpcb *, void *, size_t));
+int ipv6_getmoptions __P((int, struct ipv6_moptions *, int *));
+void ipv6_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in6 *));
+int ipv6_finddivpoint __P((struct mbuf *, uint8_t *));
+
+int ipv6_controltoheader(struct mbuf **m, struct mbuf *control, struct ifnet **forceifp, int *);
+
+/*----------------------------------------------------------------------
+ * IPv6 output routine. The mbuf chain contains a near-complete IPv6 header,
+ * and an already-inserted list of options. (I figure it's something for
+ * the code with PCB access to handle.) The options should have their
+ * fields in network order. The header should have its fields in host order.
+ * (Save the addresses, which IMHO are always in network order. Weird.)
+ ----------------------------------------------------------------------*/
+
+int
+ipv6_output(outgoing,ro,flags,i6mo, forceifp, socket)
+ struct mbuf *outgoing;
+ struct route6 *ro;
+ int flags;
+ struct ipv6_moptions *i6mo;
+ struct ifnet *forceifp;
+ struct socket *socket;
+{
+ struct ipv6 *header;
+ struct route6 ipv6route;
+ struct sockaddr_in6 *dst;
+ struct in6_ifaddr *i6a = NULL;
+ struct ifnet *ifp = NULL;
+ int error=0;
+ uint32_t outmtu = 0;
+
+#ifdef DIAGNOSTIC
+ if ((outgoing->m_flags & M_PKTHDR) == 0)
+ panic("ipv6_output() no HDR");
+#endif
+
+ /*
+ * Assume the IPv6 header is already contiguous.
+ */
+ header = mtod(outgoing, struct ipv6 *);
+
+ DDO(IDL_FINISHED,printf("ipv6_output:\n");dump_ipv6(header));
+ DPRINTF(IDL_FINISHED,("\n"));
+
+ /*
+ * Fill in v6 header. Assume flow id/version field is in network order,
+ * and that the high 4 bits are 0's.
+ */
+
+ if ((flags & (IPV6_FORWARDING|IPV6_RAWOUTPUT)) == 0)
+ {
+ header->ipv6_versfl = htonl(0x60000000) |
+ (header->ipv6_versfl & htonl(0x0fffffff));
+ ipv6stat.ips_localout++;
+ }
+
+ /*
+ * Determine interface and physical destination to send datagram out
+ * towards. Do this by looking up a route, or using the route we were
+ * passed.
+ */
+
+ DPRINTF(IDL_FINISHED,("route passed to ipv6_output is:\n"));
+ DDO(IDL_FINISHED,if (ro) dump_rtentry(ro->ro_rt));
+ if (ro == 0)
+ {
+ ro = &ipv6route;
+ bzero((caddr_t)ro,sizeof(struct route6));
+ }
+ dst = &ro->ro_dst;
+
+ if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
+ !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &header->ipv6_dst)))
+ {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = NULL;
+ }
+
+ if (ro->ro_rt == NULL)
+ {
+ dst->sin6_family = AF_INET6;
+ dst->sin6_len = sizeof(struct sockaddr_in6);
+ dst->sin6_addr = header->ipv6_dst;
+ dst->sin6_port = 0;
+ dst->sin6_flowinfo = 0;
+ }
+
+#define ifatoi6a(ifa) ((struct in6_ifaddr *)(ifa))
+#define sin6tosa(sin6) ((struct sockaddr *)(sin6))
+
+ if (flags & IPV6_ROUTETOIF)
+ {
+ /*
+ * Check for route to interface only. (i.e. the user doesn't want
+ * to rely on the routing tables, so send it out an interface).
+ */
+ if ((i6a = ifatoi6a(ifa_ifwithdstaddr(sin6tosa(dst)))) == 0 &&
+ (i6a = ifatoi6a(ifa_ifwithnet(sin6tosa(dst)))) == 0 )
+ {
+ /*
+ * Q: Do we want to assume that if a user specifies this option,
+ * the user doesn't want ANYTHING to do with the routing tables?
+ */
+
+ ipv6stat.ips_noroute++;
+ error = ENETUNREACH;
+ goto bad;
+ }
+ ifp = i6a->i6a_ifp;
+ header->ipv6_hoplimit = 1;
+ outmtu = ifp->if_mtu;
+ }
+ else
+ {
+ /*
+ * Do normal next-hop determination with the help of the routing tree.
+ */
+ if (ro->ro_rt == 0)
+ rtalloc((struct route *)ro); /* Initial route lookup. */
+
+ if (ro->ro_rt == 0)
+ {
+ /*
+ * No route of any kind, so spray neighbor solicits out all
+ * interfaces, unless it's a multicast address.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&header->ipv6_dst))
+ goto mcast;
+ DPRINTF(IDL_FINISHED, ("v6_output doesn't have a route...calling onlink_query!\n"));
+ ipv6_onlink_query(dst);
+ rtalloc((struct route *)ro);
+ }
+ if (ro->ro_rt == NULL)
+ {
+ /*
+ * ipv6_onlink_query() should've added a route. Probably
+ * failed.
+ */
+ DPRINTF(IDL_GROSS_EVENT, ("v6_output: onlink_query didn't add route!\n"));
+ ipv6stat.ips_noroute++;
+ error = ENETUNREACH;
+ goto bad;
+ }
+
+ if (ro->ro_rt->rt_ifa == NULL)
+ {
+ /*
+ * We have a route where we don't quite know which interface
+ * the neighbor belongs to yet. If I get here, I know that this
+ * route is not pre-allocated (such as done by in6_pcbconnect()),
+ * because those pre-allocators will do the same ipv6_onlink_query()
+ * and ipv6_verify_onlink() in advance.
+ *
+ * I can therefore free the route, and get it again.
+ * Multicast dgrams should NEVER be in this code path.
+ */
+
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = NULL;
+ DPRINTF(IDL_FINISHED,("v6_output calling ipv6_verify_onlink\n"));
+ if ((error = ipv6_verify_onlink(dst)) != 0)
+ {
+ if (error == -1)
+ {
+ DPRINTF(IDL_ERROR,("verify_onlink() failed in v6_out.\n"));
+ error = ENETUNREACH;
+ }
+ ipv6stat.ips_noroute++; /* Better stat needed, because
+ error might not be
+ E{NET,HOST}UNREACH. */
+
+ goto bad;
+ }
+ rtalloc((struct route *)ro);
+ if (ro->ro_rt == NULL || ro->ro_rt->rt_ifa == NULL)
+ panic("Oops, I'm forgetting something after verify_onlink().");
+ }
+
+ /*
+ * Exploit properties of route.
+ */
+
+ ifp = ro->ro_rt->rt_ifp; /* Q: Is this REALLY the ifp
+ for the route?
+
+ A: Maybe. If multi-homed,
+ and we attempt to
+ intelligently figure out
+ where link-locals are
+ destined, then we're
+ in trouble. */
+ /*
+ * On-net route exists, but no destination as of yet. This can
+ * be snipped if an ifp is just selected. (Depends on multihomed
+ * experience.)
+ *
+ * Currently, this code never executes, because we guarantee rt_ifp is
+ * set. This may, however, change in later versions of this code as
+ * we gain multihomed experience.
+ */
+ if (ifp == NULL && ro->ro_rt->rt_gateway->sa_family == AF_LINK)
+ {
+ DPRINTF(IDL_EVENT,\
+ ("ipv6_output() calling ipv6_nsolicit(2)\n"));
+ ipv6_nsolicit(NULL, outgoing, ro->ro_rt);
+ DPRINTF(IDL_EVENT,\
+ ("ipv6_output() attempted to send neighbor solicit(2), returning.\n"));
+ goto done;
+ }
+
+ /*
+ * Q: What if address has expired? Perhaps I should use ifp to
+ * obtain optimal i6a value. There's also the question of using
+ * link-local source addresses for off-link communication. (or for
+ * that matter, on-link, but not link-local communication.
+ *
+ * Q2: Perhaps use this time to reset the route's ifa?
+ * Q3: Perhaps it is better to use the ipv6_rtrequest()?
+ *
+ * Regardless, i6a's only use in this function is to determine the
+ * source address of the packet.
+ *
+ * Currently, ipv6_rtrequest() attempts to store a decent in6_ifaddr
+ * in rt_ifa. This also may change with experience.
+ */
+
+ i6a = ifatoi6a(ro->ro_rt->rt_ifa);
+ if (i6a->i6a_addrflags & I6AF_NOTSURE)
+ if (!(outgoing->m_flags & M_DAD))
+ {
+ /*
+ * 1. Think of a better error.
+ *
+ * 2. Keep some sort of statistic.
+ */
+ DPRINTF(IDL_ERROR,("Using NOTSURE source address.\n"));
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+ else i6a = NULL;
+
+ /*
+ * More source address selection goes here.
+ */
+
+ ro->ro_rt->rt_use++;
+ /*
+ * Path MTU comes from the route entry.
+ */
+ outmtu = ro->ro_rt->rt_rmx.rmx_mtu;
+
+ if (ro->ro_rt->rt_flags & RTF_GATEWAY) /* Gateway/router/whatever. */
+ dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway;
+ }
+
+ if (forceifp) {
+ DPRINTF(IDL_EVENT, ("ipv6_output: in forceifp case\n"));
+ ifp = forceifp;
+ if (outmtu > ifp->if_mtu)
+ outmtu = ifp->if_mtu;
+ };
+
+ /*
+ * Handle case of a multicast destination.
+ */
+ mcast:
+ if (IN6_IS_ADDR_MULTICAST(&header->ipv6_dst))
+ {
+ struct in6_multi *in6m;
+
+ outgoing->m_flags |= M_MCAST;
+
+ dst = &ro->ro_dst;
+
+ if (i6mo != NULL)
+ {
+ /*
+ * As we gain more multicast experience, use i6mo fields to alter
+ * properties of outgoing packet. (Including, quite possibly,
+ * the source address.)
+ */
+ if (i6mo->i6mo_multicast_ifp != NULL)
+ ifp = i6mo->i6mo_multicast_ifp;
+ header->ipv6_hoplimit = i6mo->i6mo_multicast_ttl;
+ }
+ else
+ {
+ /*
+ * Use default values, since there are no multicast options to
+ * use.
+ */
+ if (ifp == NULL)
+ ifp = mcastdefault;
+ header->ipv6_hoplimit = IPV6_DEFAULT_MCAST_HOPS;
+ }
+
+ if (outmtu == 0) /* But what about mcast Path MTU? */
+ outmtu = ifp->if_mtu;
+
+ if ((ifp->if_flags & IFF_MULTICAST) == 0)
+ {
+ ipv6stat.ips_noroute++;
+ error = ENETUNREACH;
+ goto bad;
+ }
+
+ if ((IN6_IS_ADDR_UNSPECIFIED(&header->ipv6_src) && !(outgoing->m_flags & M_DAD)) ||
+ (IN6_IS_ADDR_LINKLOCAL(&header->ipv6_src) &&
+ GET_IN6_MCASTSCOPE(header->ipv6_dst) > IN6_INTRA_LINK))
+ {
+ register struct in6_ifaddr *i6a;
+
+ /*
+ * Source address selection for multicast datagrams.
+ * If link-local source, get in here too, because you don't want
+ * link-local addresses going on non-local multicast.
+ *
+ * Eventually should fix this to perform best source address
+ * selection. Probably should separate this out into a function.
+ */
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ if (i6a->i6a_ifp == ifp)
+ {
+ header->ipv6_src = I6A_SIN(i6a)->sin6_addr;
+ break;
+ }
+ }
+
+ IN6_LOOKUP_MULTI(&header->ipv6_dst, ifp, in6m);
+ DPRINTF(IDL_GROSS_EVENT,("in6m == 0x%lx, i6mo == 0x%lx\n", (unsigned long)in6m, (unsigned long)i6mo));
+ if (in6m != NULL &&
+ (i6mo == NULL || i6mo->i6mo_multicast_loop))
+ {
+
+ /*
+ * See ipv6_mloopback for details, but that function will tag
+ * the packet with the actual interface the multicast is
+ * supposed to go out. This makes duplicate address detection
+ * harder to implement, because the inbound mbuf SHOULD be tagged
+ * as coming from me for the case of solicits. (Perhaps burning
+ * another flag...)
+ */
+ DPRINTF(IDL_GROSS_EVENT,("Calling ipv6_mloopback().\n"));
+ ipv6_mloopback(ifp, outgoing, dst);
+ }
+
+#ifdef MROUTING
+ /*
+ * Do m-cast routing even if I can't find it in my m-cast list.
+ */
+#endif
+
+ /*
+ * If intra-node scope. I've already hit it with ipv6_mloopback above.
+ */
+
+ if (GET_IN6_MCASTSCOPE(header->ipv6_dst) == IN6_INTRA_NODE || (ifp->if_flags & IFF_LOOPBACK))
+ goto bad; /* Not really bad, y'know, just getting out of here. */
+ }
+
+ if (ro->ro_rt == NULL && outmtu == 0)
+ panic("ipv6_output: How did I get here without a route or MTU?");
+
+ /*
+ * Specify source address. Use i6a, for now.
+ */
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&header->ipv6_src) && i6a != NULL &&
+ !(outgoing->m_flags & M_DAD))
+ header->ipv6_src = I6A_SIN(i6a)->sin6_addr;
+
+ DPRINTF(IDL_FINISHED,("header & chain before security check are:\n"));
+ DDO(IDL_FINISHED,dump_ipv6(header));
+ DDO(IDL_FINISHED,dump_mchain(outgoing));
+
+#ifdef IPSEC
+ if (!(flags & IPV6_FORWARDING)) {
+ size_t preoverhead, postoverhead;
+ void *state;
+
+ /* NB: If there exists a configured secure tunnel, then
+ the packet being tunneled will have been encapsulated
+ inside an IP packet with (src=me, dest=tunnel-end-point)
+ PRIOR to ip_output() being called, so the above
+ check doesn't preclude secure tunnelling. rja */
+ /*
+ * I would like to just pass in &ia->ia_addr, but there is a small
+ * chance that the source address doesn't match ia->ia_addr.
+ *
+ * Also, if you need a dest. port, fill in ro->ro_dst with it.
+ */
+ {
+ struct sockaddr_in6 srcsa, dstsa;
+
+ bzero(&srcsa, sizeof(struct sockaddr_in6));
+ srcsa.sin6_family = AF_INET6;
+ srcsa.sin6_len = sizeof(struct sockaddr_in6);
+ /* XXX - port */
+ srcsa.sin6_addr = header->ipv6_src;
+
+ bzero(&dstsa, sizeof(struct sockaddr_in6));
+ dstsa.sin6_family = AF_INET6;
+ dstsa.sin6_len = sizeof(struct sockaddr_in6);
+ /* XXX - port */
+ dstsa.sin6_addr = header->ipv6_dst;
+
+ /* XXX - get the ULP protocol number */
+ if (error = netproc_outputpolicy(socket, (struct sockaddr *)&srcsa,
+ (struct sockaddr *)&dstsa, header->ipv6_nexthdr, &preoverhead,
+ &postoverhead, &state)) {
+ if (error == EACCES) /* XXX - means fail silently */
+ error = 0;
+ goto bad;
+ }
+ }
+
+ if (state) {
+ struct nbuf *nbuf;
+
+ DP(FINISHED, preoverhead, d);
+ DP(FINISHED, postoverhead, d);
+ {
+ struct netproc_policycache *policycache =
+ (struct netproc_policycache *)state;
+
+ DP(FINISHED, policycache->doah, d);
+ DP(FINISHED, policycache->doesp, d);
+ DP(FINISHED, policycache->docombinedesp, d);
+ }
+
+ if (!(nbuf = mton(outgoing, preoverhead, postoverhead))) {
+ netproc_outputfree(state);
+ error = ENOMEM;
+ goto bad;
+ }
+
+ outgoing = NULL;
+
+ if (error = netproc_output(state, nbuf)) {
+ if (error == EACCES)
+ error = 0;
+ }
+
+ /* If successful, netproc_output actually does the output.
+ Either way, it frees the nbuf. */
+ goto done;
+ }
+ }
+#endif /* defined(IPSEC) || defined(NRL_IPSEC) */
+
+ /*
+ * Assume above three return a contiguous and UPDATED IPv6 header.
+ */
+ header = mtod(outgoing,struct ipv6 *);
+
+ /*
+ * Determine the outbound i6a to record statistics.
+ */
+ if (flags & IPV6_FORWARDING)
+ i6a = NULL;
+ else if (i6a == NULL ||
+ !IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &header->ipv6_src))
+ {
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ if (i6a->i6a_ifp == ifp &&
+ IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &header->ipv6_src))
+ break;
+ }
+
+ /*
+ * If small enough for path MTU, send, otherwise, fragment.
+ */
+
+ DPRINTF(IDL_FINISHED,("Before output, path mtu = %d, header is:\n",\
+ (int)outmtu));
+ DDO(IDL_FINISHED,dump_ipv6(header));
+ DDO(IDL_FINISHED,printf("Chain is:\n");dump_mchain(outgoing));
+
+#if 0
+/* DEBUG tunnel */
+ DPRINTF(IDL_EVENT,("ROUTE passed to ipv6_output is:\n"));
+ DDO(IDL_EVENT,if (ro) dump_rtentry(ro->ro_rt));
+ if (ro->ro_rt && (ro->ro_rt->rt_flags & RTF_TUNNEL))
+ DPRINTF(IDL_FINISHED,("HEY !! I see the tunnel!!!\n"));
+ else {
+ DPRINTF(IDL_FINISHED,("HEY !! I can't see the tunnel!!!\n"));
+ if (ro->ro_rt == NULL)
+ DPRINTF(IDL_FINISHED,("ro->ro_rt is null!!\n"));
+ else
+ {
+ DPRINTF(IDL_FINISHED,("ro->ro_rt is not null!!\n"));
+ if (ro->ro_rt->rt_flags & RTF_TUNNEL)
+ DPRINTF(IDL_FINISHED,("HEY, I can see RTFTUNNEL!\n"));
+ else
+ DPRINTF(IDL_FINISHED,("HEY, I can't see RTFTUNNEL!\n"));
+ }
+ }
+/* END OF DEBUG tunnel */
+#endif /* 0 */
+
+ if (outgoing->m_pkthdr.len <= outmtu)
+ {
+DPRINTF(IDL_EVENT,("IPV6_OUTPUT(): Not entering fragmenting engine.\n"));
+ header->ipv6_length = htons(outgoing->m_pkthdr.len -
+ sizeof(struct ipv6));
+
+ /*
+ * If there is a route, and its TUNNEL bit is turned on, do not send
+ * out the interface, but send through a tunneling routine, which will,
+ * given information from the route, encapsulate the packet accordingly.
+ *
+ * Keith Sklower suggested a "rt_output() method" which would save
+ * the checking here.
+ */
+ if (ro->ro_rt && (ro->ro_rt->rt_flags & RTF_TUNNEL)) {
+ DPRINTF(IDL_EVENT,("ipv6_output():-Sending out IPV6 in IPV4/6 tunnel.\n"));
+ error = ipv6_tunnel_output(outgoing, dst, ro->ro_rt);
+ } else {
+#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
+ if (i6a) {
+ i6a->i6a_ifa.ifa_opackets++;
+ i6a->i6a_ifa.ifa_obytes += outgoing->m_pkthdr.len;
+ }
+#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+ error = (*ifp->if_output)(ifp, outgoing, (struct sockaddr *)dst, ro->ro_rt);
+ }
+ DPRINTF(IDL_FINISHED,("Lone IPv6 went out if (error = %d).\n",error));
+ goto done;
+ }
+
+
+ /*
+ * If I make it here, then the packet is too big for the path MTU, and must
+ * be fragmented.
+ */
+
+ DPRINTF(IDL_EVENT,("IPV6_OUTPUT(): Entering fragmenting engine.\n"));
+
+ if (flags & IPV6_FORWARDING)
+ {
+ error = EMSGSIZE;
+ goto bad;
+ }
+
+ if (outgoing->m_pkthdr.len > 0xffff) {
+ DPRINTF(IDL_ERROR,("Jumbogram needs fragmentation, something that can't be done\n"));
+ ipv6stat.ips_odropped++; /* ?!? */
+ error = EINVAL;
+ goto bad;
+ }
+
+ /*
+ * The following check should never really take place.
+ */
+#ifdef DIAGNOSTIC
+ if (outmtu < IPV6_MINMTU)
+ {
+ DPRINTF(IDL_ERROR,("Outbound MTU is less than IPV6_MINMTU (%d).\n",\
+ IPV6_MINMTU));
+ error = ENETUNREACH; /* Can you think of a better idea? */
+ goto bad;
+ }
+#endif
+
+ /*
+ * ipv6_fragment returns a chain of outgoing packets. It returns NULL
+ * if something went wrong.
+ */
+ outgoing = ipv6_fragment(outgoing,outmtu);
+ if (outgoing == NULL)
+ error = ENOBUFS; /* Can you think of a better idea? */
+
+ DPRINTF(IDL_FINISHED,\
+ ("ipv6_fragment() returned, attempting to send fragments out.\n"));
+
+ /*
+ * Walk through chain of fragments, and send them out.
+ */
+ while (outgoing != NULL)
+ {
+ struct mbuf *current = outgoing;
+
+ DPRINTF(IDL_FINISHED,("In fragment-sending loop, error == %d.\n",\
+ error));
+ outgoing = current->m_nextpkt;
+ current->m_nextpkt = NULL;
+
+ DDO(IDL_FINISHED,printf("Current (0x%lx) 1st mbuf is:\n", (unsigned long)current);\
+ dump_mbuf(current));
+
+ if (error != 0)
+ m_freem(current);
+ else
+ if (ro->ro_rt && (ro->ro_rt->rt_flags & RTF_TUNNEL)) {
+ DPRINTF(IDL_EVENT,("Sending fragments out tunnel.\n"));
+ error = ipv6_tunnel_output(current, dst, ro->ro_rt);
+ } else {
+ DPRINTF(IDL_EVENT,("After if_output(), error == %d.\n",error));
+#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
+ if (i6a) {
+ i6a->i6a_ifa.ifa_opackets++;
+ i6a->i6a_ifa.ifa_obytes += current->m_pkthdr.len;
+ }
+#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
+ error = (*ifp->if_output)(ifp, current,(struct sockaddr *)dst, ro->ro_rt);
+ }
+ }
+
+ if (error == 0)
+ ipv6stat.ips_fragmented++;
+
+done:
+ if (ro == &ipv6route && (flags & IPV6_ROUTETOIF) == 0 && ro->ro_rt)
+ RTFREE(ro->ro_rt);
+ return (error);
+
+bad:
+ if (outgoing != NULL)
+ m_freem(outgoing);
+ goto done;
+}
+
+#define INDEX_TO_IFP(index, ifp)\
+{\
+ struct in6_ifnet *i6ifp; \
+ for (i6ifp = in6_ifnet; i6ifp; i6ifp = i6ifp->i6ifp_next) \
+ if (i6ifp->i6ifp_ifp->if_index == index) { \
+ (ifp) = i6ifp->i6ifp_ifp; \
+ break; \
+ }; \
+} \
+
+/*----------------------------------------------------------------------
+ * Set IPv6 multicast options.
+ ----------------------------------------------------------------------*/
+int ipv6_setmoptions(int optname, struct inpcb *inp, void *p, size_t len)
+{
+ register int error = 0;
+ register int i;
+ register struct ipv6_mreq *mreq;
+ register struct ifnet *ifp = NULL;
+ register struct ipv6_moptions *imo = inp->inp_moptions6;
+ struct route6 ro;
+
+ if (imo == NULL)
+ {
+ imo = (struct ipv6_moptions *)malloc(sizeof(*imo), M_IPMOPTS,M_WAITOK);
+ if (imo == NULL)
+ return ENOBUFS;
+ inp->inp_moptions6 = imo;
+ inp->inp_flags |= INP_IPV6_MCAST;
+ imo->i6mo_multicast_ifp = NULL;
+ imo->i6mo_multicast_ttl = IPV6_DEFAULT_MCAST_HOPS;
+ imo->i6mo_multicast_loop = IPV6_DEFAULT_MCAST_LOOP;
+ imo->i6mo_num_memberships = 0;
+ }
+ else /* Only if points to v6 moptions can I set them! */
+ if (!(inp->inp_flags & INP_IPV6_MCAST))
+ return EEXIST;
+
+ switch (optname)
+ {
+ case IPV6_MULTICAST_IF:
+ {
+ unsigned int index;
+ if (!p || (len != sizeof(unsigned int))) {
+ error = EINVAL;
+ break;
+ }
+
+ index = *((int *)p);
+
+ if (!index) {
+ imo->i6mo_multicast_ifp = NULL;
+ break;
+ }
+
+ INDEX_TO_IFP(index, ifp);
+ if (!ifp || !(ifp->if_flags & IFF_MULTICAST))
+ error = EADDRNOTAVAIL;
+ else
+ imo->i6mo_multicast_ifp = ifp;
+ };
+ break;
+
+ case IPV6_MULTICAST_HOPS:
+ /*
+ * Set the IPv6 hop limit for outgoing multicast packets.
+ */
+ if (!p || (len != sizeof(int))) {
+ error = EINVAL;
+ break;
+ }
+ if (*((int *)p) == -1)
+ imo->i6mo_multicast_ttl = IPV6_DEFAULT_MCAST_HOPS;
+ else
+ if ((*((int *)p) > -1) && (*((int *)p) < 256))
+ imo->i6mo_multicast_ttl = *((int *)p);
+ else
+ error = EINVAL;
+ break;
+
+ case IPV6_MULTICAST_LOOP:
+ /*
+ * Set the loopback flag for outgoing multicast packets.
+ * Must be zero or one.
+ */
+ if (!p || (len != sizeof(int))) {
+ error = EINVAL;
+ break;
+ }
+ switch(*((int *)p)) {
+ case 0:
+ case 1:
+ imo->i6mo_multicast_loop = *((int *)p);
+ break;
+ case -1:
+ imo->i6mo_multicast_loop = IPV6_DEFAULT_MCAST_LOOP;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ };
+ break;
+ case IPV6_ADD_MEMBERSHIP:
+ /*
+ * Add a multicast group membership.
+ * Group must be a valid IP multicast address.
+ */
+ if (!p || (len != sizeof(struct ipv6_mreq))) {
+ error = EINVAL;
+ break;
+ }
+ mreq = (struct ipv6_mreq *)p;
+ if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
+ error = EINVAL;
+ break;
+ }
+ /*
+ * If no interface address was provided, use the interface of
+ * the route to the given multicast address.
+ */
+ if (!mreq->ipv6mr_interface) {
+ ro.ro_rt = NULL;
+ ro.ro_dst.sin6_family = AF_INET6;
+ ro.ro_dst.sin6_len = sizeof(struct sockaddr_in6);
+ ro.ro_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 {
+ INDEX_TO_IFP(mreq->ipv6mr_interface, ifp);
+ }
+ /*
+ * See if we found an interface, and confirm that it
+ * supports multicast.
+ */
+ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ /*
+ * See if the membership already exists or if all the
+ * membership slots are full.
+ */
+ for (i = 0; i < imo->i6mo_num_memberships; ++i) {
+ if (imo->i6mo_membership[i]->in6m_ifp == ifp &&
+ IN6_ARE_ADDR_EQUAL(&mreq->ipv6mr_multiaddr,
+ &imo->i6mo_membership[i]->in6m_addr))
+ break;
+ }
+ if (i < imo->i6mo_num_memberships) {
+ error = EADDRINUSE;
+ break;
+ }
+ if (i == IN6_MAX_MEMBERSHIPS) {
+ error = ETOOMANYREFS;
+ break;
+ }
+ /*
+ * Everything looks good; add a new record to the multicast
+ * address list for the given interface.
+ */
+ if ((imo->i6mo_membership[i] = in6_addmulti(&mreq->ipv6mr_multiaddr, ifp))
+ == NULL)
+ {
+ error = ENOBUFS;
+ break;
+ }
+ ++imo->i6mo_num_memberships;
+ break;
+
+ case IPV6_DROP_MEMBERSHIP:
+ /*
+ * Drop a multicast group membership.
+ * Group must be a valid IP multicast address.
+ */
+ if (!p || (len != sizeof(struct ipv6_mreq))) {
+ error = EINVAL;
+ break;
+ }
+ mreq = (struct ipv6_mreq *)p;
+ if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
+ error = EINVAL;
+ break;
+ }
+
+ /*
+ * If an interface index was specified, get a pointer to its ifnet
+ * structure.
+ */
+ if (!mreq->ipv6mr_interface)
+ ifp = NULL;
+ else {
+ INDEX_TO_IFP(mreq->ipv6mr_interface, ifp);
+ if (ifp == NULL) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ }
+
+ /*
+ * Find the membership in the membership array.
+ */
+ for (i = 0; i < imo->i6mo_num_memberships; ++i) {
+ if ((ifp == NULL ||
+ imo->i6mo_membership[i]->in6m_ifp == ifp) &&
+ IN6_ARE_ADDR_EQUAL(&imo->i6mo_membership[i]->in6m_addr,
+ &mreq->ipv6mr_multiaddr))
+ break;
+ }
+
+ if (i == imo->i6mo_num_memberships) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ /*
+ * Give up the multicast address record to which the
+ * membership points.
+ */
+ in6_delmulti(imo->i6mo_membership[i]);
+ /*
+ * Remove the gap in the membership array.
+ */
+ for (++i; i < imo->i6mo_num_memberships; ++i)
+ imo->i6mo_membership[i-1] = imo->i6mo_membership[i];
+
+ --imo->i6mo_num_memberships;
+
+ break;
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ if (imo->i6mo_multicast_ifp == NULL &&
+ imo->i6mo_multicast_ttl == IPV6_DEFAULT_MCAST_HOPS &&
+ imo->i6mo_multicast_loop == IPV6_DEFAULT_MCAST_LOOP &&
+ imo->i6mo_num_memberships == 0) {
+ free(inp->inp_moptions6, M_IPMOPTS);
+ inp->inp_moptions6 = NULL;
+ inp->inp_flags &= ~INP_IPV6_MCAST;
+ }
+
+ return (error);
+}
+
+#define IFP_TO_INDEX(ifp, index) \
+{\
+ (index) = ifp->if_index; \
+}
+
+/*----------------------------------------------------------------------
+ * Get IPv6 multicast options.
+ ----------------------------------------------------------------------*/
+/* ... now assumes all returned values are ints... */
+int ipv6_getmoptions(int optname, struct ipv6_moptions *i6mo, int *mp)
+{
+ switch (optname)
+ {
+ case IPV6_MULTICAST_IF:
+ if (!i6mo == NULL || !i6mo->i6mo_multicast_ifp)
+ *mp = 0;
+ else {
+ IFP_TO_INDEX(i6mo->i6mo_multicast_ifp, *(unsigned int *)mp);
+ }
+ return (0);
+
+ case IPV6_MULTICAST_HOPS:
+ *mp = i6mo ? IPV6_DEFAULT_MCAST_HOPS : i6mo->i6mo_multicast_ttl;
+ return (0);
+
+ case IPV6_MULTICAST_LOOP:
+ *mp = i6mo ? IPV6_DEFAULT_MCAST_LOOP : i6mo->i6mo_multicast_loop;
+ return (0);
+
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
+/*----------------------------------------------------------------------
+ * Free IPv6 multicast options.
+ ----------------------------------------------------------------------*/
+void
+ipv6_freemoptions(i6mo)
+ register struct ipv6_moptions *i6mo;
+{
+ register int i;
+
+ if (i6mo != NULL)
+ {
+ for (i = 0 ; i < i6mo->i6mo_num_memberships ; i++)
+ in6_delmulti(i6mo->i6mo_membership[i]);
+ free(i6mo, M_IPMOPTS);
+ }
+}
+
+/*----------------------------------------------------------------------
+ * Handler for IPV6 [gs]etsockopt() calls. One problem arises when an
+ * AF_INET6 socket actually wants to set IPv4 options.
+ *
+ * The decision to call this or to call ip_ctloutput() is best left in
+ * the hands of TCP/UDP/etc., which have information about which IP is
+ * in use.
+ *
+ ----------------------------------------------------------------------*/
+
+#if __FreeBSD__
+int ipv6_ctloutput(struct socket *so, struct sockopt *sopt)
+{
+ register struct inpcb *inp;
+ int op;
+ int level;
+ int optname;
+ int optval;
+ int error = 0;
+
+ DPRINTF(FINISHED, ("ipv6_ctloutput(so=%08x, sopt=%08x)\n", OSDEP_PCAST(so),
+ OSDEP_PCAST(sopt)));
+
+ inp = sotoinpcb(so);
+
+ switch(sopt->sopt_dir) {
+ case SOPT_GET:
+ op = PRCO_GETOPT;
+ break;
+ case SOPT_SET:
+ op = PRCO_SETOPT;
+ break;
+ default:
+ return EINVAL;
+ };
+
+ level = sopt->sopt_level;
+ optname = sopt->sopt_name;
+
+ DS();
+#else /* __FreeBSD__ */
+int
+ipv6_ctloutput (op, so, level, optname, mp)
+ int op;
+ struct socket *so;
+ int level;
+ int optname;
+ struct mbuf **mp;
+{
+ register struct inpcb *inp = sotoinpcb(so);
+ struct mbuf *m = *mp;
+ int error = 0;
+
+ DPRINTF(IDL_EVENT, ("ipv6_ctloutput(op=%x,so=%08lx,level=%x,optname=%x,mp)\n", op, (unsigned long)so, level, optname));
+#endif /* __FreeBSD__ */
+
+ if ((level != IPPROTO_IP) && (level != IPPROTO_IPV6) && (level != IPPROTO_ROUTING) && (level != IPPROTO_ICMPV6)) {
+#if !__FreeBSD__
+ if (op == PRCO_SETOPT && *mp)
+ m_free(*mp);
+#endif /* !__FreeBSD__ */
+ return EINVAL;
+ }
+
+ DS();
+ switch (op) {
+ case PRCO_SETOPT:
+ switch(optname) {
+ case IPV6_UNICAST_HOPS:
+ DPRINTF(IDL_GROSS_EVENT, ("ipv6_ctloutput: Reached IPV6_UNICAST_HOPS\n"));
+#if __FreeBSD__
+ if (sopt->sopt_valsize != sizeof(int))
+#else /* __FreeBSD__ */
+ if (m->m_len != sizeof(int))
+#endif /* __FreeBSD__ */
+ error = EINVAL;
+ else {
+ struct tcpcb *tp;
+#if __FreeBSD__
+ int i;
+
+ if (error = sooptcopyin(sopt, &i, sizeof(int), sizeof(int)))
+ break;
+#else /* __FreeBSD__ */
+ int i = *mtod(m, int *);
+#endif /* __FreeBSD__ */
+
+ if (i == -1)
+ i = ipv6_defhoplmt;
+
+ if ((i < 0) || (i > 255)) {
+ error = EINVAL;
+ break;
+ };
+
+ inp->inp_ipv6.ipv6_hoplimit = i;
+
+ /*
+ * Minor optimization for TCP. We change the hoplimit
+ * in the template here so we don't have to do the extra
+ * load before the ipv6_output() call in tcp_output() for
+ * every single packet (as is the case for IPv4).
+ */
+ DS();
+#if !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)
+ if ((so->so_type == SOCK_STREAM) && (tp = intotcpcb(inp)) &&
+ tp->t_template)
+#if __FreeBSD__
+ (mtod(tp->t_template, struct ipv6 *))->ipv6_hoplimit = i;
+#else /* __FreeBSD__ */
+ ((struct ipv6 *)(tp->t_template))->ipv6_hoplimit = i;
+#endif /* __FreeBSD__ */
+#else /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
+ if ((so->so_type == SOCK_STREAM) && (tp = intotcpcb(inp)))
+ ((struct ipv6 *)(tp->t_tcpiphdr))->ipv6_hoplimit = i;
+#endif /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
+ }
+ break;
+ case IPV6_MULTICAST_IF:
+ case IPV6_MULTICAST_HOPS:
+ case IPV6_MULTICAST_LOOP:
+ case IPV6_DROP_MEMBERSHIP:
+ case IPV6_ADD_MEMBERSHIP:
+#if __FreeBSD__
+ {
+ void *val;
+
+ if (!(val = OSDEP_MALLOC(sopt->sopt_valsize))) {
+ error = ENOMEM;
+ break;
+ };
+
+ if (error = sooptcopyin(sopt, val, sopt->sopt_valsize,
+ sopt->sopt_valsize)) {
+ OSDEP_FREE(val);
+ break;
+ };
+
+ error = ipv6_setmoptions(optname, inp, val, sopt->sopt_valsize);
+ OSDEP_FREE(val);
+ };
+#else /* __FreeBSD__ */
+ error = ipv6_setmoptions(optname, inp, mtod(m, void *), m->m_len);
+#endif /* __FreeBSD__ */
+ break;
+ case IPV6_ADDRFORM:
+ {
+ int newpf;
+ int oldpf = sotopf(inp->inp_socket);
+ union inpaddru new_faddru;
+ union inpaddru new_laddru;
+ int new_flags;
+ struct protosw *new_proto;
+ int s;
+
+#if __FreeBSD__
+ if (sopt->sopt_valsize != sizeof(int)) {
+ DPRINTF(IDL_ERROR, ("addrform: valsize = %d\n",
+ sopt->sopt_valsize));
+#else /* __FreeBSD__ */
+ if (m->m_len != sizeof(int)) {
+ DPRINTF(IDL_ERROR, ("addrform: m->m_len = %d\n", m->m_len));
+#endif /* __FreeBSD__ */
+ error = EINVAL;
+ break;
+ };
+
+#if __FreeBSD__
+ if (error = sooptcopyin(sopt, &newpf, sizeof(int), sizeof(int)))
+ break;
+#else /* __FreeBSD__ */
+ newpf = *(mtod(m, int *));
+#endif /* __FreeBSD__ */
+
+ DPRINTF(IDL_ERROR, ("newpf = %d, oldpf = %d", newpf, oldpf));
+
+ if (((newpf != AF_INET) && (newpf != AF_INET6)) ||
+ ((oldpf != AF_INET) && (oldpf != AF_INET6)))
+ return EINVAL;
+
+ DP(ERROR, __LINE__, d);
+
+ if (newpf == oldpf)
+ return 0;
+
+ DP(ERROR, inp->inp_flags, 08x);
+
+ if (newpf == AF_INET6)
+ if (!(inp->inp_flags & INP_IPV6_UNDEC) &&
+ !(inp->inp_flags & INP_IPV6_MAPPED))
+ return EINVAL;
+
+ DP(ERROR, __LINE__, d);
+
+ if (!(new_proto = pffindproto(newpf,
+ so->so_proto->pr_protocol,
+ so->so_proto->pr_type)))
+ return EINVAL;
+
+ DP(ERROR, new_proto->pr_domain->dom_family, d);
+
+ new_flags = inp->inp_flags;
+ new_faddru = inp->inp_faddru;
+ new_laddru = inp->inp_laddru;
+
+ if (newpf == AF_INET) {
+ if (new_flags & INP_IPV6_UNDEC) {
+ new_flags &= ~(INP_IPV6 | INP_IPV6_MAPPED | INP_IPV6_UNDEC);
+ new_laddru.iau_a4u.inaddr.s_addr = INADDR_ANY;
+ new_faddru.iau_a4u.inaddr.s_addr = INADDR_ANY;
+ } else {
+ new_flags &= ~(INP_IPV6 | INP_IPV6_MAPPED);
+ }
+ } else {
+ new_faddru.iau_addr6.in6a_words[0] = 0;
+ new_faddru.iau_addr6.in6a_words[1] = 0;
+ new_laddru.iau_addr6.in6a_words[0] = 0;
+ new_laddru.iau_addr6.in6a_words[1] = 0;
+
+ if (new_laddru.iau_a4u.inaddr.s_addr == INADDR_ANY) {
+ new_flags |= (INP_IPV6 | INP_IPV6_MAPPED | INP_IPV6_UNDEC);
+ new_faddru.iau_addr6.in6a_words[2] = 0;
+ new_laddru.iau_addr6.in6a_words[2] = 0;
+ } else {
+ new_flags |= (INP_IPV6 | INP_IPV6_MAPPED);
+ new_faddru.iau_addr6.in6a_words[2] = htonl(0xffff);
+ new_faddru.iau_addr6.in6a_words[2] = htonl(0xffff);
+ }
+ }
+
+ s = splnet();
+
+ inp->inp_flags = new_flags;
+ inp->inp_faddru = new_faddru;
+ inp->inp_laddru = new_laddru;
+ so->so_proto = new_proto;
+
+ splx(s);
+ }
+ break;
+ case IPV6_PKTINFO:
+#if __FreeBSD__
+ if (sopt->sopt_valsize != sizeof(int)) {
+#else /* __FreeBSD__ */
+ if (m->m_len != sizeof(int)) {
+#endif /* __FreeBSD__ */
+ error = EINVAL;
+ break;
+ };
+
+#if __FreeBSD__
+ if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int)))
+ break;
+
+ if (optval)
+#else /* __FreeBSD__ */
+ if (*(mtod(m, int *)))
+#endif /* __FreeBSD__ */
+ inp->inp_flags |= INP_RXINFO;
+ else
+ inp->inp_flags &= ~INP_RXINFO;
+ break;
+ case IPV6_HOPOPTS:
+#if __FreeBSD__
+ if (sopt->sopt_valsize != sizeof(int)) {
+#else /* __FreeBSD__ */
+ if (m->m_len != sizeof(int)) {
+#endif /* __FreeBSD__ */
+ error = EINVAL;
+ break;
+ };
+
+#if __FreeBSD__
+ if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int)))
+ break;
+
+ if (optval)
+#else /* __FreeBSD__ */
+ if (*(mtod(m, int *)))
+#endif /* __FreeBSD__ */
+ inp->inp_flags |= INP_RXHOPOPTS;
+ else
+ inp->inp_flags &= ~INP_RXHOPOPTS;
+ break;
+ case IPV6_DSTOPTS:
+#if __FreeBSD__
+ if (sopt->sopt_valsize != sizeof(int)) {
+#else /* __FreeBSD__ */
+ if (m->m_len != sizeof(int)) {
+#endif /* __FreeBSD__ */
+ error = EINVAL;
+ break;
+ };
+
+#if __FreeBSD__
+ if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int)))
+ break;
+
+ if (optval)
+#else /* __FreeBSD__ */
+ if (*(mtod(m, int *)))
+#endif /* __FreeBSD__ */
+ inp->inp_flags |= INP_RXDSTOPTS;
+ else
+ inp->inp_flags &= ~INP_RXDSTOPTS;
+ break;
+ case IPV6_RTHDR:
+#if __FreeBSD__
+ if (sopt->sopt_valsize != sizeof(int)) {
+#else /* __FreeBSD__ */
+ if (m->m_len != sizeof(int)) {
+#endif /* __FreeBSD__ */
+ error = EINVAL;
+ break;
+ };
+
+#if __FreeBSD__
+ if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int)))
+ break;
+
+ if (optval)
+#else /* __FreeBSD__ */
+ if (*(mtod(m, int *)))
+#endif /* __FreeBSD__ */
+ inp->inp_flags |= INP_RXSRCRT;
+ else
+ inp->inp_flags &= ~INP_RXSRCRT;
+ break;
+ case IPV6_HOPLIMIT:
+#if __FreeBSD__
+ if (sopt->sopt_valsize != sizeof(int)) {
+#else /* __FreeBSD__ */
+ if (m->m_len != sizeof(int)) {
+#endif /* __FreeBSD__ */
+ error = EINVAL;
+ break;
+ };
+
+#if __FreeBSD__
+ if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int)))
+ break;
+
+ if (optval)
+#else /* __FreeBSD__ */
+ if (*(mtod(m, int *)))
+#endif /* __FreeBSD__ */
+ inp->inp_flags |= INP_HOPLIMIT;
+ else
+ inp->inp_flags &= ~INP_HOPLIMIT;
+ break;
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+#if !__FreeBSD__
+ if (m)
+ m_free(m);
+#endif /* !__FreeBSD__ */
+ break;
+ case PRCO_GETOPT:
+ switch(optname)
+ {
+ case IPV6_ADDRFORM:
+ {
+ int pf = sotopf(inp->inp_socket);
+ DP(ERROR, pf, d);
+ if ((pf != PF_INET) && (pf != PF_INET6))
+ return EINVAL;
+ DP(ERROR, __LINE__, d);
+#if __FreeBSD__
+ error = sooptcopyout(sopt, &pf, sizeof(int));
+#else /* __FreeBSD__ */
+ if (!(m = m_get(M_NOWAIT, MT_SOOPTS))) {
+ error = ENOBUFS;
+ break;
+ };
+ *mp = m;
+ m->m_len = sizeof(int);
+ *mtod(m, int *) = pf;
+#endif /* __FreeBSD__ */
+ }
+ break;
+ case IPV6_UNICAST_HOPS:
+ DPRINTF(IDL_GROSS_EVENT,("ipv6_ctloutput(): Reached IP_UNICAST_HOPS\n"));
+#if __FreeBSD__
+ error = sooptcopyout(sopt, &inp->inp_ipv6.ipv6_hoplimit,
+ sizeof(int));
+#else /* __FreeBSD__ */
+ if (!(m = m_get(M_NOWAIT, MT_SOOPTS))) {
+ error = ENOBUFS;
+ break;
+ };
+ *mp = m;
+ m->m_len = sizeof(int);
+ *mtod(m, int *) = inp->inp_ipv6.ipv6_hoplimit;
+#endif /* __FreeBSD__ */
+ break;
+ case IPV6_MULTICAST_IF:
+ case IPV6_MULTICAST_HOPS:
+ case IPV6_MULTICAST_LOOP:
+ case IPV6_DROP_MEMBERSHIP:
+ case IPV6_ADD_MEMBERSHIP:
+#if __FreeBSD__
+ error = ipv6_getmoptions(optname, inp->inp_moptions6, &optval);
+ if (error)
+ break;
+
+ error = sooptcopyout(sopt, &optval, sizeof(int));
+#else /* __FreeBSD__ */
+ if (!(*mp = m_get(M_NOWAIT, MT_SOOPTS))) {
+ error = ENOBUFS;
+ break;
+ };
+
+ error = ipv6_getmoptions(optname, inp->inp_moptions6,
+ mtod(*mp, int *));
+#endif /* __FreeBSD__ */
+ break;
+ case IPV6_PKTINFO:
+#if __FreeBSD__
+ optval = (inp->inp_flags & INP_RXINFO) ? 1 : 0;
+ error = sooptcopyout(sopt, &optval, sizeof(int));
+#else /* __FreeBSD__ */
+ if (!(m = m_get(M_NOWAIT, MT_SOOPTS))) {
+ error = ENOBUFS;
+ break;
+ };
+ *mp = m;
+ m->m_len = sizeof(int);
+ *mtod(m, int *) = (inp->inp_flags & INP_RXINFO) ? 1 : 0;
+#endif /* __FreeBSD__ */
+ break;
+ case IPV6_HOPOPTS:
+#if __FreeBSD__
+ optval = (inp->inp_flags & INP_RXHOPOPTS) ? 1 : 0;
+ error = sooptcopyout(sopt, &optval, sizeof(int));
+#else /* __FreeBSD__ */
+ if (!(*mp = m = m_get(M_NOWAIT, MT_SOOPTS))) {
+ error = ENOBUFS;
+ break;
+ };
+ m->m_len = sizeof(int);
+ *mtod(m, int *) = (inp->inp_flags & INP_RXHOPOPTS) ? 1 : 0;
+#endif /* __FreeBSD__ */
+ break;
+ case IPV6_DSTOPTS:
+#if __FreeBSD__
+ optval = (inp->inp_flags & INP_RXDSTOPTS) ? 1 : 0;
+ error = sooptcopyout(sopt, &optval, sizeof(int));
+#else /* __FreeBSD__ */
+ if (!(*mp = m = m_get(M_NOWAIT, MT_SOOPTS))) {
+ error = ENOBUFS;
+ break;
+ };
+ m->m_len = sizeof(int);
+ *mtod(m, int *) = (inp->inp_flags & INP_RXDSTOPTS) ? 1 : 0;
+#endif /* __FreeBSD__ */
+ break;
+ case IPV6_RTHDR:
+#if __FreeBSD__
+ optval = (inp->inp_flags & INP_RXSRCRT) ? 1 : 0;
+ error = sooptcopyout(sopt, &optval, sizeof(int));
+#else /* __FreeBSD__ */
+ if (!(*mp = m = m_get(M_NOWAIT, MT_SOOPTS))) {
+ error = ENOBUFS;
+ break;
+ };
+ m->m_len = sizeof(int);
+ *mtod(m, int *) = (inp->inp_flags & INP_RXSRCRT) ? 1 : 0;
+#endif /* __FreeBSD__ */
+ break;
+ case IPV6_HOPLIMIT:
+#if __FreeBSD__
+ optval = (inp->inp_flags & INP_HOPLIMIT) ? 1 : 0;
+ error = sooptcopyout(sopt, &optval, sizeof(int));
+#else /* __FreeBSD__ */
+ if (!(*mp = m = m_get(M_NOWAIT, MT_SOOPTS))) {
+ error = ENOBUFS;
+ break;
+ };
+ m->m_len = sizeof(int);
+ *mtod(m, int *) = (inp->inp_flags & INP_HOPLIMIT) ? 1 : 0;
+#endif /* __FreeBSD__ */
+ break;
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+
+ return error;
+}
+
+/*----------------------------------------------------------------------
+ * Loops back multicast packets to groups of which I'm a member.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_mloopback(ifp, m, dst)
+ struct ifnet *ifp;
+ register struct mbuf *m;
+ register struct sockaddr_in6 *dst;
+{
+ struct mbuf *copym;
+ register struct ipv6 *header;
+
+#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
+ if (!loifp)
+ return;
+#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+
+ /*
+ * Copy mbuf chain in m, and send to loopback interface.
+ */
+
+ copym=m_copym(m,0,M_COPYALL,M_DONTWAIT);
+ if (copym != NULL)
+ {
+ header=mtod(copym,struct ipv6 *);
+ /* Jumbogram? */
+ header->ipv6_length = htons(header->ipv6_length);
+ /*
+ * Also, there's an issue about address collision. You may want to
+ * check the ipv6 destination (or the dst address) and set the ifp
+ * argument to looutput to be the loopback interface itself iff
+ * it is to a solicited nodes multicast.
+ *
+ * Then again, it may be easier for the soliciting code to burn another
+ * m_flags bit, and look for it on loopback.
+ */
+#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
+ (*loifp->if_output)(ifp, copym, (struct sockaddr *)dst, NULL);
+#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+#if __FreeBSD__
+ loif->if_output(ifp,copym,(struct sockaddr *)dst,NULL);
+#else /* __FreeBSD__ */
+ looutput(ifp,copym,(struct sockaddr *)dst,NULL);
+#endif /* __FreeBSD__ */
+#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+ }
+ else
+ DPRINTF(IDL_GROSS_EVENT,("m_copym() failed in ipv6_mloopback.\n"));
+}
+
+/*----------------------------------------------------------------------
+ * Fragment IPv6 datagram.
+ *
+ * Given a large mbuf chain m, with only its ipv6->ipv6_length field in
+ * host order, fragment into mtu sized chunks, and return a meta-chain
+ * with m->m_nextpkt being the subsequent fragments. If there's a problem,
+ * m_freem all fragments, and return NULL. Also, all ipv6->ipv6_length
+ * fields are in network order, i.e. ready-to-transmit.
+ *
+ * Note that there is an unusually large amount of calls to m_pullup,
+ * m_copym2etc. here. This will cause performance hits.
+ *
+ * A redesign of this is in order, but that will have to wait.
+ ----------------------------------------------------------------------*/
+
+struct mbuf *
+ipv6_fragment(m,mtu)
+ struct mbuf *m;
+ int mtu;
+{
+ uint8_t nextopt = IPPROTO_FRAGMENT;
+ uint divpoint = sizeof(struct ipv6), chunksize, sofar = 0, goal;
+ struct mbuf *retval = NULL, *newfrag = NULL;
+ struct ipv6_fraghdr *frag = NULL;
+
+ outfragid++;
+
+ DPRINTF(IDL_FINISHED,\
+ ("Entering ipv6_fragment, m_pkthdr.len = %d, mtu = %d\n",\
+ m->m_pkthdr.len, mtu));
+
+ /*
+ * Find the dividing point between pre-fragment and post-fragment options.
+ */
+ divpoint = ipv6_finddivpoint(m, &nextopt);
+
+ /*
+ * Options being larger than MTU can happen, especially given large routing
+ * headers and large options bags.
+ */
+ if (divpoint + sizeof(struct ipv6_fraghdr) >= mtu)
+ {
+ DPRINTF(IDL_ERROR,
+ ("ipv6_fragment(): Options are larger than passed-in MTU.\n"));
+ m_freem(m);
+ return NULL;
+ }
+
+#ifdef DIAGNOSTIC
+ if (divpoint & 0x7)
+ panic("divpoint not a multiple of 8!");
+#endif
+
+ /*
+ * sofar keeps track of how much I've fragmented, chunksize is how
+ * much per fragment, goal is how much data to actually fragment.
+ */
+ chunksize = mtu - divpoint - sizeof(struct ipv6_fraghdr);
+ chunksize &= ~7;
+ goal = m->m_pkthdr.len - divpoint;
+
+ DPRINTF(IDL_FINISHED, \
+ ("Found divpoint (%d), nextopt (%d), chunksize(%d) goal(%d)\n",\
+ divpoint, nextopt, chunksize,goal));
+
+ while (sofar < goal)
+ if ((newfrag = m_copym2(m, 0, divpoint, M_DONTWAIT)) != NULL)
+ {
+ struct mbuf *fraghdrmbuf = NULL;
+ struct ipv6 *ipv6 = NULL;
+ uint tocopy = (chunksize <= (goal - sofar))?chunksize:(goal - sofar);
+
+ DPRINTF(IDL_FINISHED,("tocopy == %d\n",tocopy));
+
+ /*
+ * Create a new IPv6 fragment, using the header that was slightly
+ * munged by ipv6_finddivpoint().
+ *
+ * The above m_copym2() creates a copy of the first
+ */
+
+ newfrag->m_nextpkt = retval;
+ retval = newfrag;
+
+ /*
+ * Append IPv6 fragment header to pre-fragment
+ */
+ for(fraghdrmbuf = retval;fraghdrmbuf->m_next != NULL;)
+ fraghdrmbuf = fraghdrmbuf->m_next;
+ MGET(fraghdrmbuf->m_next,M_DONTWAIT,MT_DATA);
+ if (fraghdrmbuf->m_next == NULL)
+ {
+ DPRINTF(IDL_ERROR,("couldn't get new mbuf for frag hdr\n"));
+ ipv6stat.ips_odropped++;
+ goto bail;
+ }
+
+ fraghdrmbuf = fraghdrmbuf->m_next;
+ fraghdrmbuf->m_len = sizeof(struct ipv6_fraghdr);
+ retval->m_pkthdr.len += sizeof(struct ipv6_fraghdr);
+ frag = mtod(fraghdrmbuf,struct ipv6_fraghdr *);
+ frag->frag_nexthdr = nextopt;
+ frag->frag_reserved = 0;
+ frag->frag_bitsoffset = htons(sofar | ((sofar + tocopy) < goal));
+ frag->frag_id = outfragid;
+
+ /*
+ * Copy off (rather than just m_split()) the portion of data that
+ * goes with this fragment.
+ */
+ if ((fraghdrmbuf->m_next = m_copym2(m,divpoint + sofar,tocopy,
+ M_DONTWAIT)) == NULL)
+ {
+ DPRINTF(IDL_ERROR,("couldn't copy segment.\n"));
+ goto bail;
+ }
+ retval->m_pkthdr.len += tocopy;
+
+ /*
+ * Update fragment's IPv6 header appropriately.
+ */
+ ipv6 = mtod(retval,struct ipv6 *);
+ ipv6->ipv6_length = htons(retval->m_pkthdr.len - sizeof(struct ipv6));
+ ipv6->ipv6_nexthdr = IPPROTO_FRAGMENT;
+ ipv6stat.ips_ofragments++;
+ sofar += tocopy;
+ }
+ else
+ {
+ /*
+ * Creation of new fragment failed.
+ */
+ DPRINTF(IDL_ERROR,("m_copym2() failed in fragmentation loop.\n"));
+ ipv6stat.ips_odropped++;
+ bail:
+ DPRINTF(IDL_ERROR,("Bailing out of ipv6_fragment()\n"));
+ while (retval != NULL)
+ {
+ newfrag = retval;
+ retval = retval->m_nextpkt;
+ m_freem(newfrag);
+ }
+ m_freem(m);
+ return NULL;
+ }
+
+ m_freem(m);
+
+ /* Dump mbuf chain list constructed for debugging purposes. */
+ DDO(IDL_FINISHED,\
+ for (newfrag = retval; newfrag != NULL; newfrag = newfrag->m_nextpkt)\
+ dump_mbuf(newfrag) );
+
+ return retval;
+}
+
+/*----------------------------------------------------------------------
+ * Find the dividing point between pre-fragment and post-fragment options.
+ * The argument nexthdr is read/write, on input, it is the next header
+ * value that should be written into the previous header's "next hdr" field,
+ * and what is written back is what used to be in the previous field's
+ * "next hdr" field. For example:
+ *
+ * IP (next hdr = routing) becomes --> IP (next hdr = routing)
+ * Routing (next hdr = TCP) Routing (next hdr = fragment)
+ * TCP TCP
+ * argument nexthdr = fragment argument nexthdr = TCP
+ *
+ * This function returns the length of the pre-fragment options, ideal for
+ * calls to m_split.
+ *
+ * As in ipv6_fragment, too many calls to m_pullup/m_pullup2 are performed
+ * here. Another redesign is called for, but not now.
+ ----------------------------------------------------------------------*/
+
+int
+ipv6_finddivpoint(m, nexthdr)
+ struct mbuf *m;
+ uint8_t *nexthdr;
+{
+ uint8_t iprevopt, *prevopt = &(iprevopt), new = *nexthdr;
+ uint8_t *nextopt;
+ uint divpoint,maybe = 0;
+
+ /*
+ * IPv4 authentication code calls this function too. It is likely that
+ * v4 will just return almost immedately, after determining options
+ * length. (i.e. never go through the while loop.)
+ */
+ if (mtod(m, struct ip *)->ip_v == 4)
+ {
+ iprevopt = IPPROTO_IPV4;
+ nextopt = &(mtod(m, struct ip *)->ip_p);
+ divpoint = sizeof(struct ip);
+ }
+ else
+ {
+ iprevopt = IPPROTO_IPV6;
+ nextopt = &(mtod(m, struct ipv6 *)->ipv6_nexthdr);
+ divpoint = sizeof(struct ipv6);
+ }
+
+ /*
+ * Scan through options finding dividing point. Dividing point
+ * for authentication and fragmentation is the same place.
+ *
+ * Some weirdness here is that there MIGHT be a "Destination options bag"
+ * which is actually a "per source-route-hop" bag. There is a strong
+ * argument for giving this particular options bag a separate type, but
+ * for now, kludge around it.
+ *
+ * The "maybe" variable takes into account the length of this options bag.
+ */
+ while (IS_PREFRAG(*nextopt) && *prevopt != IPPROTO_ROUTING)
+ {
+ struct ipv6_srcroute0 *i6sr;
+ struct ipv6_opthdr *oh;
+
+ /*
+ * ASSUMES: both nextopt and length will be in the first
+ * 8 bytes of ANY pre-fragment header.
+ */
+
+ if ((divpoint + maybe + 8) > MHLEN)
+ {
+ /*
+ * This becomes complicated. Try and collect invariant part into
+ * first (now cluster) mbuf on chain. m_pullup() doesn't work with
+ * clusters, so either write m_pullup2() or inline it here.
+ *
+ * m_pullup2(), unlike m_pullup() will only collect exactly
+ * how many bytes the user requested. This is to avoid problems
+ * with m_copym() and altering data that is merely referenced
+ * multiple times, rather than actually copied. (We may eliminate
+ * the Net/2 hack of adding m_copym2().)
+ */
+ if ((m = m_pullup2(m,divpoint + maybe + 8)) == NULL)
+ {
+ DPRINTF(IDL_ERROR,\
+ ("m_pullup2(%d) failed in ipv6_fragment().\n",\
+ divpoint + maybe + 8));
+ return 0;
+ }
+ }
+ else
+ {
+ if ((m = m_pullup(m,divpoint + maybe + 8)) == NULL)
+ {
+ DPRINTF(IDL_ERROR,\
+ ("m_pullup() failed in ipv6_fragment().\n"));
+ return 0;
+ }
+ }
+
+ /*
+ * Find nextopt, and advance accordingly.
+ */
+ switch (*nextopt)
+ {
+ case IPPROTO_HOPOPTS:
+ /*
+ * Hop-by-hops should be right after IPv6 hdr. If extra is nonzero,
+ * then there was a destination options bag. If divpoint is not
+ * only the size of the IPv6 header, then something came before
+ * hop-by-hop options. This is not good.
+ */
+ if (maybe || divpoint != sizeof(struct ipv6))
+ {
+ DPRINTF(IDL_ERROR,
+ ("ipv6_input(): Weird ordering in headers.\n"));
+ m_freem(m);
+ return 0;
+ }
+ oh = (struct ipv6_opthdr *)(m->m_data + divpoint);
+ prevopt = nextopt;
+ nextopt = &(oh->oh_nexthdr);
+ divpoint += 8 + (oh->oh_extlen << 3);
+ if (oh->oh_extlen)
+ if (divpoint > MHLEN)
+ {
+ if ((m = m_pullup2(m,divpoint)) == NULL)
+ {
+ DPRINTF(IDL_EVENT,\
+ ("m_pullup2(%d) failed in IPPROTO_HOPOPTS nextopt.\n",\
+ divpoint));
+ return 0;
+ }
+ }
+ else
+ {
+ if ((m = m_pullup(m,divpoint)) == NULL)
+ {
+ DPRINTF(IDL_EVENT,\
+ ("m_pullup() failed in IPPROTO_HOPOPTS nextopt.\n"));
+ return 0;
+ }
+ }
+ break;
+ case IPPROTO_DSTOPTS:
+ oh = (struct ipv6_opthdr *)(m->m_data + divpoint);
+ prevopt = nextopt;
+ nextopt = &(oh->oh_nexthdr);
+ maybe = 8 + (oh->oh_extlen << 3);
+ if (oh->oh_extlen)
+ if ( divpoint + maybe > MHLEN)
+ {
+ if ((m = m_pullup2(m,divpoint + maybe)) == NULL)
+ {
+ DPRINTF(IDL_EVENT,\
+ ("m_pullup2(%d) failed in IPPROTO_DSTOPTS nextopt.\n",\
+ divpoint+maybe));
+ return 0;
+ }
+ }
+ else
+ {
+ if ((m = m_pullup(m,divpoint + maybe)) == NULL)
+ {
+ DPRINTF(IDL_EVENT,\
+ ("m_pullup() failed in IPPROTO_DSTOPTS nextopt.\n"));
+ return 0;
+ }
+ }
+ break;
+ case IPPROTO_ROUTING:
+ if (maybe) /* i.e. if we had a destination options bag before
+ this routing header, we should advance the dividing
+ point. */
+ divpoint += maybe;
+ maybe = 0;
+ i6sr = (struct ipv6_srcroute0 *)(m->m_data + divpoint);
+ prevopt = nextopt;
+ nextopt = &(i6sr->i6sr_nexthdr);
+ switch (i6sr->i6sr_type)
+ {
+ case 0:
+ divpoint += 8 + (i6sr->i6sr_len * 8);
+ break;
+ default:
+ DPRINTF(IDL_ERROR,
+ ("ipv6_input(): Unknown outbound routing header.\n"));
+ break;
+ }
+ if (divpoint > MHLEN)
+ {
+ if ((m = m_pullup2(m,divpoint)) == NULL)
+ {
+ DPRINTF(IDL_EVENT,\
+ ("m_pullup2(%d) failed in IPPROTO_ROUTING nextopt.\n",\
+ divpoint));
+ return 0;
+ }
+ }
+ else
+ {
+ if ((m = m_pullup(m,divpoint)) == NULL)
+ {
+ DPRINTF(IDL_EVENT,\
+ ("m_pullup() failed in IPPROTO_ROUTING nextopt.\n"));
+ return 0;
+ }
+ }
+ break;
+ } /* End of switch statement. */
+ }; /* End of while loop. */
+ *nexthdr = *nextopt;
+ *nextopt = new;
+ return divpoint;
+}
+
+int ipv6_controltoheader(struct mbuf **m, struct mbuf *control, struct ifnet **forceifp, int *payload)
+{
+ struct cmsghdr *cmsghdr;
+ int error = EINVAL;
+ struct mbuf *srcrtm = NULL;
+
+ DPRINTF(IDL_EVENT, ("ipv6_controltoheader(m=%08lx, control=%08lx, forceif=%08lx, payload=%08lx)\n", (unsigned long)m, (unsigned long)control, (unsigned long)forceifp, (unsigned long)payload));
+ DDO(IDL_EVENT, dump_mchain(control));
+
+ while((control = m_pullup2(control, sizeof(struct cmsghdr))) &&
+ (cmsghdr = mtod(control, struct cmsghdr *)) &&
+ (control = m_pullup2(control, cmsghdr->cmsg_len))) {
+ cmsghdr = mtod(control, struct cmsghdr *);
+ switch(cmsghdr->cmsg_level) {
+ case IPPROTO_IPV6:
+ switch(cmsghdr->cmsg_type) {
+ case IPV6_PKTINFO:
+ {
+ struct in6_pktinfo in6_pktinfo;
+ struct in6_ifnet *i6ifp;
+ struct ifaddr *ifa;
+
+ if (cmsghdr->cmsg_len != sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo))
+ goto ret;
+
+ bcopy((caddr_t)cmsghdr + sizeof(struct cmsghdr), &in6_pktinfo, sizeof(struct in6_pktinfo));
+
+ if (!in6_pktinfo.ipi6_ifindex) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&in6_pktinfo.ipi6_addr)) {
+ DPRINTF(IDL_EVENT, ("ipv6_controltoheader: in degenerate IPV6_PKTINFO case\n"));
+ break;
+ } else {
+ struct in6_ifaddr *i6a;
+ DPRINTF(IDL_EVENT, ("ipv6_controltoheader: in index = unspec, addr = spec case\n"));
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ if (IN6_ARE_ADDR_EQUAL(&I6A_SIN(i6a)->sin6_addr, &in6_pktinfo.ipi6_addr))
+ goto l2;
+ goto ret;
+ };
+ };
+
+ DPRINTF(FINISHED, ("ipv6_controltoheader: in index = spec case\n"));
+
+ for (i6ifp = in6_ifnet; i6ifp && (i6ifp->i6ifp_ifp->if_index != in6_pktinfo.ipi6_ifindex); i6ifp = i6ifp->i6ifp_next);
+
+ if (!i6ifp)
+ goto ret;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&in6_pktinfo.ipi6_addr)) {
+ DPRINTF(FINISHED, ("ipv6_controltoheader: in index = spec, addr = unspec case\n"));
+ goto l1;
+ };
+
+ DPRINTF(FINISHED, ("ipv6_controltoheader: in index = spec, addr = spec case\n"));
+
+#ifdef __FreeBSD__
+ for (ifa = i6ifp->i6ifp_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next) {
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (ifa = i6ifp->i6ifp_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) {
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (ifa = i6ifp->i6ifp_ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ if ((ifa->ifa_addr->sa_family == AF_INET6) && !bcmp(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, &in6_pktinfo.ipi6_addr, sizeof(struct in6_addr)))
+ goto l1;
+ };
+ goto ret;
+
+l1: *forceifp = i6ifp->i6ifp_ifp;
+l2: bcopy(&in6_pktinfo.ipi6_addr, &(mtod(*m, struct ipv6 *)->ipv6_src), sizeof(struct in6_addr));
+ };
+ break;
+ case IPV6_HOPLIMIT:
+ {
+ int i;
+
+ if (cmsghdr->cmsg_len != sizeof(struct cmsghdr) + sizeof(int))
+ goto ret;
+
+ i = *((int *)((caddr_t)cmsghdr + sizeof(struct cmsghdr)));
+
+ if (i == -1)
+ if (IN6_IS_ADDR_MULTICAST(&mtod(*m, struct ipv6 *)->ipv6_dst))
+ i = IPV6_DEFAULT_MCAST_HOPS;
+ else
+ i = ipv6_defhoplmt;
+
+ if ((i < 0) || (i > 255))
+ goto ret;
+
+ mtod(*m, struct ipv6 *)->ipv6_hoplimit = i;
+ };
+ break;
+ default:
+ goto ret;
+ };
+ break;
+ case IPPROTO_ROUTING:
+ MGET(srcrtm, M_DONTWAIT, MT_DATA);
+ if (!srcrtm) {
+ error = ENOBUFS;
+ goto ret;
+ };
+
+ srcrtm->m_len = cmsghdr->cmsg_len - sizeof(struct cmsghdr) + 3;
+ if (srcrtm->m_len > MLEN) {
+ DPRINTF(IDL_ERROR, ("ipv6_controltoheader: requested source route that we can't fit in an mbuf (length %d)\n", srcrtm->m_len));
+ goto ret;
+ };
+
+ if (srcrtm->m_len & 7) {
+ DPRINTF(IDL_ERROR, ("ipv6_controltoheader: requested source route has an invalid length; %d needs to be a multiple of eight bytes\n", srcrtm->m_len));
+ goto ret;
+ };
+
+ *(mtod(srcrtm, uint8_t *) + 1) = (srcrtm->m_len >> 3) - 1;
+ *(mtod(srcrtm, uint8_t *) + 2) = cmsghdr->cmsg_type;
+ bcopy((caddr_t *)cmsghdr + sizeof(struct cmsghdr), mtod(srcrtm, uint8_t *) + 3, cmsghdr->cmsg_len - sizeof(struct cmsghdr));
+ break;
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ /* XXX */
+ goto ret;
+ default:
+ goto ret;
+ };
+ m_adj(control, cmsghdr->cmsg_len);
+ if (!control->m_len)
+ goto finish;
+ };
+
+ DPRINTF(IDL_ERROR, ("ipv6_controltoheader: pullups failed\n"));
+ goto ret;
+
+finish:
+ if (srcrtm) {
+ struct mbuf *m2;
+ DPRINTF(IDL_EVENT, ("ipv6_controltoheader: in srcrtm case\n"));
+ if (!(m2 = m_split(*m, sizeof(struct ipv6), M_DONTWAIT)))
+ goto ret;
+ (*m)->m_next = srcrtm;
+ srcrtm->m_next = m2;
+ *mtod(srcrtm, uint8_t *) = mtod(*m, struct ipv6 *)->ipv6_nexthdr;
+ mtod(*m, struct ipv6 *)->ipv6_nexthdr = IPPROTO_ROUTING;
+
+ *payload += srcrtm->m_len;
+ (*m)->m_pkthdr.len += srcrtm->m_len;
+ };
+ m_freem(control);
+ DPRINTF(IDL_FINISHED, ("ipv6_controltoheader: returning\n"));
+ return 0;
+
+ret:
+ DPRINTF(IDL_ERROR, ("ipv6_controltoheader: returning error %d\n", error));
+ if (srcrtm)
+ m_free(srcrtm);
+ if (control)
+ m_freem(control);
+ return error;
+};
diff --git a/sys/netinet6/ipv6_rtrequest.c b/sys/netinet6/ipv6_rtrequest.c
new file mode 100644
index 00000000000..7e0bb20e27a
--- /dev/null
+++ b/sys/netinet6/ipv6_rtrequest.c
@@ -0,0 +1,1005 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+/*#include <sys/ioctl.h>*/
+#include <sys/syslog.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_systm.h>
+#include <netinet/ip.h>
+
+#include <netinet6/in6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ipv6.h>
+#include <netinet6/ipv6_var.h>
+#include <netinet6/ipv6_icmp.h>
+
+#include <sys/debug.h>
+
+/*
+ * Globals (and forward function declarations).
+ */
+
+/*
+ * External globals.
+ */
+extern struct sockaddr_in6 in6_allones;
+extern struct discq dqhead;
+extern struct v6router defrtr;
+extern struct v6router nondefrtr;
+
+extern struct in6_ifaddr *in6_ifaddr;
+
+extern int ipv6forwarding;
+
+/*
+ * External function prototypes.
+ */
+
+/*
+ * General notes:
+ *
+ * These functions are mainly called from ipv6_discovery.c but are
+ * fairly generic and generally useful and so live in their own file.
+ * danmcd rja
+ */
+void tunnel_parent __P((struct rtentry *));
+struct v6router *ipv6_add_defrouter_rtrequest(struct rtentry *);
+void tunnel_child __P((struct rtentry *));
+void ipv6_nsolicit __P((struct ifnet *, struct mbuf *, struct rtentry *));
+void tunnel_parent_clean __P((struct rtentry *));
+int ipv6_delete_defrouter __P((struct v6router *));
+void tunnel_child_clean __P((struct rtentry *));
+
+static int add_defchild __P((struct rtentry *));
+static void add_netchild __P((struct rtentry *));
+void ipv6_setrtifa __P((struct rtentry *));
+static void add_non_default __P((struct rtentry *));
+
+/*
+ * Functions and macros.
+ */
+
+
+/*----------------------------------------------------------------------
+ * add_defchild():
+ * Find the best default router out of our list and use it
+ * for this destination route.
+ ----------------------------------------------------------------------*/
+static int
+add_defchild(rt)
+ struct rtentry *rt;
+{
+ struct v6router *v6r = defrtr.v6r_next;
+ struct sockaddr_in6 *dst;
+
+ /*
+ * What if this child turns out to be a tunneling route? OUCH!
+ *
+ * One other thing: If rt_key(rt) is link-local, but it hit from
+ * the default router, then perhaps it should
+ * go with the ipv6_onlink_query() route.
+ */
+
+ dst = (struct sockaddr_in6 *)rt_key(rt);
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr))
+ /*
+ * Since there are no priorities, just pick one. For now, I guess I'll
+ * Just pick v6r_next.
+ */
+ if (v6r != &defrtr)
+ {
+ struct v6child *v6c;
+ int rc = 0;
+
+ DPRINTF(IDL_EVENT,("About to do rt_setgate. rt before is:\n"));
+ DDO(IDL_EVENT,dump_rtentry(rt));
+ DPRINTF(IDL_EVENT,("Args are (dst, parent->rt_gateway, v6r_rt):\n"));
+ DDO(IDL_EVENT,dump_smart_sockaddr((struct sockaddr *)dst);\
+ dump_smart_sockaddr(rt->rt_parent->rt_gateway);\
+ dump_rtentry(v6r->v6r_rt));
+
+ if (rt_key(v6r->v6r_rt)->sa_family != AF_INET6)
+ {
+ /*
+ * Hmmm, we must be a tunneling default route. Current
+ * conventional wisdom is that we can only see one in our
+ * "default router list," and that it was added manually. Using
+ * that assumption, get things right, including chasing the
+ * parent and using its rt_gateway sockaddr for rt_setgate.
+ */
+ rt_setgate(rt,(struct sockaddr *)dst,rt->rt_parent->rt_gateway);
+ rc = 1;
+ }
+ else
+ {
+ rt_setgate(rt,(struct sockaddr *)dst,rt_key(v6r->v6r_rt));
+ rt->rt_flags &= ~RTF_TUNNEL; /* In case cloned off an initially
+ tunnelling default route. */
+ }
+
+ DDO(IDL_GROSS_EVENT,\
+ printf("After rt_setgate, rt is:\n");dump_rtentry(rt));
+
+ rt->rt_llinfo = malloc(sizeof(*v6c),M_DISCQ,M_NOWAIT);
+ if (rt->rt_llinfo == NULL)
+ {
+ DPRINTF(IDL_ERROR, ("add_defchild(): malloc failed.\n"));
+ /* Perhaps route should be freed here */
+ }
+ else
+ {
+ v6c = (struct v6child *)rt->rt_llinfo;
+ insque(v6c,&v6r->v6r_children);
+ v6c->v6c_route = rt;
+ }
+ return rc;
+ }
+ else
+ {
+ /*
+ * Default route hit, but no default router children.
+ *
+ * Perhaps delete it?
+ */
+ DPRINTF(IDL_ERROR,\
+ ("Default route hit, but no default routers in list.\n"));
+ }
+
+ /* Either on-link or in trouble. Needs to be coded. */
+ DPRINTF(IDL_ERROR, ("On-link or in trouble in ipv6_rtrequest\n"));
+
+ /*
+ * Convert off-link entry to indeterminate on-link entry.
+ * Subsequent code after a call to this function will do the right thing.
+ * (ipv6_rtrequest code to handle new on-link neighbors follows this.)
+ */
+ rt->rt_flags = RTF_UP|RTF_LLINFO|RTF_HOST;
+ rt->rt_llinfo = NULL;
+ rt->rt_ifa->ifa_refcnt--;
+ rt->rt_ifa = NULL;
+ rt->rt_ifp = NULL;
+ bzero(rt->rt_gateway,rt->rt_gateway->sa_len);
+ rt->rt_gateway->sa_family = AF_LINK;
+ rt->rt_gateway->sa_len = sizeof(struct sockaddr_dl);
+ rt->rt_rmx.rmx_mtu = 0;
+
+ /*
+ * QUESTION: If I do this, should I call ipv6_nsolicit() to complete
+ * this conversion from default route child to non-deterministic
+ * on-link neighbor?
+ *
+ * ANSWER: Yes, but not here. Just return 1, and let later code take
+ * care of the nsolicit.
+ */
+ return 1;
+}
+
+/*----------------------------------------------------------------------
+ * add_netchild():
+ * Given a network route child (i.e. non-default),
+ * put this child in the parent's descendant list.
+ ----------------------------------------------------------------------*/
+
+static void
+add_netchild(rt)
+ struct rtentry *rt;
+{
+ struct v6router *v6r;
+ struct v6child *v6c;
+
+ if (rt->rt_parent == NULL)
+ {
+ DPRINTF(IDL_ERROR,
+ ("add_netchild: No parent route. (Must be manually added.)\n"));
+ return;
+ }
+ v6r = (struct v6router *)rt->rt_parent->rt_llinfo;
+
+ v6c = malloc(sizeof(*v6c),M_DISCQ,M_NOWAIT);
+ if (v6c == NULL)
+ {
+ DPRINTF(IDL_ERROR, ("add_netchild(): malloc failed.\n"));
+ /* Perhaps should free route here */
+ return;
+ }
+ insque(v6c,v6r->v6r_children.v6c_next);
+ v6c->v6c_route = rt;
+ v6c->v6c_parent = v6r;
+ rt->rt_llinfo = (caddr_t)v6c;
+}
+
+/*----------------------------------------------------------------------
+ * add_non_default():
+ * add a non-default routing entry.
+ *
+ ----------------------------------------------------------------------*/
+static void
+add_non_default(rt)
+ struct rtentry *rt;
+{
+ struct v6router *newbie;
+
+ if (rt_key(rt)->sa_family != AF_INET6)
+ {
+ DPRINTF(IDL_ERROR,
+ ("IPv6 off-net non-tunnel route w/o IPv6 gwaddr.\n"));
+ return;
+ }
+
+ newbie = malloc(sizeof(*newbie),M_DISCQ,M_NOWAIT);
+ if (newbie == NULL)
+ {
+ DPRINTF(IDL_ERROR, ("add_non_default(): malloc failed.\n"));
+ /* Should probably free route */
+ return;
+ }
+ bzero(newbie,sizeof(*newbie));
+ newbie->v6r_children.v6c_next = newbie->v6r_children.v6c_prev =
+ &newbie->v6r_children;
+ insque(newbie,&nondefrtr);
+
+ /*
+ * On creation of rt, rt_setgate() was called, therefore we take on blind
+ * faith that an appropriate neighbor cache entry was created. If not,
+ * we're in deep trouble.
+ */
+ newbie->v6r_rt = rt->rt_gwroute;
+ rt->rt_llinfo = (caddr_t)newbie;
+}
+
+
+/*----------------------------------------------------------------------
+ * ipv6_setrtifa():
+ * Set route's interface address. Source address selection for
+ * a route.
+ *
+ ----------------------------------------------------------------------*/
+void
+ipv6_setrtifa(rt)
+ struct rtentry *rt;
+{
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rt_key(rt);
+ struct sockaddr_in6 *src = (struct sockaddr_in6 *)rt->rt_ifa->ifa_addr;
+ struct in6_ifaddr *src_in6ifa = (struct in6_ifaddr *)rt->rt_ifa;
+ struct sockaddr_in6 *mask = (struct sockaddr_in6 *)rt_mask(rt);
+ struct ifaddr *ifa = NULL;
+
+ DPRINTF(IDL_EVENT,("Entering ipv6_setrtifa.\n"));
+
+ /*
+ * If I can't find a "better" source address, stick with the one I got.
+ *
+ * ASSUMES: rt_ifp is set PROPERLY. This NEEDS to be true.
+ */
+
+ /*
+ * If link-local, use link-local source.
+ */
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+ {
+ if (IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr))
+ return;
+#if __FreeBSD__
+ for (ifa = rt->rt_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (ifa = rt->rt_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ {
+ struct sockaddr_in6 *current = (struct sockaddr_in6 *)ifa->ifa_addr;
+
+ if ((current->sin6_family != AF_INET6) ||
+ IS_DEPRECATED((struct in6_ifaddr *)ifa))
+ continue; /* For loop. */
+ if (IN6_IS_ADDR_LINKLOCAL(&current->sin6_addr))
+ break; /* For loop. */
+ }
+ if (ifa == NULL)
+ return; /* We're in real trouble. */
+ }
+
+ /*
+ * If v4-compatible, use v4-compatible source address.
+ */
+ if (ifa == NULL && (sin6->sin6_addr.in6a_words[0] == 0 &&
+ sin6->sin6_addr.in6a_words[1] == 0 &&
+ sin6->sin6_addr.in6a_words[2] == 0 &&
+ sin6->sin6_addr.in6a_words[3] != htonl(1) &&
+ (mask == NULL || mask->sin6_len >=
+ sizeof(*mask) - sizeof(struct in_addr))))
+ {
+ if (IN6_IS_ADDR_V4COMPAT(&src->sin6_addr) && !IS_DEPRECATED(src_in6ifa))
+ return;
+#if __FreeBSD__
+ for (ifa = rt->rt_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (ifa = rt->rt_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ {
+ struct sockaddr_in6 *current = (struct sockaddr_in6 *)ifa->ifa_addr;
+
+ if ((current->sin6_family != AF_INET6)
+ || IS_DEPRECATED((struct in6_ifaddr *)ifa))
+ continue; /* For loop. */
+ if (IN6_IS_ADDR_V4COMPAT(&current->sin6_addr))
+ break; /* For loop. */
+ }
+ /*if (ifa == NULL)
+ return;*/ /* if ifa == NULL here, pretend it's global, because
+ it is global! */
+ }
+
+ /*
+ * If site-local, use a site-local source address.
+ */
+
+ if (ifa == NULL && IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))
+ {
+ if (IN6_IS_ADDR_SITELOCAL(&src->sin6_addr) && !IS_DEPRECATED(src_in6ifa))
+ return;
+#ifdef __FreeBSD__
+ for (ifa = rt->rt_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (ifa = rt->rt_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ {
+ struct sockaddr_in6 *current = (struct sockaddr_in6 *)ifa->ifa_addr;
+
+ if ((current->sin6_family != AF_INET6)
+ || IS_DEPRECATED((struct in6_ifaddr *)ifa))
+ continue; /* For loop. */
+ if (IN6_IS_ADDR_SITELOCAL(&current->sin6_addr))
+ break; /* For loop. */
+ }
+ if (ifa == NULL)
+ return; /* We don't want to potentially pollute the global
+ internet with site-local traffic. If you feel
+ differently, comment out this ifa == NULL check
+ and fallthrough. */
+ }
+
+ if (ifa == NULL)
+ {
+ /*
+ * At this point, the address _could_ be anything, but is most likely
+ * a globally routable address that didn't fit into the previous
+ * categories.
+ *
+ * Do gyrations iff rt->rt_ifa's address is link-local and the dest
+ * isn't or the src address previously picked is deprecated.
+ */
+ DPRINTF(IDL_GROSS_EVENT,("In default case of ipv6_setrtifa().\n"));
+ if ((!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) &&
+ IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr)) || IS_DEPRECATED(src_in6ifa))
+ {
+ struct ifaddr *ifa_compat = 0, *ifa_site = 0;
+
+ /*
+ * For now, pick a non-link-local address using the following
+ * order of preference: global, compatible, site-local.
+ *
+ */
+#ifdef __FreeBSD__
+ for (ifa = rt->rt_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (ifa = rt->rt_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ if (ifa->ifa_addr->sa_family == AF_INET6 &&
+ !IS_DEPRECATED((struct in6_ifaddr *)ifa) &&
+ !IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr))
+ if (IN6_IS_ADDR_V4COMPAT(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr))
+ ifa_compat = ifa;
+ else if (IN6_IS_ADDR_SITELOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr))
+ ifa_site = ifa;
+ else /* globally routable address */
+ break;
+ if (!ifa)
+ ifa = ifa_compat ? ifa_compat : ifa_site;
+ }
+ }
+
+ if (ifa != NULL)
+ {
+ /*
+ * Q: Do I call ipv6_rtrequest (through ifa_rtrequest) if I
+ * change ifa's on a route?
+ */
+ /*if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest)
+ rt->rt_ifa->ifa_rtrequest(RTM_DELETE,rt,rt->rt_gateway);*/
+ IFAFREE(rt->rt_ifa);
+ ifa->ifa_refcnt++;
+ rt->rt_ifa = ifa;
+ rt->rt_ifp = ifa->ifa_ifp; /* Is this desirable? */
+ /*if (ifa->ifa_rtrequest)
+ ifa->ifa_rtrequest(RTM_ADD, rt, rt->rt_gateway);*/
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ * ipv6_rtrequest():
+ * IPv6-specific route ADD, RESOLVE, and DELETE function.
+ * Typically called from route.c's rtrequest() function to handle
+ * the IPv6 unique things.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_rtrequest(req, rt, sa)
+ int req;
+ register struct rtentry *rt;
+ struct sockaddr *sa; /* Ignored parameter, I believe. */
+{
+ static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}, *sdlp;
+ int spray = 0;
+
+ DPRINTF(IDL_EVENT,("ipv6_rtrequest():0000-Starting.\n"));
+ DPRINTF(IDL_EVENT,("req = %d, rt = 0x%lx, sa = 0x%lx\n",req,\
+ (unsigned long)rt, (unsigned long)sa));
+ DDO(IDL_EVENT,dump_rtentry(rt));
+
+ switch (req)
+ {
+ case RTM_ADD:
+ DPRINTF(IDL_EVENT,("ipv6_rtrequest():0100-case RTM_ADD:.\n"));
+
+ /*
+ * Set route Path MTU if not already set on brand new route.
+ */
+ if (rt->rt_rmx.rmx_mtu == 0)
+ rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
+
+ /*
+ * An explicitly added "all-ones" mask or no mask at all should be
+ * a host route.
+ */
+
+ if (rt_mask(rt) == NULL ||
+ (rt_mask(rt)->sa_len == sizeof(struct sockaddr_in6) &&
+ IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)rt_mask(rt))->sin6_addr,
+ &in6_allones.sin6_addr))
+ )
+ rt->rt_flags |= RTF_HOST;
+
+ /*
+ * Explicitly add the cloning bit to the route entry.
+ * (Do we still want to do this?)
+ *
+ * One fallout from this is that if someone wants to disable cloning,
+ * the disabling will have to be explicitly done after adding the route.
+ */
+ if ((rt->rt_flags & RTF_HOST) == 0)
+ {
+ /*
+ * All non-host routes that aren't m-cast are cloning!
+ */
+ if (!IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr))
+ rt->rt_flags |= RTF_CLONING;
+
+ if (rt->rt_flags & RTF_TUNNEL)
+ {
+ DPRINTF(IDL_EVENT,("ipv6_rtrequest():0150-Calling tunnel_parent().\n"));
+ tunnel_parent(rt);
+ }
+
+ if (rt->rt_flags & RTF_GATEWAY)
+ if (rt_mask(rt)->sa_len == 0)
+ {
+ if (rt->rt_flags & RTF_TUNNEL)
+ {
+ /*
+ * Add tunnel to default router list.
+ */
+ ipv6_add_defrouter_rtrequest(rt);
+ }
+ else
+ {
+ struct v6router *v6r;
+ /*
+ * The "default" route has just been added.
+ * Do default router stuff here.
+ *
+ * Search router list to see if I'm already there.
+ * this avoids double-adding.
+ */
+
+ for (v6r = defrtr.v6r_next; v6r != &defrtr;
+ v6r = v6r->v6r_next)
+ if (v6r->v6r_rt == rt->rt_gwroute)
+ break;
+
+ if (v6r == &defrtr)
+ {
+ DPRINTF(IDL_ERROR,
+ ("Calling ipv6_add_defrouter from RTM_ADD.\n"));
+ ipv6_add_defrouter_rtrequest(rt);
+ }
+
+ rt->rt_gwroute->rt_flags |= RTF_ISAROUTER;
+ }
+ rt->rt_flags |= RTF_DEFAULT;
+ }
+ else
+ if (rt->rt_flags & RTF_TUNNEL)
+ {
+ /*
+ * Perhaps if the tunnel bit is already set here, don't
+ * do anything.
+ */
+ }
+ else
+ {
+ /*
+ * A non-default network route has just been added.
+ * Do non-default router stuff here.
+ */
+ add_non_default(rt);
+ }
+ else if (!(rt->rt_flags & RTF_TUNNEL))
+ {
+ /*
+ * Interface route (i.e. on-link non-host prefix).
+ */
+ DPRINTF(IDL_EVENT,("Setting up i/f route.\n"));
+ rt_setgate(rt, rt_key(rt), (struct sockaddr *)&null_sdl);
+ sdlp = (struct sockaddr_dl *)rt->rt_gateway;
+ sdlp->sdl_type = rt->rt_ifp->if_type;
+ sdlp->sdl_index = rt->rt_ifp->if_index;
+ }
+ }
+
+ DPRINTF(IDL_EVENT,("ipv6_rtrequest():0199-Falling out of RTM_ADD:.\n"));
+ /* FALLTHROUGH... */
+ case RTM_RESOLVE:
+ {
+#ifdef __FreeBSD__
+ extern struct ifnet loif[];
+ struct ifnet *loifp = loif;
+#else /* __FreeBSD__ */
+#if (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802))
+ extern struct ifnet loif;
+#else /* (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)) */
+ extern struct ifnet *loifp;
+#endif /* (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)) */
+#endif /* __FreeBSD__ */
+
+ DPRINTF(IDL_EVENT,("ipv6_rtrequest():0200-Now in case RTM_RESOLVE:.\n"));
+
+ /*
+ * First, check if ifa addr is same as route addr,
+ * If yes, then wire the loopback interface as the destination.
+ */
+ if ((rt->rt_flags & RTF_HOST) && rt->rt_ifa &&
+ IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
+ &I6A_SIN(rt->rt_ifa)->sin6_addr)
+#if (defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802))
+ && loifp
+#endif /* (defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)) */
+ )
+ {
+ /* Change ifp to loopback and gateway to itself. */
+#ifdef __FreeBSD__
+ rt->rt_ifp = loifp;
+ rt->rt_gateway = rt_key(rt);
+ rt->rt_rmx.rmx_mtu = loifp->if_mtu;
+#else /* __FreeBSD__ */
+#if (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802))
+ rt->rt_ifp = &loif;
+ rt->rt_gateway = rt_key(rt);
+ rt->rt_rmx.rmx_mtu = loif.if_mtu;
+#else /* (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)) */
+ rt->rt_ifp = loifp;
+ rt->rt_gateway = rt_key(rt);
+ rt->rt_rmx.rmx_mtu = loifp->if_mtu;
+#endif /* (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)) */
+#endif /* __FreeBSD__ */
+
+ if (rt->rt_parent)
+ rt->rt_flags &= ~(RTF_STATIC | RTF_TUNNEL);
+
+ break;
+ };
+ }
+
+ /*
+ * Second, check the accuracy of the route's interface address (ifa).
+ * If possible, update the route's ifa such that it is the "best"
+ * source address for packets bound for this destination.
+ *
+ * This is done to any routes and is subtle enough
+ * to merit its own separate function:
+ */
+
+ ipv6_setrtifa(rt);
+
+ if (!(rt->rt_flags & RTF_HOST))
+ {
+ /*
+ * This should be the "none of the above" case. For now,
+ * do nothing and just return.
+ */
+ DPRINTF(IDL_GROSS_EVENT,("In RESOLVE/lateADD, but not RTF_HOST.\n"));
+ break;
+ }
+
+ /*
+ * At this point, I know (1) I'm a host route which has either been
+ * added manually or (2) I have cloned off to become either:
+ *
+ * a tunnelling route child,
+ * RTF_TUNNEL is set on this entry.
+ * I should be okay, although tunnel state may become necessary.
+ * I'm a next-hop cache entry.
+ *
+ * a default router child,
+ * a non-default network route child,
+ * RTF_GATEWAY is set on this entry.
+ * I have to insert this child into the router's children list.
+ * My child entry hangs off rt_llinfo. I'm a next-hop cache
+ * entry.
+ *
+ * an interface (i.e. neighbor) route child,
+ * a link-local (i.e. off link-local mask) route child
+ * rt->rt_gateway is AF_LINK
+ * I should set RTF_LLINFO, and set up a discq entry because I'm
+ * a neighbor cache entry. (Neighbor caches double as next-hop
+ * cache entries.)
+ */
+
+ if (rt->rt_gateway == NULL)
+ panic("ipv6_rtrequest(): No rt_gateway at the RTM_RESOLVE code.\n");
+
+ /*
+ * Order of bit checking is very important. I check TUNNEL first,
+ * because it's REALLY special. Then I check GATEWAY or not.
+ */
+
+ if (rt->rt_parent != NULL && rt_mask(rt->rt_parent)->sa_len == 0)
+ {
+ DPRINTF(IDL_EVENT,("Cloning off default route!\n"));
+ /*
+ * Find a default router out of the list, and assign it to this
+ * child. Clear or add tunnel bits if necessary.
+ */
+
+ rt->rt_flags &= ~RTF_STATIC;
+ /*
+ * If add_defchild returns 0, then either it's a tunneling
+ * default route, or a link-local converted to a neighbor entry.
+ *
+ * Set 'spray' to the result of add_defchild, because if it is
+ * a neighbor entry (see AF_LINK case below), then spray will
+ * indicate the need to do some pre-emptive neighbor adverts.
+ */
+ if (!(spray = add_defchild(rt))) /* If an actual off-net default
+ route... */
+ break;
+DPRINTF(IDL_ERROR,("add_defchild returned 1, either on-link or tunnel.\n"));
+ /* Otherwise, either tunnel or converted into LINK address. */
+ }
+ else rt->rt_llinfo = NULL; /* Is this already done? */
+
+ if (rt->rt_flags & RTF_TUNNEL)
+ {
+ DPRINTF(IDL_EVENT,("ipv6_rtrequest():0250-Calling tunnel_child().\n"));
+ tunnel_child(rt); /* Be careful if manually added RTF_TUNNEL. */
+ break; /* We're done with this Tunnel host route. */
+ }
+
+ if (rt->rt_flags & RTF_GATEWAY)
+ {
+ add_netchild(rt);
+ if (req == RTM_RESOLVE)
+ rt->rt_flags &= ~RTF_STATIC;
+ break;
+ }
+
+ if (rt->rt_gateway->sa_family == AF_LINK)
+ {
+ /*
+ * I may enter here even after RTF_GATEWAY check because
+ * get_defrouter() may convert the route entry into an on-link one!
+ */
+ struct discq *dq;
+
+ rt->rt_llinfo = malloc(sizeof(*dq),M_DISCQ,M_NOWAIT);
+ dq = (struct discq *)rt->rt_llinfo;
+ if (dq == NULL)
+ {
+ DPRINTF(IDL_ERROR,
+ ("ipv6_rtrequest(): malloc for rt_llinfo failed.\n"));
+ /* Probably should free route */
+ break;
+ }
+ bzero(dq,sizeof(*dq));
+ dq->dq_rt = rt;
+ dq->dq_unanswered = -1;
+ rt->rt_flags |= RTF_LLINFO;
+ insque(dq,&dqhead);
+ /*
+ * State is already INCOMPLETE, because of link stuff.
+ *
+ * If this neigbor entry is caused by add_defchild (i.e. link-local)
+ * spray nsolicit out all interfaces.
+ */
+
+ if (spray)
+ ipv6_nsolicit(NULL,NULL,rt);
+
+ break;
+ }
+ else
+#if 0
+ /*
+ * support for PPP goes here.
+ * Frame Relay, and ATM will probably probably be handled by the
+ * AF_LINK case. Other AF_* will be covered later.
+ */
+ panic("ipv6_rtrequest: Not tunnel, not off-net, and not neighbor.");
+#else /* 0 */
+ {
+ DPRINTF(IDL_ERROR, ("ipv6_rtrequest: Not tunnel, not off-net, and not neighbor. rt is:\n"));
+ DDO(IDL_ERROR, dump_rtentry(rt));
+ }
+#endif /* 0 */
+
+ break;
+ case RTM_DELETE:
+ DPRINTF(IDL_GROSS_EVENT,("ipv6_rtrequest():0300-Now in case RTM_DELETE.\n"));
+ /*
+ * The FLUSH call ('route flush...') checks the ifp, perhaps I should
+ * fill that just in case.
+ */
+
+ if (rt->rt_ifp == NULL)
+ rt->rt_ifp = rt->rt_ifa->ifa_ifp;
+
+ if ((rt->rt_flags & RTF_HOST) == 0)
+ {
+ /*
+ * Clean up after network routes.
+ */
+ if (rt->rt_flags & RTF_TUNNEL)
+ {
+ DPRINTF(IDL_EVENT,("Cleaning up tunnel route.\n"));
+ tunnel_parent_clean(rt);
+ if (rt_mask(rt)->sa_len != 0)
+ return; /* If default route is RTF_TUNNEL, then continue. */
+ }
+ if (rt->rt_flags & RTF_GATEWAY)
+ if (rt_mask(rt)->sa_len == 0)
+ {
+ struct v6router *v6r;
+
+ DPRINTF(IDL_GROSS_EVENT,\
+ ("Cleaning up THE default route.\n"));
+ /*
+ * Find manually added default route thing, and clean up that
+ * entry.
+ *
+ * If user deletes default route added by receiving router
+ * adverts, then the router is still on the default router
+ * list, and needs to be added back.
+ */
+ for (v6r = defrtr.v6r_next; v6r != &defrtr;
+ v6r = v6r->v6r_next)
+ {
+ /*
+ * PROBLEM: rt->rt_gwroute, which is what I REALLY
+ * want to check against, is gone at this
+ * point.
+ *
+ * POSSIBLE SOLUTION: Do a check with rt_key(v6r->v6r_rt)
+ * and rt->rt_gateway, but there
+ * is the question of tunneling
+ * default routes, etc., which may
+ * mean doing a masked match.
+ */
+#define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0)
+ /*
+ * PRESENTED SOLUTION: Try checking the expiration
+ * (easy giveaway for manually
+ * added default routes), followed
+ * by blatant sockaddr compares
+ * (will nail all other normal
+ * cases), followed by sa_family
+ * check (may be unneccesary).
+ */
+ if (v6r->v6r_expire == 0 ||
+ equal(rt_key(v6r->v6r_rt),rt->rt_gateway) ||
+ (rt->rt_gateway->sa_family != AF_INET6 &&
+ rt->rt_gateway->sa_family ==
+ rt_key(v6r->v6r_rt)->sa_family))
+ break;
+ }
+ if (v6r != &defrtr)
+ (void)ipv6_delete_defrouter(v6r);
+ if (defrtr.v6r_next != &defrtr)
+ {
+ /*
+ * Somehow re-add the default route.
+ *
+ * The default route has been deleted from the radix
+ * tree already, so re-adding should be relatively
+ * straightforward.
+ */
+ DPRINTF(IDL_ERROR,
+ ("Auto-added default routers still there!\n"));
+ }
+ }
+ else
+ {
+ struct v6router *v6r = (struct v6router *)rt->rt_llinfo;
+ struct v6child *v6c;
+
+ /* Non-default router. */
+ DPRINTF(IDL_GROSS_EVENT,\
+ ("Cleaning up a non-{default,host} route.\n"));
+
+ if (v6r == NULL)
+ panic("Non-default w/o v6router entry.");
+
+ v6c = v6r->v6r_children.v6c_next;
+ while (v6c != v6c->v6c_next)
+ {
+ DPRINTF(IDL_ERROR,("Calling RTM_DELETE of child.\n"));
+ /* rtrequest() should remove child from the linked list */
+ rtrequest(RTM_DELETE,rt_key(v6c->v6c_route),NULL,NULL,0,
+ NULL);
+ v6c = v6r->v6r_children.v6c_next;
+ }
+ remque(v6r);
+ free(v6r,M_DISCQ);
+ }
+ /* Anything else that isn't a HOST needs no work, so return. */
+ return;
+ }
+
+ DPRINTF(IDL_EVENT,("v6_discov_rtrequest() deleting a host\n"));
+ DPRINTF(IDL_EVENT,("I'm at a host-route point.\n"));
+
+ if (rt->rt_flags & RTF_TUNNEL)
+ {
+ DPRINTF(IDL_EVENT,("Tunneling child.\n"));
+ /*
+ * PROBLEM: Following check will die if parent went bye-bye.
+ * See if you can fix in another way.
+ */
+ /*if (rt_mask(rt->rt_parent)->sa_len == 0)*/
+ if (rt->rt_flags & RTF_DEFAULT)
+ {
+ /*
+ * Tunneling default route child. Clean off meta-state.
+ */
+ struct v6child *v6c = (struct v6child *)rt->rt_llinfo;
+
+ DPRINTF(IDL_ERROR,("Cleaning tunnel-default child.\n"));
+ remque(v6c);
+ rt->rt_llinfo = NULL;
+ free(v6c,M_DISCQ);
+ }
+ tunnel_child_clean(rt);
+ }
+ else if (rt->rt_flags & RTF_GATEWAY)
+ {
+ struct v6child *v6c = (struct v6child *) rt->rt_llinfo;
+
+ if (v6c == NULL)
+ {
+ if (rt->rt_flags & RTF_HOST)
+ DPRINTF(IDL_ERROR,
+ ("no v6c in RTM_DELETE of RTF_GATEWAY.\n"));
+ }
+ else
+ {
+ remque(v6c);
+ rt->rt_llinfo = NULL;
+ free(v6c,M_DISCQ);
+ }
+ }
+ else if (rt->rt_flags & RTF_LLINFO)
+ {
+ struct discq *dq;
+
+ /* Neighbor cache entry. */
+ if (rt->rt_flags & RTF_ISAROUTER)
+ {
+ struct v6router *v6r,*head;
+
+ /* Clean up all children of this router. */
+
+ DPRINTF(IDL_GROSS_EVENT,("Cleaning up router neighbor.\n"));
+ if (rt->rt_flags & RTF_DEFAULT)
+ head = &defrtr;
+ else
+ {
+ head = &nondefrtr;
+ /* Q: Do I want to delete the actual network route too? */
+ /* Q2: Do I want to delete the children? */
+ }
+
+ for (v6r = head->v6r_next; v6r != head; v6r = v6r->v6r_next)
+ if (v6r->v6r_rt == rt)
+ break;
+ if (v6r == head)
+ {
+ /*
+ * At many addresses per interface, this isn't a huge
+ * problem, because a router's on-link address might not
+ * be tied to a router entry.
+ */
+ DPRINTF(IDL_EVENT,
+ ("Router neighbor inconsistency.\n"));
+ }
+ else
+ if (head == &defrtr)
+ (void) ipv6_delete_defrouter(v6r); /* This gets rid of
+ children, too! */
+ }
+
+ dq = (struct discq *)rt->rt_llinfo;
+ rt->rt_flags &= ~RTF_LLINFO;
+ if (dq == NULL)
+ panic("No discq or other rt_llinfo in RTM_DELETE");
+ remque(dq);
+ rt->rt_llinfo = NULL;
+ if (dq->dq_queue)
+ {
+ /* Send ICMP unreachable error. */
+ ipv6_icmp_error(dq->dq_queue, ICMPV6_UNREACH,
+ ICMPV6_UNREACH_ADDRESS, 0);
+ /* m_freem(dq->dq_queue);*/
+ }
+ free(dq,M_DISCQ);
+ }
+ else
+ {
+ DPRINTF(IDL_GROSS_EVENT,\
+ ("Freeing self-wired address. Doing nothing.\n"));
+ }
+ break;
+ }
+ DPRINTF(IDL_GROSS_EVENT,("ipv6_rtrequest():1000-Finished.\n"));
+}
+
+/* EOF */
diff --git a/sys/netinet6/ipv6_trans.c b/sys/netinet6/ipv6_trans.c
new file mode 100644
index 00000000000..adfd61710b9
--- /dev/null
+++ b/sys/netinet6/ipv6_trans.c
@@ -0,0 +1,669 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+
+#include <netinet6/in6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ipv6.h>
+#include <netinet6/ipv6_var.h>
+
+#include <sys/debug.h>
+
+#if __OpenBSD__ && defined(NRL_IPSEC)
+#define IPSEC 1
+#endif /* __OpenBSD__ && defined(NRL_IPSEC) */
+
+/*
+ * External globals.
+ */
+
+extern struct in6_ifaddr *in6_ifaddr;
+extern int ipv6_defhoplmt;
+
+int ipv6_trans_mtu __P((struct mbuf **, int, int));
+int ipv6_trans_output __P((struct mbuf *, struct sockaddr_in *, struct rtentry *));
+int ipv6_encaps_output __P((struct mbuf *, struct sockaddr_in6 *, struct rtentry *));
+int ipv6_tunnel_output __P((struct mbuf *, struct sockaddr_in6 *, struct rtentry *));
+int ipv4_trans_output __P((struct mbuf *, struct sockaddr_in6 *, struct rtentry *));
+int ipv4_encaps_output __P((struct mbuf *, struct sockaddr_in *, struct rtentry *));
+int ipv4_tunnel_output __P((struct mbuf *, struct sockaddr_in *, struct rtentry *));
+
+/*----------------------------------------------------------------------
+ * Called from ip_icmp.c, this function will reduce the tunnel path MTU
+ * precisely. I know I have enough to reconstruct the IPv6 header, which
+ * is all I care about for this case. Return 1 if m0 is intact, and 0 if
+ * m0 is corrupted somehow. Don't forget to update m0.
+ ----------------------------------------------------------------------*/
+
+int
+ipv6_trans_mtu(m0, newmtu, len)
+ struct mbuf **m0;
+ int newmtu,len;
+{
+ struct ip *ip,*iip;
+ struct ipv6 *ipv6;
+ struct icmp *icp;
+ struct rtentry *rt;
+ struct sockaddr_in6 sin6;
+ struct in6_ifaddr *i6a;
+
+ /*
+ * Make packet contiguous into one block of memory. If the IPv6 header is
+ * beyond MCLBYTES into the packet, then I'm in big trouble.
+ */
+ *m0 = m_pullup2(*m0,min(len,MCLBYTES));
+ if (*m0 == NULL)
+ return 0;
+
+ ip = mtod(*m0,struct ip *);
+ icp = (struct icmp *) ((caddr_t)ip + (ip->ip_hl << 2));
+ iip = &icp->icmp_ip;
+ ipv6 = (struct ipv6 *) ((caddr_t)iip + (iip->ip_hl << 2));
+
+ /*
+ * Verify source is one of mine?
+ */
+ for (i6a = in6_ifaddr; i6a != NULL; i6a = i6a->i6a_next)
+ if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &ipv6->ipv6_src))
+ break;
+ if (i6a == NULL)
+ {
+ /* Packet didn't originate with me. Drop it. */
+ return 1;
+ }
+
+ /*
+ * Find route for this destination and update it.
+ */
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_port = 0;
+ sin6.sin6_flowinfo = 0;
+ sin6.sin6_addr = ipv6->ipv6_dst;
+
+#ifdef __FreeBSD__
+ rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
+#else /* __FreeBSD__ */
+ rt = rtalloc1((struct sockaddr *)&sin6, 0);
+#endif /* __FreeBSD__ */
+
+ if (rt == NULL)
+ return 1;
+ rt->rt_refcnt--;
+ /*
+ * Update path MTU.
+ */
+ if (!(rt->rt_flags & RTF_HOST))
+ return 1; /* Can't update path MTU on non-host-route. */
+ if (rt->rt_rmx.rmx_mtu < newmtu - sizeof(struct ip))
+ panic("MTU WEIRDNESS !!!");
+ rt->rt_rmx.rmx_mtu = newmtu - sizeof(struct ip);
+ return 1;
+}
+
+/*----------------------------------------------------------------------
+ * Handle ICMP errors for IPv6-in-IPv4 tunnels.
+ *
+ * Security processing should be put in here, as it was with the other
+ * ctlinput() functions, but with current ICMP implementations returning
+ * only sizeof(struct ip) + 64 bits of offending packet.
+ ----------------------------------------------------------------------*/
+#if __OpenBSD__
+void *ipv6_trans_ctlinput(int cmd, struct sockaddr *sa, void *vp, struct mbuf *incoming)
+#else /* __OpenBSD__ */
+void ipv6_trans_ctlinput(int cmd, struct sockaddr *sa, register struct ip *ip, struct mbuf *incoming)
+#endif /* __OpenBSD__ */
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ struct sockaddr_in6 sin6;
+ struct ipv6 *ipv6;
+ struct rtentry *rt;
+ struct in6_ifaddr *i6a;
+#if __OpenBSD__
+ struct ip *ip = (struct ip *)vp;
+#endif /* __OpenBSD__ */
+
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_port = 0;
+ sin6.sin6_flowinfo = 0;
+ DPRINTF(IDL_EVENT,("Entered ipv6_trans_ctlinput().\n"));
+
+ /*
+ * Do standard checks to see that all parameters are here.
+ */
+ if ((unsigned)cmd > PRC_NCMDS || sa->sa_family != AF_INET ||
+ sin->sin_addr.s_addr == INADDR_ANY || ip == NULL)
+ {
+ DPRINTF(IDL_EVENT,("Failed one of the four checks. Returning.\n"));
+#ifdef __OpenBSD__
+ return NULL;
+#else /* __OpenBSD__ */
+ return;
+#endif /* __OpenBSD__ */
+ }
+
+ /*
+ * Okay, at this point I have a contiguous IPv6 in IPv4 datagram.
+ * I achieved this effect by convincing ip_icmp.[ch] to pull up
+ * more than the first 64 bits.
+ */
+
+ ipv6 = (struct ipv6 *) ((caddr_t)ip + (ip->ip_hl << 2));
+ /*
+ * Verify source address is one of mine.
+ */
+ for (i6a = in6_ifaddr; i6a != NULL; i6a = i6a->i6a_next)
+ if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &ipv6->ipv6_src))
+ break;
+ if (i6a == NULL)
+ {
+ /*
+ * Packet didn't originate with me. Drop it.
+ */
+#ifdef __OpenBSD__
+ return NULL;
+#else /* __OpenBSD__ */
+ return;
+#endif /* __OpenBSD__ */
+ }
+
+ sin6.sin6_addr = ipv6->ipv6_dst;
+#ifdef __FreeBSD__
+ rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
+#else /* __FreeBSD__ */
+ rt = rtalloc1((struct sockaddr *)&sin6, 0);
+#endif /* __FreeBSD__ */
+ if (rt == NULL)
+#ifdef __OpenBSD__
+ return NULL;
+#else /* __OpenBSD__ */
+ return;
+#endif /* __OpenBSD__ */
+ rt->rt_refcnt--;
+
+ switch (cmd)
+ {
+ case PRC_MSGSIZE:
+ /*
+ * This function was called because the actual MTU wasn't grokked
+ * from the ICMP packet.
+ *
+ * If I get this, drop to IPV6_MINMTU. If the actual MTU was in the
+ * ICMP packet and was read correctly, it went up a different codepath.
+ *
+ * RFC 1191 talks about a plateau table. Here's the place to do it,
+ * either that, or on increase.
+ */
+ if (rt->rt_flags & RTF_HOST)
+ {
+ /*
+ * Only attempt path MTU update if I'm a host.
+ */
+ if (rt->rt_rmx.rmx_mtu == IPV6_MINMTU)
+ panic("Too big on v6 MTU of 576!!!");
+ rt->rt_rmx.rmx_mtu = IPV6_MINMTU;
+ }
+ break;
+ case PRC_UNREACH_NET:
+ rt->rt_flags &= ~RTF_HOST; /* Is this wise? I'm doing this to return
+ the right error on future requests. */
+ /* FALLTHROUGH */
+ case PRC_UNREACH_HOST:
+ case PRC_UNREACH_PROTOCOL:
+ /*
+ * Other end isn't a v6/v4 node.
+ */
+ rt->rt_flags |= RTF_REJECT; /* Don't want to send any packets. */
+ break;
+ default:
+ break;
+ }
+#ifdef __OpenBSD__
+ return NULL;
+#endif /* __OpenBSD__ */
+}
+
+/*----------------------------------------------------------------------
+ *"IPv6 in IPv4 tunnelling."
+ *
+ * Output routine for IPv6 in IPv4. Uses M_PREPEND to prepend an IPv4
+ * header, and call ip_output().
+ *
+ * Called in: (only) ipv6_tunnel_output() from this file.
+ ----------------------------------------------------------------------*/
+
+int
+ipv6_trans_output(outgoing, v4dst, v6rt)
+ struct mbuf *outgoing;
+ struct sockaddr_in *v4dst;
+ struct rtentry *v6rt;
+{
+ struct route v4route;
+ struct ip *ip;
+ struct ipv6 *ipv6 = mtod(outgoing,struct ipv6 *);
+ int rc;
+#ifdef IPSEC
+ struct socket *socket;
+#endif /* IPSEC */
+
+ /*
+ * Like all below-IP(v6) output routines, check RTF_REJECT flag.
+ */
+ if (v6rt->rt_flags & RTF_REJECT)
+ {
+ m_freem(outgoing);
+ return (v6rt->rt_flags & RTF_HOST) ? EHOSTUNREACH : ENETUNREACH;
+ }
+
+ if (v6rt->rt_gwroute)
+ v6rt->rt_gwroute->rt_refcnt++;
+ v4route.ro_rt = v6rt->rt_gwroute;
+ bcopy(v4dst,&v4route.ro_dst,v4dst->sin_len);
+
+ /*
+ * Prepend IPv4 header.
+ */
+ M_PREPEND(outgoing,sizeof(struct ip), M_DONTWAIT);
+ if (outgoing == NULL)
+ return ENOBUFS;
+
+ ip = mtod(outgoing,struct ip *);
+ bzero(ip,sizeof(*ip));
+
+ /*
+ * Following four lines are done here rather than ip_output() because of
+ * the *&*&%^^& don't fragment bit.
+ */
+ ip->ip_v = IPVERSION;
+ ip->ip_id = htons(ip_id++);
+ ip->ip_hl = sizeof(*ip)>>2;
+ if (v6rt->rt_rmx.rmx_mtu > IPV6_MINMTU)
+ ip->ip_off |= IP_DF;
+ ipstat.ips_localout++;
+
+ if (v6rt->rt_flags & (RTF_HOST|RTF_GATEWAY))
+ ip->ip_dst = v4dst->sin_addr;
+ else
+ {
+ /*
+ * If I'm in here, this means I'm not a host route, but when I was
+ * supposed to clone, I was supposed to change the v4dst addr.
+ *
+ * This will only happen if I'm a v6-in-v4 router-to-host route,
+ * in which case I have to do the translation on the fly, based on
+ * the data in the IPv6 header.
+ */
+ if (!IN6_IS_ADDR_V4COMPAT(&ipv6->ipv6_dst))
+ {
+ printf("Oooh boy, v6-in-v4 tunnel ( trans_output() ) trouble!!!\n");
+ }
+ ip->ip_dst.s_addr = ipv6->ipv6_dst.in6a_words[3];
+ }
+
+ ip->ip_src.s_addr = INADDR_ANY;
+ ip->ip_p = IPPROTO_IPV6;
+ ip->ip_ttl = ip_defttl;
+ ip->ip_len = outgoing->m_pkthdr.len;
+
+#ifdef IPSEC
+ if (v6rt->rt_flags & (RTF_CRYPT|RTF_AUTH)) {
+ /*
+ * A secure route has hanging off its rt_netproc field something which
+ * can be tagged onto an outgoing mbuf such that ipv4_output can
+ * secure the IPv6-in-IPv4 packet.
+ */
+ DPRINTF(IDL_EVENT,("Secure route, sending cheesy socket.\n"));
+ socket = v6rt->rt_netproc;
+ } else
+ socket = NULL;
+
+ rc = ip_output(outgoing, NULL, &v4route, IP_RAWOUTPUT, NULL, socket);
+#else /* IPSEC */
+#if __bsdi__
+ rc = ip_output(outgoing, NULL, &v4route, IP_RAWOUTPUT, NULL, NULL);
+#else /* __bsdi__ */
+ rc = ip_output(outgoing, NULL, &v4route, IP_RAWOUTPUT, NULL);
+#endif /* __bsdi__ */
+#endif /* IPSEC */
+
+ if (rc == EMSGSIZE)
+ {
+ DPRINTF(IDL_ERROR,("Path MTU adjustment needed in trans_output().\n"));
+ }
+ if (v4route.ro_rt != NULL)
+ RTFREE(v4route.ro_rt);
+ return rc;
+}
+
+/*----------------------------------------------------------------------
+ * "IPv6 in IPv6 tunnelling."
+ *
+ * Encapsulate IPv6 packet in another IPv6 packet. This, in combination
+ * with passing on a fake socket with a security request, can enable a
+ * configured secure tunnel.
+ *
+ * Called in: (only) ipv6_tunnel_output() from this file.
+ ----------------------------------------------------------------------*/
+
+int
+ipv6_encaps_output(outgoing, tundst, tunrt)
+ struct mbuf *outgoing;
+ struct sockaddr_in6 *tundst;
+ struct rtentry *tunrt;
+{
+ struct route6 actroute;
+ struct ipv6 *ipv6;
+ int rc;
+#ifdef IPSEC
+ struct socket *socket;
+#endif /* IPSEC */
+
+DPRINTF(IDL_GROSS_EVENT,("\n\nipv6_encaps_output():0000-Hey! I'm in IPV6_in_IPV6 tunnelling code!!\n"));
+ if (tunrt->rt_flags & RTF_REJECT)
+ {
+ m_freem(outgoing);
+ return (tunrt->rt_flags & RTF_HOST) ? EHOSTUNREACH : ENETUNREACH;
+ }
+
+ if (tunrt->rt_gwroute)
+ tunrt->rt_gwroute->rt_refcnt++;
+ actroute.ro_rt = tunrt->rt_gwroute;
+ bcopy(tundst,&actroute.ro_dst,tundst->sin6_len);
+
+ M_PREPEND(outgoing,sizeof(struct ipv6), M_DONTWAIT);
+ if (outgoing == NULL)
+ return ENOBUFS;
+
+ ipv6 = mtod(outgoing,struct ipv6 *);
+ bzero(ipv6,sizeof(*ipv6));
+
+ ipv6->ipv6_versfl = htonl(0x60000000);
+ ipv6->ipv6_length = outgoing->m_pkthdr.len - sizeof(struct ipv6);
+ ipv6->ipv6_nexthdr = IPPROTO_IPV6;
+ ipv6->ipv6_hoplimit = ipv6_defhoplmt;
+ ipv6->ipv6_dst = tundst->sin6_addr;
+
+#ifdef IPSEC
+ if (tunrt->rt_flags & (RTF_CRYPT|RTF_AUTH)) {
+ /*
+ * A secure route has hanging off its rt_netproc field something which
+ * can be tagged onto an outgoing mbuf such that ipv6_output can
+ * secure the IPv6-in-IPv6 packet.
+ */
+ DPRINTF(IDL_EVENT,("ipv6_encaps_output():0500-Secure route, sending cheesy socket.\n"));
+ socket = tunrt->rt_netproc;
+ } else
+ socket = NULL;
+
+ rc = ipv6_output(outgoing, &actroute, IPV6_RAWOUTPUT, NULL, NULL, socket);
+#else /* IPSEC */
+ rc = ipv6_output(outgoing, &actroute, IPV6_RAWOUTPUT, NULL, NULL, NULL);
+#endif /* IPSEC */
+
+ if (rc == EMSGSIZE)
+ {
+ DPRINTF(IDL_ERROR,("Path MTU adjustment needed in trans_output().\n"));
+ }
+ if (actroute.ro_rt != NULL)
+ RTFREE(actroute.ro_rt);
+ return rc;
+
+}
+/*----------------------------------------------------------------------
+ * "IPv4 in IPv4 tunnelling"
+ *
+ * Output routine for IPv4 in IPv4. Uses M_PREPEND to prepend an IPv4
+ * header (i.e. the tunnel header), and then call ip_output() to
+ * send the tunnel packet. The v4 in v4 tunnel seems redundant, but is
+ * useful for setting up secure tunnels.
+ *
+ * Called in: (only) ipv4_tunnel_output() from this file.
+ ----------------------------------------------------------------------*/
+
+int
+ipv4_encaps_output(outgoing, v4tundst, v4rt)
+ struct mbuf *outgoing;
+ struct sockaddr_in *v4tundst; /* Goto to destination tunnel endpoint. */
+ struct rtentry *v4rt; /* The encapsulated (i.e. passenger) IPv4
+ * packet's route. */
+{
+ struct route v4tunroute; /* The tunnel- (i.e. actual) route to the tunnel's
+ * destination endpoint (i.e. to the other
+ * side of the tunnel). */
+ struct ip *ip; /* For setting up the IPv4 data needed for the
+ * tunnel IPv4 packet. */
+ int rc; /* Return Code */
+#ifdef IPSEC
+ struct socket *socket;
+#endif /* IPSEC */
+
+ /*
+ * Like all below-IP(v4) output routines, check RTF_REJECT flag.
+ * Why? We need to make sure the logical (i.e. encapsulated packet's)
+ * route to the final destination is reachable.
+ */
+ if (v4rt->rt_flags & RTF_REJECT)
+ {
+ m_freem(outgoing);
+ return (v4rt->rt_flags & RTF_HOST) ? EHOSTUNREACH : ENETUNREACH;
+ }
+
+ if (v4rt->rt_gwroute)
+ v4rt->rt_gwroute->rt_refcnt++;
+ v4tunroute.ro_rt = v4rt->rt_gwroute;
+ bcopy(v4tundst,&v4tunroute.ro_dst,v4tundst->sin_len);
+
+ /*
+ * Prepend IPv4 header.
+ */
+ M_PREPEND(outgoing,sizeof(struct ip), M_DONTWAIT);
+ if (outgoing == NULL)
+ return ENOBUFS;
+
+ ip = mtod(outgoing,struct ip *);
+ bzero(ip,sizeof(*ip));
+
+ /*
+ * Initialization of IP header: We'll let ip_output() fill in
+ * IP's version, ip, offset, and header length as well as update
+ * ipstat.ips_localout.
+ *
+ * Of course, we need to do the rest ...
+ */
+
+ ip->ip_src.s_addr = INADDR_ANY;
+ ip->ip_p = IPPROTO_IPV4;
+ ip->ip_ttl = ip_defttl;
+ ip->ip_len = outgoing->m_pkthdr.len;
+
+ ip->ip_dst = v4tundst->sin_addr;
+
+#ifdef IPSEC
+ if (v4rt->rt_flags & (RTF_CRYPT|RTF_AUTH)) {
+ /*
+ * A secure route has hanging off its rt_netproc field something which
+ * can be tagged onto an outgoing mbuf such that ipv4_output can
+ * secure the IPv4-in-IPv4 packet.
+ */
+ DPRINTF(IDL_EVENT,("ipv4_encaps_output():0500-Secure route, sending cheesy socket.\n"));
+ socket = v4rt->rt_netproc;
+ } else
+ socket = NULL;
+
+ rc = ip_output(outgoing, NULL, &v4tunroute, 0, NULL, socket);
+#else /* IPSEC */
+#if __bsdi__
+ rc = ip_output(outgoing, NULL, &v4tunroute, 0, NULL, NULL);
+#else /* __bsdi__ */
+ rc = ip_output(outgoing, NULL, &v4tunroute, 0, NULL);
+#endif /* __bsdi__ */
+#endif /* IPSEC */
+
+ if (v4tunroute.ro_rt != NULL)
+ RTFREE(v4tunroute.ro_rt);
+ return rc;
+}
+
+/*----------------------------------------------------------------------
+ *"IPv4 in IPv6"
+ *
+ * Encapsulate IPv4 packet in a IPv6 packet.
+ *
+ * Called in: (only) ipv4_tunnel_output() from this file.
+ *---------------------------------------------------------------------*/
+
+int
+ipv4_trans_output(outgoing, tunv6dst, v4rt)
+ struct mbuf *outgoing;
+ struct sockaddr_in6 *tunv6dst;
+ struct rtentry *v4rt;
+{
+ struct route6 tunv6route;
+ struct ipv6 *ipv6;
+ int rc;
+#ifdef IPSEC
+ struct socket *socket;
+#endif /* IPSEC */
+
+ if (v4rt->rt_flags & RTF_REJECT)
+ {
+ m_freem(outgoing);
+ return (v4rt->rt_flags & RTF_HOST) ? EHOSTUNREACH : ENETUNREACH;
+ }
+
+ if (v4rt->rt_gwroute)
+ v4rt->rt_gwroute->rt_refcnt++;
+ tunv6route.ro_rt = v4rt->rt_gwroute;
+ bcopy(tunv6dst,&tunv6route.ro_dst,tunv6dst->sin6_len);
+
+ M_PREPEND(outgoing,sizeof(struct ipv6), M_DONTWAIT);
+ if (outgoing == NULL)
+ return ENOBUFS;
+
+ ipv6 = mtod(outgoing,struct ipv6 *);
+ bzero(ipv6,sizeof(*ipv6));
+
+ ipv6->ipv6_versfl = htonl(0x60000000);
+ ipv6->ipv6_length = outgoing->m_pkthdr.len - sizeof(struct ipv6);
+ ipv6->ipv6_nexthdr = IPPROTO_IPV4;
+ ipv6->ipv6_hoplimit = ipv6_defhoplmt;
+ ipv6->ipv6_dst = tunv6dst->sin6_addr;
+
+#ifdef IPSEC
+ if (v4rt->rt_flags & (RTF_CRYPT|RTF_AUTH)) {
+ /*
+ * A secure route has hanging off its rt_netproc field something which
+ * can be tagged onto an outgoing mbuf such that ipv6_output can
+ * secure the IPv4-in-IPv6 packet.
+ */
+ DPRINTF(IDL_EVENT,("ipv4_trans_output():0500-Secure route, sending cheesy socket.\n"));
+ socket = v4rt->rt_netproc;
+ } else
+ socket = NULL;
+
+ rc = ipv6_output(outgoing, &tunv6route, IPV6_RAWOUTPUT, NULL, NULL, socket);
+#else /* IPSEC */
+ rc = ipv6_output(outgoing, &tunv6route, IPV6_RAWOUTPUT, NULL, NULL, NULL);
+#endif /* IPSEC */
+
+ if (rc == EMSGSIZE)
+ {
+ DPRINTF(IDL_ERROR,("Path MTU adjustment needed in trans_output().\n"));
+ }
+ if (tunv6route.ro_rt != NULL)
+ RTFREE(tunv6route.ro_rt);
+ return rc;
+}
+
+
+/*----------------------------------------------------------------------
+ * Called by ipv6_output if the RTF_TUNNEL bit is set on a route,
+ * this function examines the route, and sees what sort of encapsulation is
+ * needed. Often, the rt->rt_gateway sockaddr is used to figure this out.
+ ----------------------------------------------------------------------*/
+
+int
+ipv6_tunnel_output(outgoing, dst, rt)
+ struct mbuf *outgoing;
+ struct sockaddr_in6 *dst;
+ struct rtentry *rt;
+{
+ DPRINTF(IDL_EVENT,("\n\nipv6_tunnel_output():0000-Just entered.\n"));
+
+ /*
+ * Determine what type of tunnel it is with rt. Perform correct kind
+ * of encapsulation (in IPv4, ESP, etc.) and call output routine of
+ * what you want encapsulated.
+ */
+
+ /* IPv6 in IPv4. */
+ if (rt->rt_gateway != NULL && rt->rt_gateway->sa_family == AF_INET)
+ return ipv6_trans_output(outgoing,(struct sockaddr_in *)rt->rt_gateway,rt);
+
+ /* IPv6 in IPv6. */
+ if (rt->rt_gateway != NULL && rt->rt_gateway->sa_family == AF_INET6)
+ return ipv6_encaps_output(outgoing,(struct sockaddr_in6 *)rt->rt_gateway,rt);
+
+ m_freem(outgoing);
+ return EHOSTUNREACH;
+}
+
+
+/*----------------------------------------------------------------------
+ * Called by ip_output if the RTF_TUNNEL bit is set on a route,
+ * this function examines the route, and sees what sort of encapsulation is
+ * needed. Often, the rt->rt_gateway sockaddr is used to figure this out.
+ ----------------------------------------------------------------------*/
+
+int
+ipv4_tunnel_output(outgoing, dst, rt)
+ struct mbuf *outgoing;
+ struct sockaddr_in *dst;
+ struct rtentry *rt;
+{
+ DPRINTF(IDL_EVENT,("\n\nipv4_tunnel_output():0000-Just entered.\n"));
+
+ /*
+ * Determine what type of tunnel it is with rt. Perform correct kind
+ * of encapsulation (in IPv4, ESP, etc.) and call output routine of
+ * what you want encapsulated.
+ */
+
+ /* IPv4 in IPv6. */
+ if (rt->rt_gateway != NULL && rt->rt_gateway->sa_family == AF_INET6)
+ return ipv4_trans_output(outgoing,(struct sockaddr_in6 *)rt->rt_gateway,rt);
+
+ /* IPv4 in IPv4. */
+ if (rt->rt_gateway != NULL && rt->rt_gateway->sa_family == AF_INET)
+ return ipv4_encaps_output(outgoing,(struct sockaddr_in *)rt->rt_gateway,rt);
+
+ m_freem(outgoing);
+ return EHOSTUNREACH;
+}
diff --git a/sys/netinet6/ipv6_trans.h b/sys/netinet6/ipv6_trans.h
new file mode 100644
index 00000000000..5ceb2456e3a
--- /dev/null
+++ b/sys/netinet6/ipv6_trans.h
@@ -0,0 +1,26 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+#ifndef _NETINET6_IPV6_TRANS_H
+#define _NETINET6_IPV6_TRANS_H 1
+
+/* I don't include any #includes, as I'm using this for our (NRL)
+ * modified netinet/ip_output() function; thus, this #include should be
+ * used/stuck-in after all the other necessary includes.
+ *
+ * And yes, I only put one declaration here. There's no real need
+ * to stick the other prototypes in here and have ip_output() fluffed
+ * during preprocessing time.
+ */
+
+int ipv4_tunnel_output __P((struct mbuf *, struct sockaddr_in *, struct rtentry *));
+
+#endif /* _NETINET6_IPV6_TRANS_H */
diff --git a/sys/netinet6/ipv6_var.h b/sys/netinet6/ipv6_var.h
new file mode 100644
index 00000000000..f0e88034bcd
--- /dev/null
+++ b/sys/netinet6/ipv6_var.h
@@ -0,0 +1,346 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+#ifndef _NETINET6_IPV6_VAR_H
+#define _NETINET6_IPV6_VAR_H 1
+
+#include <netinet6/in6.h>
+
+/*
+ * IPv6 multicast "options". Session state for multicast, including
+ * weird per-session multicast things.
+ */
+
+struct ipv6_moptions
+{
+ struct ifnet *i6mo_multicast_ifp; /* ifp for outgoing multicasts */
+ u_char i6mo_multicast_ttl; /* TTL for outgoing multicasts.
+ Does this matter in IPv6? */
+ u_char i6mo_multicast_loop; /* 1 => hear sends if a member */
+ u_short i6mo_num_memberships; /* no. memberships this socket */
+ struct in6_multi *i6mo_membership[IN6_MAX_MEMBERSHIPS];
+};
+
+/*
+ * IPv6 stats.
+ */
+
+#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
+#define _IPV6STAT_TYPE u_quad_t
+#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+#define _IPV6STAT_TYPE u_long
+#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
+
+struct ipv6stat {
+ _IPV6STAT_TYPE ips_total; /* total packets received */
+ _IPV6STAT_TYPE ips_tooshort; /* packet too short */
+ _IPV6STAT_TYPE ips_toosmall; /* not enough data */
+ _IPV6STAT_TYPE ips_fragments; /* fragments received */
+ _IPV6STAT_TYPE ips_fragdropped; /* frags dropped (dups, out of space) */
+ _IPV6STAT_TYPE ips_fragtimeout; /* fragments timed out */
+ _IPV6STAT_TYPE ips_forward; /* packets forwarded */
+ _IPV6STAT_TYPE ips_cantforward; /* packets rcvd for unreachable dest */
+ _IPV6STAT_TYPE ips_redirectsent; /* packets forwarded on same net */
+ _IPV6STAT_TYPE ips_noproto; /* unknown or unsupported protocol */
+ _IPV6STAT_TYPE ips_delivered; /* datagrams delivered to upper level*/
+ _IPV6STAT_TYPE ips_localout; /* total ip packets generated here */
+ _IPV6STAT_TYPE ips_odropped; /* lost packets due to nobufs, etc. */
+ _IPV6STAT_TYPE ips_reassembled; /* total packets reassembled ok */
+ _IPV6STAT_TYPE ips_fragmented; /* datagrams sucessfully fragmented */
+ _IPV6STAT_TYPE ips_ofragments; /* output fragments created */
+ _IPV6STAT_TYPE ips_cantfrag; /* don't fragment flag was set, etc. */
+ _IPV6STAT_TYPE ips_badoptions; /* error in option processing */
+ _IPV6STAT_TYPE ips_noroute; /* packets discarded due to no route */
+ _IPV6STAT_TYPE ips_badvers; /* IPv6 version != 6 */
+ _IPV6STAT_TYPE ips_rawout; /* total raw ip packets generated */
+};
+
+#ifdef KERNEL
+
+/*
+ * The IPv6 fragment queue entry structure.
+ * Notes:
+ * Nodes are stored in ttl order.
+ * prefix comes from whichever packet gets here first.
+ * data contains a chain of chains of mbufs (m_next down a chain, m_nextpkt
+ * chaining chains together) where the chains are ordered by assembly
+ * position. When two chains are contiguous for reassembly, they are
+ * combined and the frag header disappears.
+ * The structure is deliberately sized so MALLOC will round up on the order
+ * of much less than the total size instead of doubling the size.
+ */
+
+struct ipv6_fragment
+{
+ struct ipv6_fragment *next; /* Next fragment chain */
+ struct mbuf *prefix; /* Headers before frag header(s) */
+ struct mbuf *data; /* Frag headers + whatever data */
+ u_char ttl; /* Fragment chain TTL. */
+ u_char flags; /* Bit 0 indicates got end of chain */
+};
+
+/*
+ * Structures and definitions for discovery mechanisms in IPv6.
+ */
+
+/*
+ * Neighbor cache:
+ *
+ * Number of unanswered probes is in discq.
+ * "Time of next event" will be in rt->rt_rmx.rmx_expire
+ * (rmx_expire will actually be quite overloaded, actually.)
+ * Status REACHABLE will be dq_unanswered < 0
+ * Status PROBE will be dq_unanswered >= 0
+ * Status INCOMPLETE will be link addr length of 0 if held,
+ * or deleted if not held.
+ *
+ * If held, but INCOMPLETE fails set RTF_REJECT and make sure
+ * IPv6 and HLP's know how to deal with RTF_REJECT being set.
+ */
+
+struct discq /* Similar to v4's llinfo_arp, discovery's "neighbor entry". */
+{
+ struct discq *dq_next,*dq_prev; /* For {ins,rem}que(). */
+ struct rtentry *dq_rt; /* Back pointer to routing entry for
+ an address that may be dead. */
+ struct mbuf *dq_queue; /* Queue of outgoing messages. */
+ int dq_unanswered; /* Number of unanswered probes. */
+};
+
+#if !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)
+/* Routing flag redefinitions */
+#define RTF_ISAROUTER RTF_PROTO2 /* Neighbor is a router. */
+#define RTF_DEFAULT RTF_PROTO1 /* Default route. */
+#endif /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
+
+/*
+ * These should be configurable parameters, see ipv6_discovery.c.
+ * All units are in comments besides constants.
+ */
+
+#define MAX_INITIAL_RTR_ADVERT_INTERVAL 16 /* seconds */
+#define MAX_INITIAL_RTR_ADVERTISEMENTS 3 /* transmissions */
+#define MAX_RTR_RESPONSE_DELAY 2 /* seconds */
+
+#define MAX_RTR_SOLICITATION_DELAY 1 /* second */
+#define RTR_SOLICITATION_INTERVAL 3 /* seconds */
+#define MAX_RTR_SOLICITATIONS 3 /* transmissions */
+
+#define MAX_MULTICAST_SOLICIT 3 /* transmissions */
+#define MAX_UNICAST_SOLICIT 3 /* transmissions */
+#define MAX_ANYCAST_DELAY_TIME 1 /* seconds */
+#define MAX_NEIGHBOR_ADVERTISEMENTS 3 /* transmissions */
+#define MIN_NEIGHBOR_ADVERT_INTERVAL 16 /* seconds */
+#define REACHABLE_TIME 30 /* seconds */
+#define RETRANS_TIMER 3 /* seconds */
+#define DELAY_FIRST_PROBE_TIME 3 /* seconds */
+/* Need to somehow define random factors. */
+
+#define NEXTHOP_CLEAN_INTERVAL 600 /* seconds */
+#define REJECT_TIMER 20 /* seconds */
+
+/*
+ * Child of a router or tunnel. Is a "meta-entry" for garbage collection.
+ */
+
+struct v6child
+{
+ struct v6child *v6c_next,*v6c_prev; /* For {ins,rem}que() */
+ struct v6router *v6c_parent; /* Parent router. I'm null if
+ I'm the router, or a tunnel
+ child. */
+ struct rtentry *v6c_route; /* Next-hop cache entry. I won't
+ be holding it, but I'm attached
+ to it, like discq is to neighbor
+ cache entries. */
+};
+
+/*
+ * Default router list entry. Should be inserted
+ * in priority order. Will also have entries for non-
+ * default routers, because I may be a router myself.
+ */
+
+struct v6router
+{
+ struct v6router *v6r_next,*v6r_prev; /* For {ins,rem}que() */
+ struct rtentry *v6r_rt; /* Route for this. Could be neighbor,
+ could be tunnel. */
+ struct v6child v6r_children; /* Children of this router. */
+
+ /* Metric information? */
+ uint32_t v6r_expire; /* Expiration time. */
+};
+#define V6R_SIN6(v6r) ((struct sockaddr_in6 *)rt_key((v6r)->v6r_rt))
+
+/*
+ * Flags for "flags" argument in ipv6_output().
+ */
+
+#define IPV6_FORWARDING 0x1 /* Most of IPv6 header exists? */
+#define IPV6_RAWOUTPUT 0x2 /* Raw IPv6 packet! */
+#define IPV6_ROUTETOIF SO_DONTROUTE /* Include sys/socket.h... */
+
+void ipv6_init __P((void));
+void ipv6_drain __P((void));
+void ipv6_slowtimo __P((void));
+int ipv6_sysctl __P((int *, uint, void *, size_t *, void *, size_t));
+struct route6;
+
+#if __FreeBSD__
+int ipv6_ctloutput __P((struct socket *, struct sockopt *));
+int ripv6_ctloutput __P((struct socket *, struct sockopt *));
+#else /* __FreeBSD__ */
+int ipv6_ctloutput __P((int, struct socket *,int,int, struct mbuf **));
+int ripv6_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
+#endif /* __FreeBSD__ */
+void ripv6_init __P((void));
+#if __OpenBSD__
+void ripv6_input __P((struct mbuf *, ...));
+int ripv6_output __P((struct mbuf *, ...));
+#else /* __OpenBSD__ */
+void ripv6_input __P((struct mbuf *, int));
+int ripv6_output __P((struct mbuf *, struct socket *, struct in6_addr *, struct mbuf *));
+#endif /* __OpenBSD__ */
+
+#if __NetBSD__ || __FreeBSD__
+int ripv6_usrreq_send(struct socket *, int, struct mbuf *, struct sockaddr *,
+ struct mbuf *, struct proc *);
+#else /* __NetBSD__ || __FreeBSD__ */
+int ripv6_usrreq_send(struct socket *, int, struct mbuf *, struct sockaddr *,
+ struct mbuf *);
+#endif /* __NetBSD__ || __FreeBSD__ */
+
+#if __FreeBSD__
+int ripv6_usrreq_abort(struct socket *);
+int ripv6_usrreq_attach(struct socket *, int , struct proc *);
+int ripv6_usrreq_bind(struct socket *, struct sockaddr *, struct proc *);
+int ripv6_usrreq_connect(struct socket *, struct sockaddr *, struct proc *);
+int ripv6_usrreq_control(struct socket *, u_long, caddr_t, struct ifnet *,
+ struct proc *);
+int ripv6_usrreq_detach(struct socket *);
+int ripv6_usrreq_peeraddr(struct socket *, struct sockaddr **);
+int ripv6_usrreq_sense(struct socket *, struct stat *);
+int ripv6_usrreq_shutdown(struct socket *);
+int ripv6_usrreq_sockaddr(struct socket *, struct sockaddr **);
+#else /* __FreeBSD__ */
+#if __NetBSD__
+int ripv6_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *,
+ struct mbuf *, struct proc *));
+#else /* __NetBSD__ */
+int ripv6_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *,
+ struct mbuf *));
+#endif /* __NetBSD__ */
+#endif /* __FreeBSD__ */
+
+#if 0 /* __OpenBSD__ */
+void ipv6_input __P((struct mbuf *, ...));
+int ipv6_output __P((struct mbuf *, ...));
+#else /* __OpenBSD__ */
+void ipv6_input __P((struct mbuf *, int));
+int ipv6_output __P((struct mbuf *, struct route6 *, int, struct ipv6_moptions *, struct ifnet *, struct socket *));
+#endif /* __OpenBSD__ */
+void ipv6_reasm __P((struct mbuf *, int));
+void ipv6_hop __P((struct mbuf *, int));
+
+#if __FreeBSD__
+int in6_control __P((struct socket *,int, caddr_t, struct ifnet *,int, struct proc *));
+#else /* __FreeBSD__ */
+#if __NetBSD__
+int in6_control __P((struct socket *,u_long, caddr_t, struct ifnet *,int, struct proc *));
+#else /* __NetBSD__ */
+int in6_control __P((struct socket *,int, caddr_t, struct ifnet *,int));
+#endif /* __NetBSD__ */
+#endif /* __FreeBSD__ */
+void ipv6_stripoptions __P((struct mbuf *, int));
+struct in6_multi *in6_addmulti __P((struct in6_addr *,struct ifnet *));
+void in6_delmulti __P((struct in6_multi *));
+
+#if __FreeBSD__
+/* ripv6_usrreq and ipv6_icmp_usrreq functions */
+extern struct pr_usrreqs ripv6_usrreqs;
+extern struct pr_usrreqs ipv6_icmp_usrreqs;
+
+extern int ripv6_usr_attach(struct socket *, int , struct proc *);
+extern int ripv6_usr_disconnect(struct socket *);
+extern int ripv6_usr_abort(struct socket *);
+extern int ripv6_usr_detach(struct socket *);
+extern int ripv6_usr_bind(struct socket *, struct sockaddr *, struct proc *);
+extern int ripv6_usr_connect(struct socket *, struct sockaddr *, struct proc *);
+extern int ripv6_usr_shutdown(struct socket *);
+extern int ripv6_usr_send(struct socket *, int, struct mbuf *,
+ struct sockaddr *, struct mbuf *, struct proc *);
+extern int ripv6_usr_control(struct socket *, int, caddr_t,
+ struct ifnet *, struct proc *);
+extern int ripv6_usr_sense(struct socket *, struct stat *);
+extern int ripv6_usr_sockaddr(struct socket *, struct sockaddr **);
+extern int ripv6_usr_peeraddr(struct socket *, struct sockaddr **);
+#endif /* __FreeBSD__ */
+
+extern int ipv6_icmp_send(struct socket *, int, struct mbuf *,
+ struct sockaddr *, struct mbuf *, struct proc *);
+
+#if __OpenBSD__
+void *ipv6_trans_ctlinput __P((int, struct sockaddr *, void *, struct mbuf *));
+#else /* __OpenBSD__ */
+struct ip;
+void ipv6_trans_ctlinput __P((int, struct sockaddr *, struct ip *, struct mbuf *));
+#endif /* __OpenBSD__ */
+
+/* These might belong in in_pcb.h */
+struct inpcb;
+#if __FreeBSD__
+/*
+ * FreeBSD, having done away with the *_usrreq() functions no longer needs to
+ * pass mbufs to these functions. Thus they pass in sockaddrs instead.
+ */
+int in6_pcbbind(struct inpcb *, struct sockaddr *);
+int in6_pcbconnect(struct inpcb *, struct sockaddr *);
+int in6_setsockaddr(struct inpcb *, struct sockaddr **);
+int in6_setpeeraddr(struct inpcb *, struct sockaddr **);
+#else /* __FreeBSD__ */
+int in6_pcbbind(struct inpcb *, struct mbuf *);
+int in6_pcbconnect(struct inpcb *, struct mbuf *);
+int in6_setsockaddr(struct inpcb *, struct mbuf *);
+int in6_setpeeraddr(struct inpcb *, struct mbuf *);
+#endif /* __FreeBSD__ */
+void ipv6_onlink_query(struct sockaddr_in6 *);
+int ipv6_verify_onlink(struct sockaddr_in6 *);
+
+#if __FreeBSD__
+struct inpcbhead; /* XXX? Forward declaration needed. */
+#define __IN6_PCBNOTIFY_FIRSTARG struct inpcbhead *
+#endif /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+struct inpcbtable;
+#define __IN6_PCBNOTIFY_FIRSTARG struct inpcbtable *
+#endif /* __NetBSD__ || __OpenBSD__ */
+#if __bsdi__
+struct inpcb;
+#define __IN6_PCBNOTIFY_FIRSTARG struct inpcb *
+#endif /* __bsdi__ */
+
+#if (!__OpenBSD__ && defined(IPSEC)) || (__OpenBSD__ && defined(NRL_IPSEC))
+int in6_pcbnotify __P((__IN6_PCBNOTIFY_FIRSTARG, struct sockaddr *, uint,
+ struct in6_addr *, uint, int, void (*)(struct inpcb *,
+ int), struct mbuf *, int));
+#else /* (!__OpenBSD__ && defined(IPSEC)) || (__OpenBSD__ && defined(NRL_IPSEC)) */
+int in6_pcbnotify __P((__IN6_PCBNOTIFY_FIRSTARG, struct sockaddr *, uint,
+ struct in6_addr *, uint, int, void (*)(struct inpcb *,
+ int)));
+#endif /* (!__OpenBSD__ && defined(IPSEC)) || (__OpenBSD__ && defined(NRL_IPSEC)) */
+
+#undef __IN6_PCBNOTIFY_FIRSTARG
+
+void ipv6_freemoptions __P((struct ipv6_moptions *));
+#endif /* KERNEL */
+
+#endif /* _NETINET6_IPV6_VAR_H */
diff --git a/sys/netinet6/nd6_protocol.h b/sys/netinet6/nd6_protocol.h
new file mode 100644
index 00000000000..df20abcd66c
--- /dev/null
+++ b/sys/netinet6/nd6_protocol.h
@@ -0,0 +1,100 @@
+/*
+%%% portions-copyright-nrl-97
+Portions of this software are Copyright 1997-1998 by Randall Atkinson,
+Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights
+Reserved. All rights under this copyright have been assigned to the US
+Naval Research Laboratory (NRL). The NRL Copyright Notice and License
+Agreement Version 1.1 (January 17, 1995) applies to these portions of the
+software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+
+#ifndef _NETINET6_ND6_PROTOCOL_H
+#define _NETINET6_ND6_PROTOCOL_H 1
+
+#include <netinet6/icmpv6.h>
+
+#define ND6_ROUTER_SOLICITATION 133
+#define ND6_ROUTER_ADVERTISEMENT 134
+#define ND6_NEIGHBOR_SOLICITATION 135
+#define ND6_NEIGHBOR_ADVERTISEMENT 136
+#define ND6_REDIRECT 137
+
+enum nd6_option {
+ ND6_OPT_SOURCE_LINKADDR=1,
+ ND6_OPT_TARGET_LINKADDR=2,
+ ND6_OPT_PREFIX_INFORMATION=3,
+ ND6_OPT_REDIRECTED_HEADER=4,
+ ND6_OPT_MTU=5,
+ ND6_OPT_ENDOFLIST=256
+};
+
+struct nd_router_solicit { /* router solicitation */
+ struct icmpv6hdr rsol_hdr;
+};
+
+#define rsol_type rsol_hdr.icmpv6_type
+#define rsol_code rsol_hdr.icmpv6_code
+#define rsol_cksum rsol_hdr.icmpv6_cksum
+#define rsol_reserved rsol_hdr.icmpv6_data32[0]
+
+struct nd_router_advert { /* router advertisement */
+ struct icmpv6hdr radv_hdr;
+ uint32_t radv_reachable; /* reachable time */
+ uint32_t radv_retransmit; /* reachable retransmit time */
+};
+
+#define radv_type radv_hdr.icmpv6_type
+#define radv_code radv_hdr.icmpv6_code
+#define radv_cksum radv_hdr.icmpv6_cksum
+#define radv_maxhoplimit radv_hdr.icmpv6_data8[0]
+#define radv_m_o_res radv_hdr.icmpv6_data8[1]
+#define ND6_RADV_M_BIT 0x80
+#define ND6_RADV_O_BIT 0x40
+#define radv_router_lifetime radv_hdr.icmpv6_data16[1]
+
+struct nd6_nsolicitation { /* neighbor solicitation */
+ struct icmpv6hdr nsol6_hdr;
+ struct in6_addr nsol6_target;
+};
+
+struct nd6_nadvertisement { /* neighbor advertisement */
+ struct icmpv6hdr nadv6_hdr;
+ struct in6_addr nadv6_target;
+};
+
+#define nadv6_flags nadv6_hdr.icmpv6_data32[0]
+#define ND6_NADVERFLAG_ISROUTER 0x80
+#define ND6_NADVERFLAG_SOLICITED 0x40
+#define ND6_NADVERFLAG_OVERRIDE 0x20
+
+struct nd6_redirect { /* redirect */
+ struct icmpv6hdr redirect_hdr;
+ struct in6_addr redirect_target;
+ struct in6_addr redirect_destination;
+};
+
+struct nd6_opt_prefix_info { /* prefix information */
+ uint8_t opt_type;
+ uint8_t opt_length;
+ uint8_t opt_prefix_length;
+ uint8_t opt_l_a_res;
+ uint32_t opt_valid_life;
+ uint32_t opt_preferred_life;
+ uint32_t opt_reserved2;
+ struct in6_addr opt_prefix;
+};
+
+#define ND6_OPT_PI_L_BIT 0x80
+#define ND6_OPT_PI_A_BIT 0x40
+
+struct nd6_opt_mtu { /* MTU option */
+ uint8_t opt_type;
+ uint8_t opt_length;
+ uint16_t opt_reserved;
+ uint32_t opt_mtu;
+};
+
+#endif /* _NETINET6_ND6_PROTOCOL_H */
diff --git a/sys/netinet6/raw_ipv6.c b/sys/netinet6/raw_ipv6.c
new file mode 100644
index 00000000000..fb95a4858b2
--- /dev/null
+++ b/sys/netinet6/raw_ipv6.c
@@ -0,0 +1,1059 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+/*
+ * 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.7 (Berkeley) 5/15/95
+ * $Id: raw_ipv6.c,v 1.1 1999/01/06 23:26:58 deraadt Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#if __NetBSD__ || __FreeBSD__
+#include <sys/proc.h>
+#endif /* __NetBSD__ || __FreeBSD__ */
+#if __FreeBSD__
+#include <vm/vm_zone.h>
+#endif /* __FreeBSD__ */
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/ip_mroute.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+
+#include <netinet6/in6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ipv6.h>
+#include <netinet6/ipv6_var.h>
+#include <netinet6/icmpv6.h>
+
+#if __OpenBSD__ && defined(NRL_IPSEC)
+#define IPSEC 1
+#endif /* __OpenBSD__ && defined(NRL_IPSEC) */
+
+#ifdef IPSEC
+#include <sys/osdep.h>
+#include <net/netproc.h>
+#include <net/netproc_var.h>
+#endif /* IPSEC */
+
+#include <sys/debug.h>
+
+/*
+ * Globals
+ */
+
+#if __NetBSD__ || __OpenBSD__
+struct inpcbtable rawin6pcbtable;
+#else /* __NetBSD__ || __OpenBSD__ */
+struct inpcb rawin6pcb;
+#endif /* __NetBSD__ || __OpenBSD__ */
+struct sockaddr_in6 ripv6src = { sizeof(struct sockaddr_in6), AF_INET6 };
+
+/*
+ * Nominal space allocated to a raw ip socket.
+ */
+
+#define RIPV6SNDQ 8192
+#define RIPV6RCVQ 8192
+u_long ripv6_sendspace = RIPV6SNDQ;
+u_long ripv6_recvspace = RIPV6RCVQ;
+
+/*
+ * External globals
+ */
+#if __FreeBSD__
+static struct inpcbhead ri6pcb;
+static struct inpcbinfo ri6pcbinfo;
+#endif /* __FreeBSD__ */
+
+extern struct ipv6stat ipv6stat;
+
+struct mbuf *ipv6_headertocontrol(struct mbuf *m, size_t extra, int inp_flags);
+int ipv6_controltoheader(struct mbuf **m, struct mbuf *control, struct ifnet **forceifp, int *);
+
+#define RETURN_ERROR(x) { \
+ DPRINTF(EVENT, ("%s: returning %s\n", DEBUG_STATUS, #x)); \
+ return x; \
+}
+#define RETURN_VALUE(x) { \
+ DPRINTF(EVENT, ("%s: returning %d\n", DEBUG_STATUS, x)); \
+ return x; \
+}
+
+/*----------------------------------------------------------------------
+ * Raw IPv6 PCB initialization.
+ ----------------------------------------------------------------------*/
+
+void
+ripv6_init()
+{
+#if __FreeBSD__
+ LIST_INIT(&ri6pcb);
+ ri6pcbinfo.listhead = &ri6pcb;
+ /*
+ * XXX We don't use the hash list for raw IP, but it's easier
+ * to allocate a one entry hash list than it is to check all
+ * over the place for hashbase == NULL.
+ */
+ ri6pcbinfo.hashbase = hashinit(1, M_PCB, &ri6pcbinfo.hashmask);
+ ri6pcbinfo.porthashbase = hashinit(1, M_PCB, &ri6pcbinfo.porthashmask);
+ ri6pcbinfo.ipi_zone = zinit("ri6pcb", sizeof(struct inpcb),
+ nmbclusters / 4, ZONE_INTERRUPT, 0);
+#else /* __FreeBSD__ */
+#if __NetBSD__
+ in_pcbinit(&rawin6pcbtable, 1, 1);
+#else /* __NetBSD__ */
+#if __OpenBSD__
+ in_pcbinit(&rawin6pcbtable, 1);
+#else /* __OpenBSD__ */
+ rawin6pcb.inp_next = rawin6pcb.inp_prev = &rawin6pcb;
+#endif /* __OpenBSD__ */
+#endif /* __NetBSD__ */
+#endif /* __FreeBSD__ */
+}
+
+/* At the point where this function gets called, we don't know the nexthdr of
+ the current header to be processed, only its offset. So we have to go find
+ it the hard way. In the case where there's no chained headers, this is not
+ really painful.
+
+ The good news is that all fields have been sanity checked.
+
+ Assumes m has already been pulled up by extra. -cmetz
+*/
+#if __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__
+static __inline__ int ipv6_findnexthdr(struct mbuf *m, size_t extra)
+#else /* __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ */
+static int ipv6_findnexthdr(struct mbuf *m, size_t extra)
+#endif /* __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ */
+{
+ caddr_t p = mtod(m, caddr_t);
+ int nexthdr = IPPROTO_IPV6;
+ unsigned int hl;
+
+ do {
+ switch(nexthdr) {
+ case IPPROTO_IPV6:
+ hl = sizeof(struct ipv6);
+
+ if ((extra -= hl) < 0)
+ return -1;
+
+ nexthdr = ((struct ipv6 *)p)->ipv6_nexthdr;
+ break;
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ if (extra < sizeof(struct ipv6_opthdr))
+ return -1;
+
+ hl = sizeof(struct ipv6_opthdr) +
+ ((struct ipv6_opthdr *)p)->oh_extlen << 3;
+
+ if ((extra -= hl) < 0)
+ return -1;
+
+ nexthdr = ((struct ipv6_opthdr *)p)->oh_nexthdr;
+ break;
+ case IPPROTO_ROUTING:
+ if (extra < sizeof(struct ipv6_srcroute0))
+ return -1;
+
+ hl = sizeof(struct ipv6_srcroute0) +
+ ((struct ipv6_srcroute0 *)p)->i6sr_len << 3;
+
+ if ((extra -= hl) < 0)
+ return -1;
+
+ nexthdr = ((struct ipv6_srcroute0 *)p)->i6sr_nexthdr;
+ break;
+#ifdef IPSEC
+ case IPPROTO_AH:
+ if (extra < sizeof(struct ipv6_srcroute0))
+ return -1;
+
+ hl = sizeof(struct ipv6_srcroute0) +
+ ((struct ipv6_srcroute0 *)p)->i6sr_len << 3;
+
+ if ((extra -= hl) < 0)
+ return -1;
+
+ nexthdr = ((struct ipv6_srcroute0 *)p)->i6sr_nexthdr;
+ break;
+#endif /* IPSEC */
+ default:
+ return -1;
+ }
+ p += hl;
+ } while(extra > 0);
+
+ return nexthdr;
+}
+
+/*----------------------------------------------------------------------
+ * If no HLP's are found for an IPv6 datagram, this routine is called.
+ ----------------------------------------------------------------------*/
+void
+#if __OpenBSD__
+ripv6_input(struct mbuf *m, ...)
+#else /* __OpenBSD__ */
+ripv6_input(m,extra)
+ struct mbuf *m;
+ int extra;
+#endif /* __OpenBSD__ */
+{
+ register struct ipv6 *ipv6 = mtod(m, struct ipv6 *); /* Will have been
+ pulled up by
+ ipv6_input(). */
+ register struct inpcb *inp;
+ int nexthdr, icmpv6type;
+ int foundone = 0;
+ struct mbuf *m2 = NULL, *opts = NULL;
+ struct sockaddr_in6 srcsa;
+#ifdef IPSEC
+ struct sockaddr_in6 dstsa;
+#endif /* IPSEC */
+#if __OpenBSD__
+ int extra;
+ va_list ap;
+
+ va_start(ap, m);
+ extra = va_arg(ap, int);
+ va_end(ap);
+#endif /* __OpenBSD__ */
+
+ DPRINTF(FINISHED, ("ripv6_input(m=%08x, extra=%d)\n", OSDEP_PCAST(m), extra));
+ DP(FINISHED, m->m_pkthdr.len, d);
+
+ DDO(FINISHED,printf("In ripv6_input(), header is:\n");dump_mchain(m));
+ DPRINTF(EVENT, ("In ripv6_input()\n"));
+ DPRINTF(EVENT, ("Header is: "));
+ DDO(GROSSEVENT, dump_ipv6(ipv6));
+
+ bzero(&srcsa, sizeof(struct sockaddr_in6));
+ srcsa.sin6_family = AF_INET6;
+ srcsa.sin6_len = sizeof(struct sockaddr_in6);
+ srcsa.sin6_addr = ipv6->ipv6_src;
+
+#if IPSEC
+ bzero(&dstsa, sizeof(struct sockaddr_in6));
+ dstsa.sin6_family = AF_INET6;
+ dstsa.sin6_len = sizeof(struct sockaddr_in6);
+ dstsa.sin6_addr = ipv6->ipv6_dst;
+#endif /* IPSEC */
+
+#if 0
+ /* Will be done already by the previous input functions */
+ if (m->m_len < extra)) {
+ if (!(m = m_pullup2(m, extra)))
+ return;
+ ipv6 = mtod(m, struct ipv6 *);
+ }
+#endif /* 0 */
+
+ if ((nexthdr = ipv6_findnexthdr(m, extra)) < 0) {
+ DPRINTF(ERROR, ("ripv6_input: ipv6_findnexthdr failed\n"));
+ goto ret;
+ }
+
+ DP(FINISHED, nexthdr, d);
+
+ if (nexthdr == IPPROTO_ICMPV6) {
+ if (m->m_len < extra + sizeof(struct icmpv6hdr)) {
+ if (!(m = m_pullup2(m, extra + sizeof(struct icmpv6hdr)))) {
+ DPRINTF(ERROR, ("ripv6_input: m_pullup2 failed\n"));
+ goto ret;
+ }
+ ipv6 = mtod(m, struct ipv6 *);
+ }
+ icmpv6type = ((struct icmpv6hdr *)(mtod(m, caddr_t) + extra))->icmpv6_type;
+ } else
+ icmpv6type = -1;
+
+ /*
+ * Locate raw PCB for incoming datagram.
+ */
+#if __FreeBSD__
+ for (inp = ri6pcb.lh_first; inp != NULL; inp = inp->inp_list.le_next) {
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (inp = rawin6pcbtable.inpt_queue.cqh_first;
+ inp != (struct inpcb *)&rawin6pcbtable.inpt_queue;
+ inp = inp->inp_queue.cqe_next) {
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (inp = rawin6pcb.inp_next; inp != &rawin6pcb; inp = inp->inp_next) {
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ if (inp->inp_ipv6.ipv6_nexthdr && inp->inp_ipv6.ipv6_nexthdr != nexthdr)
+ continue;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) &&
+ !IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, &ipv6->ipv6_dst))
+ continue;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6) &&
+ !IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, &ipv6->ipv6_src))
+ continue;
+ if ((icmpv6type >= 0) &&
+ ICMPV6_FILTER_WILLBLOCK(icmpv6type, &inp->inp_filter))
+ continue;
+
+ DPRINTF(IDL_EVENT, ("Found a raw pcb (>1)\n"));
+ foundone = 1;
+
+#ifdef IPSEC
+ /* Perform input-side policy check. Drop packet if policy says to drop it.
+
+ Note: For ICMPv6 packets, we also checked policy in ipv6_icmp_input().
+
+ XXX - state arg should NOT be NULL, it should be the netproc state
+ carried up the stack - cmetz */
+ if (!netproc_inputpolicy(NULL, (struct sockaddr *)&srcsa,
+ (struct sockaddr *)&dstsa, nexthdr, m, NULL, NULL))
+#endif /* IPSEC */
+
+ DP(FINISHED, m->m_pkthdr.len, d);
+ /* Note the inefficiency here; this is a consequence of the interfaces of
+ the functions being used. The raw code is not performance critical
+ enough to require an immediate fix. - cmetz */
+ if ((m2 = m_copym(m, 0, (int)M_COPYALL, M_DONTWAIT))) {
+ m_adj(m2, extra);
+ DP(FINISHED, m2->m_pkthdr.len, d);
+ if (inp->inp_flags & INP_CONTROLOPTS)
+ opts = ipv6_headertocontrol(m, extra, inp->inp_flags);
+ else
+ opts = NULL;
+ if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&srcsa, m2,
+ opts)) {
+ sorwakeup(inp->inp_socket);
+ } else {
+ m_freem(m2);
+ };
+ };
+ };
+
+ if (!foundone) {
+ /*
+ * We should send an ICMPv6 protocol unreachable here,
+ * though original UCB 4.4-lite BSD's IPv4 does not do so.
+ */
+ ipv6stat.ips_noproto++;
+ ipv6stat.ips_delivered--;
+ }
+
+ret:
+ if (m)
+ m_freem(m);
+
+ DPRINTF(FINISHED, ("ripv6_input\n"));
+}
+
+/*----------------------------------------------------------------------
+ * Output function for raw IPv6. Called from ripv6_usrreq(), and
+ * ipv6_icmp_usrreq().
+ ----------------------------------------------------------------------*/
+
+#if __OpenBSD__
+int ripv6_output(struct mbuf *m, ...)
+#else /* __OpenBSD__ */
+int
+ripv6_output(m, so, dst, control)
+ struct mbuf *m;
+ struct socket *so;
+ struct in6_addr *dst;
+ struct mbuf *control;
+#endif /* __OpenBSD__ */
+{
+ register struct ipv6 *ipv6;
+ register struct inpcb *inp;
+ int flags;
+ struct ifnet *forceif = NULL;
+#if __OpenBSD__
+ va_list ap;
+ struct socket *so;
+ struct in6_addr *dst;
+ struct mbuf *control;
+
+ va_start(ap, m);
+ so = va_arg(ap, struct socket *);
+ dst = va_arg(ap, struct in6_addr *);
+ control = va_arg(ap, struct mbuf *);
+ va_end(ap);
+#endif /* __OpenBSD__ */
+
+ inp = sotoinpcb(so);
+ flags = (so->so_options & SO_DONTROUTE);
+
+ if (inp->inp_flags & INP_HDRINCL)
+ {
+ flags |= IPV6_RAWOUTPUT;
+ ipv6stat.ips_rawout++;
+ /* Maybe m_pullup() ipv6 header here for ipv6_output(), which
+ expects it to be contiguous. */
+ }
+ else
+ {
+ M_PREPEND(m, sizeof(struct ipv6), M_WAIT);
+ ipv6 = mtod(m, struct ipv6 *);
+ ipv6->ipv6_nexthdr = inp->inp_ipv6.ipv6_nexthdr;
+ ipv6->ipv6_hoplimit = MAXHOPLIMIT;
+ ipv6->ipv6_src = inp->inp_laddr6;
+ ipv6->ipv6_dst = *dst;
+ ipv6->ipv6_versfl = 0; /* Or possibly user flow label, in host order. */
+ /*
+ * Question: How do I handle options?
+ *
+ * Answer: I put them in here, but how?
+ */
+ }
+
+ {
+ int payload = sizeof(struct ipv6);
+ int nexthdr = mtod(m, struct ipv6 *)->ipv6_nexthdr;
+ int error;
+
+ if (control)
+ if ((error = ipv6_controltoheader(&m, control, &forceif, &payload))) {
+ m_freem(m);
+ return error;
+ }
+
+ if (inp->inp_csumoffset >= 0) {
+ uint16_t *csum;
+
+ if (!(m = m_pullup2(m, payload + inp->inp_csumoffset))) {
+ DPRINTF(IDL_ERROR, ("ripv6_output: m_pullup2(m, %d) failed\n", payload + inp->inp_csumoffset));
+ m_freem(m);
+ return ENOBUFS;
+ };
+
+ csum = (uint16_t *)(mtod(m, uint8_t *) + payload + inp->inp_csumoffset);
+
+ *csum = 0;
+ *csum = in6_cksum(m, nexthdr, m->m_pkthdr.len - payload, payload);
+ };
+ };
+
+ return ipv6_output(m,&inp->inp_route6,flags,inp->inp_moptions6, forceif, so);
+}
+
+/*----------------------------------------------------------------------
+ * Handles [gs]etsockopt() calls.
+ ----------------------------------------------------------------------*/
+
+#if __FreeBSD__
+int ripv6_ctloutput(struct socket *so, struct sockopt *sopt)
+{
+ register struct inpcb *inp = sotoinpcb(so);
+ int op;
+ int level;
+ int optname;
+ int optval;
+
+ DPRINTF(FINISHED, ("ripv6_ctloutput(so=%08x, sopt=%08x)\n",
+ OSDEP_PCAST(so), OSDEP_PCAST(sopt)));
+
+ switch(sopt->sopt_dir) {
+ case SOPT_GET:
+ op = PRCO_GETOPT;
+ break;
+ case SOPT_SET:
+ op = PRCO_SETOPT;
+ break;
+ default:
+ RETURN_ERROR(EINVAL);
+ };
+
+ level = sopt->sopt_level;
+ optname = sopt->sopt_name;
+#else /* __FreeBSD__ */
+int
+ripv6_ctloutput (op, so, level, optname, m)
+ int op;
+ struct socket *so;
+ int level, optname;
+ struct mbuf **m;
+{
+ register struct inpcb *inp = sotoinpcb(so);
+
+ DPRINTF(FINISHED, ("ripv6_ctloutput(op=%x,so,level=%x,optname=%x,m)\n", op, level, optname));
+#endif /* __FreeBSD__ */
+
+ if ((level != IPPROTO_IP) && (level != IPPROTO_IPV6) && (level != IPPROTO_ICMPV6)) {
+#if !__FreeBSD__
+ if (op == PRCO_SETOPT && *m)
+ (void)m_free(*m);
+#endif /* !__FreeBSD__ */
+ RETURN_ERROR(EINVAL);
+ }
+
+ switch (optname) {
+ case IPV6_CHECKSUM:
+ if (op == PRCO_SETOPT || op == PRCO_GETOPT) {
+#if __FreeBSD__
+ if (!sopt->sopt_val || (sopt->sopt_valsize != sizeof(int)))
+ RETURN_ERROR(EINVAL);
+ if (op == PRCO_SETOPT) {
+ int error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int));
+ if (error)
+ RETURN_VALUE(error);
+ inp->inp_csumoffset = optval;
+
+ return 0;
+ } else
+ return sooptcopyout(sopt, &inp->inp_csumoffset, sizeof(int));
+#else /* __FreeBSD__ */
+ if (!m || !*m || (*m)->m_len != sizeof(int))
+ RETURN_ERROR(EINVAL);
+ if (op == PRCO_SETOPT) {
+ inp->inp_csumoffset = *(mtod(*m, int *));
+ m_freem(*m);
+ } else {
+ (*m)->m_len = sizeof(int);
+ *(mtod(*m, int *)) = inp->inp_csumoffset;
+ };
+#endif /* __FreeBSD__ */
+ return 0;
+ };
+ break;
+ case ICMPV6_FILTER:
+ if (op == PRCO_SETOPT || op == PRCO_GETOPT) {
+#if __FreeBSD__
+ if (!sopt->sopt_val || (sopt->sopt_valsize !=
+ sizeof(struct icmpv6_filter)))
+ RETURN_ERROR(EINVAL);
+ if (op == PRCO_SETOPT) {
+ struct icmpv6_filter icmpv6_filter;
+ int error = sooptcopyin(sopt, &icmpv6_filter,
+ sizeof(struct icmpv6_filter), sizeof(struct icmpv6_filter));
+ if (error)
+ return error;
+
+ inp->inp_filter = icmpv6_filter;
+
+ return 0;
+ } else
+ return sooptcopyout(sopt, &inp->inp_filter,
+ sizeof(struct icmpv6_filter));
+#else /* __FreeBSD__ */
+ if (!m || !*m || (*m)->m_len != sizeof(struct icmpv6_filter))
+ RETURN_ERROR(EINVAL);
+ if (op == PRCO_SETOPT) {
+ inp->inp_filter = *mtod(*m, struct icmpv6_filter *);
+ m_freem(*m);
+ } else {
+ (*m)->m_len = sizeof(struct icmpv6_filter);
+ *mtod(*m, struct icmpv6_filter *) = inp->inp_filter;
+ };
+ return 0;
+#endif /* __FreeBSD__ */
+ };
+ break;
+
+/* Should this be obsoleted? */
+ case IP_HDRINCL:
+ if (op == PRCO_SETOPT || op == PRCO_GETOPT)
+ {
+#if __FreeBSD__
+ if (!sopt->sopt_val || (sopt->sopt_valsize != sizeof(int)))
+ RETURN_ERROR(EINVAL);
+ if (op == PRCO_SETOPT) {
+ int error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int));
+ if (error)
+ return error;
+
+ if (optval)
+ inp->inp_flags |= INP_HDRINCL;
+ else
+ inp->inp_flags &= ~INP_HDRINCL;
+
+ return 0;
+ } else {
+ optval = (inp->inp_flags & INP_HDRINCL) ? 1 : 0;
+ return sooptcopyout(sopt, &optval, sizeof(int));
+ };
+#else /* __FreeBSD__ */
+ if (m == 0 || *m == 0 || (*m)->m_len != sizeof(int))
+ RETURN_ERROR(EINVAL);
+ if (op == PRCO_SETOPT)
+ {
+ if (*mtod(*m, int *))
+ inp->inp_flags |= INP_HDRINCL;
+ else inp->inp_flags &= ~INP_HDRINCL;
+ m_free(*m);
+ }
+ else
+ {
+ (*m)->m_len = sizeof(int);
+ *(mtod(*m, int *)) = (inp->inp_flags & INP_HDRINCL) ? 1 : 0;
+ }
+ return 0;
+#endif /* __FreeBSD__ */
+ }
+ break;
+
+#ifdef MRT_INIT
+ default:
+ if (optname >= MRT_INIT) {
+#else /* MRT_INIT */
+ case DVMRP_INIT:
+ case DVMRP_DONE:
+ case DVMRP_ADD_VIF:
+ case DVMRP_DEL_VIF:
+ case DVMRP_ADD_LGRP:
+ case DVMRP_DEL_LGRP:
+ case DVMRP_ADD_MRT:
+ case DVMRP_DEL_MRT:
+ {
+#endif /* MRT_INIT */
+#ifdef MROUTING
+/* Be careful here! */
+/* if (op == PRCO_SETOPT)
+ {
+ error = ipv6_mrouter_cmd(optname, so, *m);
+ if (*m)
+ (void)m_free(*m);
+ }
+ else error = EINVAL;
+ return (error);*/
+ RETURN_ERROR(EOPNOTSUPP);
+#else /* MROUTING */
+#if !__FreeBSD__
+ if (op == PRCO_SETOPT && *m)
+ (void)m_free(*m);
+#endif /* !__FreeBSD__ */
+ RETURN_ERROR(EOPNOTSUPP);
+#endif /* MROUTING */
+ };
+ }
+#if __FreeBSD__
+ return ipv6_ctloutput(so, sopt);
+#else /* __FreeBSD__ */
+ return ipv6_ctloutput(op, so, level, optname, m);
+#endif /* __FreeBSD__ */
+}
+
+#if __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ && !__FreeBSD__
+#define MAYBESTATIC static
+#define MAYBEINLINE __inline__
+#else /* __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ && !__FreeBSD__ */
+#define MAYBESTATIC
+#define MAYBEINLINE
+#endif /* __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ && !__FreeBSD__ */
+
+#if __NetBSD__ || __FreeBSD__
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_attach(struct socket *so, int proto,
+ struct proc *p)
+#else /* __NetBSD__ || __FreeBSD__ */
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_attach(struct socket *so, int proto)
+#endif /* __NetBSD__ || __FreeBSD__ */
+{
+ register struct inpcb *inp = sotoinpcb(so);
+ register int error = 0;
+
+ if (inp)
+ panic("ripv6_attach - Already got PCB");
+
+#if __NetBSD__ || __FreeBSD__
+ if (p == 0 || (error = suser(p->p_ucred, &p->p_acflag)))
+#else /* __NetBSD__ || __FreeBSD__ */
+ if ((so->so_state & SS_PRIV) == 0)
+#endif /* __NetBSD__ || __FreeBSD__ */
+ {
+ error = EACCES;
+ return error;
+ }
+ if ((error = soreserve(so, ripv6_sendspace, ripv6_recvspace)) ||
+
+#if __FreeBSD__
+ (error = in_pcballoc(so, &ri6pcbinfo, p)))
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ (error = in_pcballoc(so, &rawin6pcbtable)))
+#else /* __NetBSD__ || __OpenBSD__ */
+ (error = in_pcballoc(so, &rawin6pcb)))
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+
+ return error;
+
+ inp = sotoinpcb(so);
+#ifdef __alpha__
+ inp->inp_ipv6.ipv6_nexthdr = (u_long)proto; /*nam; Nam contains protocol
+ type, apparently. */
+#else
+ inp->inp_ipv6.ipv6_nexthdr = (int)proto; /*nam; Nam contains protocol
+ type, apparently. */
+#endif
+ if (inp->inp_ipv6.ipv6_nexthdr == IPPROTO_ICMPV6)
+ inp->inp_csumoffset = 2;
+ return error;
+}
+
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_detach(struct socket *so)
+{
+ register struct inpcb *inp = sotoinpcb(so);
+
+ if (inp == 0)
+ panic("ripv6_detach");
+#ifdef MROUTING
+ /* More MROUTING stuff. */
+#endif
+ in_pcbdetach(inp);
+ return 0;
+}
+
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_abort(struct socket *so)
+{
+ soisdisconnected(so);
+ return ripv6_usrreq_detach(so);
+}
+
+static MAYBEINLINE int ripv6_usrreq_disconnect(struct socket *so)
+{
+ if ((so->so_state & SS_ISCONNECTED) == 0)
+ return ENOTCONN;
+ return ripv6_usrreq_abort(so);
+}
+
+#if __NetBSD__ || __FreeBSD__
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_bind(struct socket *so,
+ struct sockaddr *nam, struct proc *p)
+#else /* __NetBSD__ || __FreeBSD__ */
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_bind(struct socket *so,
+ struct sockaddr *nam)
+#endif /* __NetBSD__ || __FreeBSD__ */
+{
+ register struct inpcb *inp = sotoinpcb(so);
+ register struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
+
+ /* 'ifnet' is declared in one of the net/ header files. */
+#if __NetBSD__ || __OpenBSD__ || __FreeBSD__
+ if ((ifnet.tqh_first == 0) ||
+#else /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ */
+ if ((ifnet == 0) ||
+#endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ */
+ (addr->sin6_family != AF_INET6) || /* I only allow AF_INET6 */
+ (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) &&
+ ifa_ifwithaddr((struct sockaddr *)addr) == 0 ) )
+
+ return EADDRNOTAVAIL;
+
+ inp->inp_laddr6 = addr->sin6_addr;
+ return 0;
+}
+
+#if __NetBSD__ || __FreeBSD__
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_connect(struct socket *so,
+ struct sockaddr *nam, struct proc *p)
+#else /* __NetBSD__ || __FreeBSD__ */
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_connect(struct socket *so,
+ struct sockaddr *nam)
+#endif /* __NetBSD__ || __FreeBSD__ */
+{
+ register struct inpcb *inp = sotoinpcb(so);
+ register struct sockaddr_in6 *addr = (struct sockaddr_in6 *) nam;
+
+ if (addr->sin6_family != AF_INET6)
+ return EAFNOSUPPORT;
+
+#if __NetBSD__ || __FreeBSD__ || __OpenBSD__
+ if (ifnet.tqh_first == 0) {
+#else /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */
+ if (ifnet == 0) {
+#endif /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */
+ return EADDRNOTAVAIL; /* This is a weird way to say there
+ are no interfaces, no? */
+ }
+
+ inp->inp_faddr6 = addr->sin6_addr; /* Will structure assignment
+ work with this compiler? */
+ soisconnected(so);
+ return 0;
+}
+
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_shutdown(struct socket *so)
+{
+ socantsendmore(so);
+ return 0;
+}
+
+#if __NetBSD__ || __FreeBSD__
+/*
+ * Note that flags and p are not used, but required by protosw in
+ * FreeBSD.
+ */
+int ripv6_usrreq_send(struct socket *so, int flags, struct mbuf *m,
+ struct sockaddr *addr, struct mbuf *control,
+ struct proc *p)
+#else /* __NetBSD__ || __FreeBSD__ */
+int ripv6_usrreq_send(struct socket *so, int flags, struct mbuf *m,
+ struct sockaddr *addr, struct mbuf *control)
+#endif /* __NetBSD__ || __FreeBSD__ */
+{
+ register struct inpcb *inp = sotoinpcb(so);
+ register int error = 0;
+ struct in6_addr *dst;
+
+ if (inp == 0) {
+ m_freem(m);
+ return EINVAL;
+ }
+
+ /*
+ * Check "connected" status, and if there is a supplied destination
+ * address.
+ */
+ if (so->so_state & SS_ISCONNECTED)
+ {
+ if (addr)
+ return EISCONN;
+
+ dst = &(inp->inp_faddr6);
+ }
+ else
+ {
+ if (addr == NULL)
+ return ENOTCONN;
+
+ dst = &((struct sockaddr_in6 *)addr)->sin6_addr;
+ }
+
+ error = ripv6_output(m,so,dst,control);
+ /* m = NULL; */
+ return error;
+}
+
+#if __NetBSD__ || __FreeBSD__
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_control(struct socket *so, u_long cmd,
+ caddr_t data, struct ifnet *ifp, struct proc *p)
+#else /* __NetBSD__ || __FreeBSD__ */
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_control(struct socket *so, int cmd,
+ caddr_t data, struct ifnet *ifp)
+#endif /* __NetBSD__ || __FreeBSD__ */
+{
+/* Notice that IPv4 raw sockets don't pass PRU_CONTROL. I wonder
+ if they panic as well? */
+#if __NetBSD__ || __FreeBSD__
+ return in6_control(so, cmd, data, ifp, 0, p);
+#else /* __NetBSD__ || __FreeBSD__ */
+ return in6_control(so, cmd, data, ifp, 0);
+#endif /* __NetBSD__ || __FreeBSD__ */
+}
+
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_sense(struct socket *so,
+ struct stat *sb)
+{
+ /* services stat(2) call. */
+ return 0;
+}
+
+#if __FreeBSD__
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_sockaddr(struct socket *so,
+ struct sockaddr **nam)
+#else /* __FreeBSD__ */
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_sockaddr(struct socket *so,
+ struct mbuf *nam)
+#endif /* __FreeBSD__ */
+{
+ register struct inpcb *inp = sotoinpcb(so);
+ return in6_setsockaddr(inp, nam);
+}
+
+#if __FreeBSD__
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_peeraddr(struct socket *so,
+ struct sockaddr **nam)
+#else /* __FreeBSD__ */
+MAYBESTATIC MAYBEINLINE int ripv6_usrreq_peeraddr(struct socket *so,
+ struct mbuf *nam)
+#endif /* __FreeBSD__ */
+{
+ register struct inpcb *inp = sotoinpcb(so);
+ return in6_setpeeraddr(inp, nam);
+}
+
+#if __FreeBSD__
+struct pr_usrreqs ripv6_usrreqs = {
+ ripv6_usrreq_abort, pru_accept_notsupp, ripv6_usrreq_attach,
+ ripv6_usrreq_bind, ripv6_usrreq_connect, pru_connect2_notsupp,
+ ripv6_usrreq_control, ripv6_usrreq_detach, ripv6_usrreq_detach,
+ pru_listen_notsupp, ripv6_usrreq_peeraddr, pru_rcvd_notsupp,
+ pru_rcvoob_notsupp, ripv6_usrreq_send, ripv6_usrreq_sense,
+ ripv6_usrreq_shutdown, ripv6_usrreq_sockaddr, sosend, soreceive, sopoll
+};
+#endif /* __FreeBSD__ */
+
+/*----------------------------------------------------------------------
+ * Handles PRU_* for raw IPv6 sockets.
+ ----------------------------------------------------------------------*/
+#if !__FreeBSD__
+int
+#if __NetBSD__
+ripv6_usrreq(so, req, m, nam, control, p)
+#else /* __NetBSD__ */
+ripv6_usrreq(so, req, m, nam, control)
+#endif /* __NetBSD__ */
+ struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+#if __NetBSD__
+ struct proc *p;
+#endif /* __NetBSD__ */
+{
+ register int error = 0;
+
+ DPRINTF(IDL_EVENT, ("ripv6_usrreq(so, req, m, nam, control)\n"));
+
+#ifdef MROUTING
+ /*
+ * Ummm, like, multicast routing stuff goes here, huh huh huh.
+ *
+ * Seriously, this would be for user-level multicast routing daemons. With
+ * multicast being a requirement for IPv6, code like what might go here
+ * may go away.
+ */
+#endif
+
+ switch (req)
+ {
+ case PRU_ATTACH:
+#if __NetBSD__
+ error = ripv6_usrreq_attach(so, 0, p); /* XXX */
+#else /* __NetBSD__ */
+ error = ripv6_usrreq_attach(so, 0); /* XXX */
+#endif /* __NetBSD__ */
+ break;
+ case PRU_DISCONNECT:
+ error = ripv6_usrreq_disconnect(so);
+ break;
+ /* NOT */
+ /* FALLTHROUGH */
+ case PRU_ABORT:
+ error = ripv6_usrreq_abort(so);
+ break;
+ /* NOT */
+ /* FALLTHROUGH */
+ case PRU_DETACH:
+ error = ripv6_usrreq_detach(so);
+ break;
+ case PRU_BIND:
+ if (nam->m_len != sizeof(struct sockaddr_in6))
+ return EINVAL;
+ /*
+ * Be strict regarding sockaddr_in6 fields.
+ */
+#if __NetBSD__
+ error = ripv6_usrreq_bind(so, mtod(nam, struct sockaddr *), p);
+#else /* __NetBSD__ */
+ error = ripv6_usrreq_bind(so, mtod(nam, struct sockaddr *));
+#endif /* __NetBSD__ */
+ break;
+ case PRU_CONNECT:
+ /*
+ * Be strict regarding sockaddr_in6 fields.
+ */
+ if (nam->m_len != sizeof(struct sockaddr_in6))
+ return EINVAL;
+#if __NetBSD__
+ error = ripv6_usrreq_connect(so, mtod(nam, struct sockaddr *), p);
+#else /* __NetBSD__ */
+ error = ripv6_usrreq_connect(so, mtod(nam, struct sockaddr *));
+#endif /* __NetBSD__ */
+ break;
+ case PRU_SHUTDOWN:
+ error = ripv6_usrreq_shutdown(so);
+ break;
+ case PRU_SEND:
+ /*
+ * Be strict regarding sockaddr_in6 fields.
+ */
+ if (nam->m_len != sizeof(struct sockaddr_in6))
+ return EINVAL;
+#if __NetBSD__
+ error = ripv6_usrreq_send(so, 0, m, mtod(nam, struct sockaddr *), control, p);
+#else /* __NetBSD__ */
+ error = ripv6_usrreq_send(so, 0, m, mtod(nam, struct sockaddr *), control);
+#endif /* __NetBSD__ */
+ m = NULL;
+ break;
+ case PRU_CONTROL:
+#if __NetBSD__
+ return ripv6_usrreq_control(so, (u_long)m, (caddr_t) nam,
+ (struct ifnet *) control, p);
+#else /* __NetBSD__ */
+ return ripv6_usrreq_control(so, (int)m, (caddr_t) nam,
+ (struct ifnet *) control);
+#endif /* __NetBSD__ */
+ case PRU_SENSE:
+ return ripv6_usrreq_sense(so, NULL); /* XXX */
+ case PRU_CONNECT2:
+ case PRU_RCVOOB:
+ case PRU_LISTEN:
+ case PRU_SENDOOB:
+ case PRU_RCVD:
+ case PRU_ACCEPT:
+ error = EOPNOTSUPP;
+ break;
+ case PRU_SOCKADDR:
+ error = ripv6_usrreq_sockaddr(so, nam);
+ break;
+ case PRU_PEERADDR:
+ error = ripv6_usrreq_peeraddr(so, nam);
+ break;
+ default:
+ panic ("ripv6_usrreq - unknown req\n");
+ }
+ if (m != NULL)
+ m_freem(m);
+ return error;
+}
+#endif /* !__FreeBSD__ */
diff --git a/sys/netinet6/tcpipv6.h b/sys/netinet6/tcpipv6.h
new file mode 100644
index 00000000000..57ca9505d21
--- /dev/null
+++ b/sys/netinet6/tcpipv6.h
@@ -0,0 +1,36 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+#ifndef _NETINET6_TCPIPV6_H
+#define _NETINET6_TCPIPV6_H 1
+
+#include <netinet6/ipv6.h>
+#include <netinet/tcp.h>
+
+struct tcpipv6hdr {
+ struct ipv6 ti6_i;
+ struct tcphdr ti6_t;
+};
+
+#define ti6_src ti6_i.ipv6_src
+#define ti6_dst ti6_i.ipv6_dst
+#define ti6_sport ti6_t.th_sport
+#define ti6_dport ti6_t.th_dport
+#define ti6_seq ti6_t.th_seq
+#define ti6_ack ti6_t.th_ack
+#define ti6_x2 ti6_t.th_x2
+#define ti6_off ti6_t.th_off
+#define ti6_flags ti6_t.th_flags
+#define ti6_win ti6_t.th_win
+#define ti6_sum ti6_t.th_sum
+#define ti6_urp ti6_t.th_urp
+
+#endif /* _NETINET6_TCPIPV6_H */