summaryrefslogtreecommitdiffstats
path: root/sys/netinet6/in6_src.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6/in6_src.c')
-rw-r--r--sys/netinet6/in6_src.c346
1 files changed, 346 insertions, 0 deletions
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c
new file mode 100644
index 00000000000..2f222592ca4
--- /dev/null
+++ b/sys/netinet6/in6_src.c
@@ -0,0 +1,346 @@
+/* $OpenBSD: in6_src.c,v 1.1 1999/12/08 06:50:21 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94
+ */
+
+#ifdef __NetBSD__ /*XXX*/
+#include "opt_ipsec.h"
+#endif
+
+#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/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_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6.h>
+#if 0
+#include <netinet6/in6_pcb.h>
+#endif
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+
+#ifndef __bsdi__
+#include "loop.h"
+#endif
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+extern struct ifnet loif[NLOOP];
+#endif
+
+/*
+ * Return an IPv6 address, which is the most appropriate for given
+ * destination and user specified options.
+ * If necessary, this function lookups the routing table and return
+ * an entry to the caller for later use.
+ */
+struct in6_addr *
+in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
+ struct sockaddr_in6 *dstsock;
+ struct ip6_pktopts *opts;
+ struct ip6_moptions *mopts;
+ struct route_in6 *ro;
+ struct in6_addr *laddr;
+ int *errorp;
+{
+ struct in6_addr *dst;
+ struct in6_ifaddr *ia6 = 0;
+ struct in6_pktinfo *pi = NULL;
+
+ dst = &dstsock->sin6_addr;
+ *errorp = 0;
+
+ /*
+ * If the source address is explicitly specified by the caller,
+ * use it.
+ */
+ if (opts && (pi = opts->ip6po_pktinfo) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr))
+ return(&pi->ipi6_addr);
+
+ /*
+ * If the source address is not specified but the socket(if any)
+ * is already bound, use the bound address.
+ */
+ if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr))
+ return(laddr);
+
+ /*
+ * If the caller doesn't specify the source address but
+ * the outgoing interface, use an address associated with
+ * the interface.
+ */
+ if (pi && pi->ipi6_ifindex) {
+ /* XXX boundary check is assumed to be already done. */
+ ia6 = in6_ifawithscope(ifindex2ifnet[pi->ipi6_ifindex],
+ dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ /*
+ * If the destination address is a link-local unicast address or
+ * a multicast address, and if the outgoing interface is specified
+ * by the sin6_scope_id filed, use an address associated with the
+ * interface.
+ * XXX: We're now trying to define more specific semantics of
+ * sin6_scope_id field, so this part will be rewritten in
+ * the near future.
+ */
+ if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MULTICAST(dst)) &&
+ dstsock->sin6_scope_id) {
+ /*
+ * I'm not sure if boundary check for scope_id is done
+ * somewhere...
+ */
+ if (dstsock->sin6_scope_id < 0 ||
+ if_index < dstsock->sin6_scope_id) {
+ *errorp = ENXIO; /* XXX: better error? */
+ return(0);
+ }
+ ia6 = in6_ifawithscope(ifindex2ifnet[dstsock->sin6_scope_id],
+ dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ /*
+ * If the destination address is a multicast address and
+ * the outgoing interface for the address is specified
+ * by the caller, use an address associated with the interface.
+ * There is a sanity check here; if the destination has node-local
+ * scope, the outgoing interfacde should be a loopback address.
+ * Even if the outgoing interface is not specified, we also
+ * choose a loopback interface as the outgoing interface.
+ */
+ if (IN6_IS_ADDR_MULTICAST(dst)) {
+ struct ifnet *ifp = mopts ? mopts->im6o_multicast_ifp : NULL;
+#ifdef __bsdi__
+#if _BSDI_VERSION >= 199802
+ extern struct ifnet *loifp;
+#else
+ extern struct ifnet loif;
+ struct ifnet *loifp = &loif;
+#endif
+#endif
+
+ if (ifp == NULL && IN6_IS_ADDR_MC_NODELOCAL(dst)) {
+#ifdef __bsdi__
+ ifp = loifp;
+#else
+ ifp = &loif[0];
+#endif
+ }
+
+ if (ifp) {
+ ia6 = in6_ifawithscope(ifp, dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+ }
+
+ /*
+ * If the next hop address for the packet is specified
+ * by caller, use an address associated with the route
+ * to the next hop.
+ */
+ {
+ struct sockaddr_in6 *sin6_next;
+ struct rtentry *rt;
+
+ if (opts && opts->ip6po_nexthop) {
+ sin6_next = satosin6(opts->ip6po_nexthop);
+ rt = nd6_lookup(&sin6_next->sin6_addr, 1, NULL);
+ if (rt) {
+ ia6 = in6_ifawithscope(rt->rt_ifp, dst);
+ if (ia6 == 0)
+ ia6 = ifatoia6(rt->rt_ifa);
+ }
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+ }
+
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+ if (ro) {
+ if (ro->ro_rt &&
+ !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr, dst)) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if (ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0) {
+ /* No route yet, so try to acquire one */
+ bzero(&ro->ro_dst, sizeof(struct sockaddr_in6));
+ ro->ro_dst.sin6_family = AF_INET6;
+ ro->ro_dst.sin6_len = sizeof(struct sockaddr_in6);
+ ro->ro_dst.sin6_addr = *dst;
+ if (IN6_IS_ADDR_MULTICAST(dst)) {
+#ifdef __FreeBSD__
+ ro->ro_rt = rtalloc1(&((struct route *)ro)
+ ->ro_dst, 0, 0UL);
+#endif /*__FreeBSD__*/
+#if defined(__bsdi__) || defined(__NetBSD__)
+ ro->ro_rt = rtalloc1(&((struct route *)ro)
+ ->ro_dst, 0);
+#endif /*__bsdi__*/
+ } else {
+#ifdef __FreeBSD__
+ rtcalloc((struct route *)ro);
+#else
+ rtalloc((struct route *)ro);
+#endif
+ }
+ }
+
+ /*
+ * in_pcbconnect() checks out IFF_LOOPBACK to skip using
+ * the address. But we don't know why it does so.
+ * It is necessary to ensure the scope even for lo0
+ * so doesn't check out IFF_LOOPBACK.
+ */
+
+ if (ro->ro_rt) {
+ ia6 = in6_ifawithscope(ro->ro_rt->rt_ifa->ifa_ifp, dst);
+ if (ia6 == 0) /* xxx scope error ?*/
+ ia6 = ifatoia6(ro->ro_rt->rt_ifa);
+ }
+#if 0
+ /*
+ * xxx The followings are necessary? (kazu)
+ * I don't think so.
+ * It's for SO_DONTROUTE option in IPv4.(jinmei)
+ */
+ if (ia6 == 0) {
+ struct sockaddr_in6 sin6 = {sizeof(sin6), AF_INET6, 0};
+
+ sin6->sin6_addr = *dst;
+
+ ia6 = ifatoia6(ifa_ifwithdstaddr(sin6tosa(&sin6)));
+ if (ia6 == 0)
+ ia6 = ifatoia6(ifa_ifwithnet(sin6tosa(&sin6)));
+ if (ia6 == 0)
+ return(0);
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+#endif /* 0 */
+ if (ia6 == 0) {
+ *errorp = EHOSTUNREACH; /* no route */
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+}
+
+/*
+ * Default hop limit selection. The precedence is as follows:
+ * 1. Hoplimit valued specified via ioctl.
+ * 2. (If the outgoing interface is detected) the current
+ * hop limit of the interface specified by router advertisement.
+ * 3. The system default hoplimit.
+*/
+int
+in6_selecthlim(inp, ifp)
+ struct inpcb *inp;
+ struct ifnet *ifp;
+{
+ if (inp && inp->inp_hops >= 0)
+ return(inp->inp_hops);
+ else if (ifp)
+ return(nd_ifinfo[ifp->if_index].chlim);
+ else
+ return(ip6_defhlim);
+}