summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet6/in6_src.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet6/in6_src.c')
-rw-r--r--freebsd/sys/netinet6/in6_src.c154
1 files changed, 64 insertions, 90 deletions
diff --git a/freebsd/sys/netinet6/in6_src.c b/freebsd/sys/netinet6/in6_src.c
index f7cd642e..4f20ee54 100644
--- a/freebsd/sys/netinet6/in6_src.c
+++ b/freebsd/sys/netinet6/in6_src.c
@@ -131,9 +131,10 @@ VNET_DEFINE(int, ip6_prefer_tempaddr) = 0;
static int selectroute __P((struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct route_in6 *, struct ifnet **,
- struct rtentry **, int));
+ struct rtentry **, int, u_int));
static int in6_selectif __P((struct sockaddr_in6 *, struct ip6_pktopts *,
- struct ip6_moptions *, struct route_in6 *ro, struct ifnet **));
+ struct ip6_moptions *, struct route_in6 *ro, struct ifnet **,
+ struct ifnet *, u_int));
static struct in6_addrpolicy *lookup_addrsel_policy(struct sockaddr_in6 *);
@@ -184,7 +185,7 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
struct ifnet **ifpp, struct in6_addr *srcp)
{
struct in6_addr dst, tmp;
- struct ifnet *ifp = NULL;
+ struct ifnet *ifp = NULL, *oifp = NULL;
struct in6_ifaddr *ia = NULL, *ia_best = NULL;
struct in6_pktinfo *pi = NULL;
int dst_scope = -1, best_scope = -1, best_matchlen = -1;
@@ -197,8 +198,18 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
KASSERT(srcp != NULL, ("%s: srcp is NULL", __func__));
dst = dstsock->sin6_addr; /* make a copy for local operation */
- if (ifpp)
+ if (ifpp) {
+ /*
+ * Save a possibly passed in ifp for in6_selectsrc. Only
+ * neighbor discovery code should use this feature, where
+ * we may know the interface but not the FIB number holding
+ * the connected subnet in case someone deleted it from the
+ * default FIB and we need to check the interface.
+ */
+ if (*ifpp != NULL)
+ oifp = *ifpp;
*ifpp = NULL;
+ }
if (inp != NULL) {
INP_LOCK_ASSERT(inp);
@@ -219,7 +230,9 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
struct in6_ifaddr *ia6;
/* get the outgoing interface */
- if ((error = in6_selectif(dstsock, opts, mopts, ro, &ifp)) != 0)
+ if ((error = in6_selectif(dstsock, opts, mopts, ro, &ifp, oifp,
+ (inp != NULL) ? inp->inp_inc.inc_fibnum : RT_DEFAULT_FIB))
+ != 0)
return (error);
/*
@@ -283,7 +296,8 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
* the outgoing interface and the destination address.
*/
/* get the outgoing interface */
- if ((error = in6_selectif(dstsock, opts, mopts, ro, &ifp)) != 0)
+ if ((error = in6_selectif(dstsock, opts, mopts, ro, &ifp, oifp,
+ (inp != NULL) ? inp->inp_inc.inc_fibnum : RT_DEFAULT_FIB)) != 0)
return (error);
#ifdef DIAGNOSTIC
@@ -506,7 +520,7 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
static int
selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
struct ip6_moptions *mopts, struct route_in6 *ro,
- struct ifnet **retifp, struct rtentry **retrt, int norouteok)
+ struct ifnet **retifp, struct rtentry **retrt, int norouteok, u_int fibnum)
{
int error = 0;
struct ifnet *ifp = NULL;
@@ -583,7 +597,7 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
if (ron->ro_rt == NULL) {
- rtalloc((struct route *)ron); /* multi path case? */
+ in6_rtalloc(ron, fibnum); /* multi path case? */
if (ron->ro_rt == NULL) {
if (ron->ro_rt) {
RTFREE(ron->ro_rt);
@@ -596,9 +610,9 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
rt = ron->ro_rt;
ifp = rt->rt_ifp;
- IF_AFDATA_LOCK(ifp);
+ IF_AFDATA_RLOCK(ifp);
la = lla_lookup(LLTABLE6(ifp), 0, (struct sockaddr *)&sin6_next->sin6_addr);
- IF_AFDATA_UNLOCK(ifp);
+ IF_AFDATA_RUNLOCK(ifp);
if (la != NULL)
LLE_RUNLOCK(la);
else {
@@ -618,7 +632,7 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
*satosin6(&ron->ro_dst) = *sin6_next;
}
if (ron->ro_rt == NULL) {
- rtalloc((struct route *)ron); /* multi path case? */
+ in6_rtalloc(ron, fibnum); /* multi path case? */
if (ron->ro_rt == NULL ||
!(ron->ro_rt->rt_flags & RTF_LLINFO)) {
if (ron->ro_rt) {
@@ -663,11 +677,11 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
sa6->sin6_scope_id = 0;
#ifdef RADIX_MPATH
- rtalloc_mpath((struct route *)ro,
- ntohl(sa6->sin6_addr.s6_addr32[3]));
+ rtalloc_mpath_fib((struct route *)ro,
+ ntohl(sa6->sin6_addr.s6_addr32[3]), fibnum);
#else
- ro->ro_rt = rtalloc1(&((struct route *)ro)
- ->ro_dst, 0, 0UL);
+ ro->ro_rt = in6_rtalloc1((struct sockaddr *)
+ &ro->ro_dst, 0, 0UL, fibnum);
if (ro->ro_rt)
RT_UNLOCK(ro->ro_rt);
#endif
@@ -748,21 +762,29 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
static int
in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
- struct ip6_moptions *mopts, struct route_in6 *ro, struct ifnet **retifp)
+ struct ip6_moptions *mopts, struct route_in6 *ro, struct ifnet **retifp,
+ struct ifnet *oifp, u_int fibnum)
{
int error;
struct route_in6 sro;
struct rtentry *rt = NULL;
+ KASSERT(retifp != NULL, ("%s: retifp is NULL", __func__));
+
if (ro == NULL) {
bzero(&sro, sizeof(sro));
ro = &sro;
}
if ((error = selectroute(dstsock, opts, mopts, ro, retifp,
- &rt, 1)) != 0) {
+ &rt, 1, fibnum)) != 0) {
if (ro == &sro && rt && rt == sro.ro_rt)
RTFREE(rt);
+ /* Help ND. See oifp comment in in6_selectsrc(). */
+ if (oifp != NULL && fibnum == RT_DEFAULT_FIB) {
+ *retifp = oifp;
+ error = 0;
+ }
return (error);
}
@@ -797,7 +819,10 @@ in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
}
/*
- * clone - meaningful only for bsdi and freebsd
+ * Public wrapper function to selectroute().
+ *
+ * XXX-BZ in6_selectroute() should and will grow the FIB argument. The
+ * in6_selectroute_fib() function is only there for backward compat on stable.
*/
int
in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
@@ -806,9 +831,21 @@ in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
{
return (selectroute(dstsock, opts, mopts, ro, retifp,
- retrt, 0));
+ retrt, 0, RT_DEFAULT_FIB));
}
+#ifndef BURN_BRIDGES
+int
+in6_selectroute_fib(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
+ struct ip6_moptions *mopts, struct route_in6 *ro,
+ struct ifnet **retifp, struct rtentry **retrt, u_int fibnum)
+{
+
+ return (selectroute(dstsock, opts, mopts, ro, retifp,
+ retrt, 0, fibnum));
+}
+#endif
+
/*
* Default hop limit selection. The precedence is as follows:
* 1. Hoplimit value specified via ioctl.
@@ -832,7 +869,7 @@ in6_selecthlim(struct inpcb *in6p, struct ifnet *ifp)
ro6.ro_dst.sin6_family = AF_INET6;
ro6.ro_dst.sin6_len = sizeof(struct sockaddr_in6);
ro6.ro_dst.sin6_addr = in6p->in6p_faddr;
- rtalloc((struct route *)&ro6);
+ in6_rtalloc(&ro6, in6p->inp_inc.inc_fibnum);
if (ro6.ro_rt) {
lifp = ro6.ro_rt->rt_ifp;
RTFREE(ro6.ro_rt);
@@ -852,9 +889,11 @@ int
in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct ucred *cred)
{
struct socket *so = inp->inp_socket;
- u_int16_t lport = 0, first, last, *lastport;
- int count, error, wild = 0, dorandom;
+ u_int16_t lport = 0;
+ int error, wild = 0;
+#ifdef INVARIANTS
struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+#endif
INP_INFO_WLOCK_ASSERT(pcbinfo);
INP_WLOCK_ASSERT(inp);
@@ -870,74 +909,9 @@ in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct ucred *cred)
inp->inp_flags |= INP_ANONPORT;
- if (inp->inp_flags & INP_HIGHPORT) {
- first = V_ipport_hifirstauto; /* sysctl */
- last = V_ipport_hilastauto;
- lastport = &pcbinfo->ipi_lasthi;
- } else if (inp->inp_flags & INP_LOWPORT) {
- error = priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT, 0);
- if (error)
- return error;
- first = V_ipport_lowfirstauto; /* 1023 */
- last = V_ipport_lowlastauto; /* 600 */
- lastport = &pcbinfo->ipi_lastlow;
- } else {
- first = V_ipport_firstauto; /* sysctl */
- last = V_ipport_lastauto;
- lastport = &pcbinfo->ipi_lastport;
- }
-
- /*
- * For UDP, use random port allocation as long as the user
- * allows it. For TCP (and as of yet unknown) connections,
- * use random port allocation only if the user allows it AND
- * ipport_tick() allows it.
- */
- if (V_ipport_randomized &&
- (!V_ipport_stoprandom || pcbinfo == &V_udbinfo))
- dorandom = 1;
- else
- dorandom = 0;
- /*
- * It makes no sense to do random port allocation if
- * we have the only port available.
- */
- if (first == last)
- dorandom = 0;
- /* Make sure to not include UDP packets in the count. */
- if (pcbinfo != &V_udbinfo)
- V_ipport_tcpallocs++;
-
- /*
- * Instead of having two loops further down counting up or down
- * make sure that first is always <= last and go with only one
- * code path implementing all logic.
- */
- if (first > last) {
- u_int16_t aux;
-
- aux = first;
- first = last;
- last = aux;
- }
-
- if (dorandom)
- *lastport = first + (arc4random() % (last - first));
-
- count = last - first;
-
- do {
- if (count-- < 0) { /* completely used? */
- /* Undo an address bind that may have occurred. */
- inp->in6p_laddr = in6addr_any;
- return (EADDRNOTAVAIL);
- }
- ++*lastport;
- if (*lastport < first || *lastport > last)
- *lastport = first;
- lport = htons(*lastport);
- } while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr,
- lport, wild, cred));
+ error = in_pcb_lport(inp, NULL, &lport, cred, wild);
+ if (error != 0)
+ return (error);
inp->inp_lport = lport;
if (in_pcbinshash(inp) != 0) {