summaryrefslogtreecommitdiffstats
path: root/sys/netinet6
diff options
context:
space:
mode:
authormelifaro <melifaro@FreeBSD.org>2016-01-10 13:40:29 +0000
committermelifaro <melifaro@FreeBSD.org>2016-01-10 13:40:29 +0000
commit21632a9bd96fd68b505602bfdd4850ab1e18574a (patch)
tree97b6bca373452e47001d3052c19a801b844cc5c5 /sys/netinet6
parent62e557108e3812d8e8b16b2a263c2b82ea2538e5 (diff)
downloadFreeBSD-src-21632a9bd96fd68b505602bfdd4850ab1e18574a.zip
FreeBSD-src-21632a9bd96fd68b505602bfdd4850ab1e18574a.tar.gz
Split in6_selectsrc() into in6_selectsrc_addr() and in6_selectsrc_socket().
in6_selectsrc() has 2 class of users: socket-based one (raw/udp/pcb/etc) and socket-less (ND code). The main reason for that change is inability to specify non-default FIB for callers w/o socket since (internally) inpcb is used to determine fib. As as result, add 2 wrappers for in6_selectsrc() (making in6_selectsrc() static): 1) in6_selectsrc_socket() for the former class. Embed scope_ambiguous check along with returning hop limit when needed. 2) in6_selectsrc_addr() for the latter case. Add 'fibnum' argument and pass IPv6 address w/ explicitly specified scope as separate argument. Reviewed by: ae (previous version)
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/icmp6.c22
-rw-r--r--sys/netinet6/in6_pcb.c12
-rw-r--r--sys/netinet6/in6_src.c84
-rw-r--r--sys/netinet6/ip6_var.h7
-rw-r--r--sys/netinet6/nd6_nbr.c41
-rw-r--r--sys/netinet6/raw_ip6.c33
-rw-r--r--sys/netinet6/udp6_usrreq.c10
7 files changed, 121 insertions, 88 deletions
diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c
index 2007a18..dd77527 100644
--- a/sys/netinet6/icmp6.c
+++ b/sys/netinet6/icmp6.c
@@ -2194,26 +2194,25 @@ icmp6_reflect(struct mbuf *m, size_t off)
}
if (srcp == NULL) {
- int e;
- struct sockaddr_in6 sin6;
+ int error;
+ struct in6_addr dst6;
+ uint32_t scopeid;
/*
* This case matches to multicasts, our anycast, or unicasts
* that we do not own. Select a source address based on the
* source address of the erroneous packet.
*/
- bzero(&sin6, sizeof(sin6));
- sin6.sin6_family = AF_INET6;
- sin6.sin6_len = sizeof(sin6);
- sin6.sin6_addr = ip6->ip6_dst; /* zone ID should be embedded */
+ in6_splitscope(&ip6->ip6_dst, &dst6, &scopeid);
+ error = in6_selectsrc_addr(RT_DEFAULT_FIB, &dst6,
+ scopeid, NULL, &src6, &hlim);
- e = in6_selectsrc(&sin6, NULL, NULL, NULL, &outif, &src6);
- if (e) {
+ if (error) {
char ip6buf[INET6_ADDRSTRLEN];
nd6log((LOG_DEBUG,
"icmp6_reflect: source can't be determined: "
"dst=%s, error=%d\n",
- ip6_sprintf(ip6buf, &ip6->ip6_dst), e));
+ ip6_sprintf(ip6buf, &ip6->ip6_dst), error));
goto bad;
}
srcp = &src6;
@@ -2228,10 +2227,7 @@ icmp6_reflect(struct mbuf *m, size_t off)
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_nxt = IPPROTO_ICMPV6;
- if (outif)
- ip6->ip6_hlim = ND_IFINFO(outif)->chlim;
- else
- ip6->ip6_hlim = hlim;
+ ip6->ip6_hlim = hlim;
icmp6->icmp6_cksum = 0;
icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6,
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
index 67ecb8c..a779b8d 100644
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -328,7 +328,6 @@ in6_pcbladdr(register struct inpcb *inp, struct sockaddr *nam,
{
register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
int error = 0;
- struct ifnet *ifp = NULL;
int scope_ambiguous = 0;
struct in6_addr in6a;
@@ -358,20 +357,15 @@ in6_pcbladdr(register struct inpcb *inp, struct sockaddr *nam,
if ((error = prison_remote_ip6(inp->inp_cred, &sin6->sin6_addr)) != 0)
return (error);
- error = in6_selectsrc(sin6, inp->in6p_outputopts,
- inp, inp->inp_cred, &ifp, &in6a);
+ error = in6_selectsrc_socket(sin6, inp->in6p_outputopts,
+ inp, inp->inp_cred, scope_ambiguous, &in6a, NULL);
if (error)
return (error);
- if (ifp && scope_ambiguous &&
- (error = in6_setscope(&sin6->sin6_addr, ifp, NULL)) != 0) {
- return(error);
- }
-
/*
* Do not update this earlier, in case we return with an error.
*
- * XXX: this in6_selectsrc result might replace the bound local
+ * XXX: this in6_selectsrc_socket result might replace the bound local
* address with the address specified by setsockopt(IPV6_PKTINFO).
* Is it the intended behavior?
*/
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c
index fb362e2..4029402 100644
--- a/sys/netinet6/in6_src.c
+++ b/sys/netinet6/in6_src.c
@@ -136,6 +136,9 @@ static int selectroute(struct sockaddr_in6 *, struct ip6_pktopts *,
static int in6_selectif(struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct ifnet **,
struct ifnet *, u_int);
+static int in6_selectsrc(uint32_t, struct sockaddr_in6 *,
+ struct ip6_pktopts *, struct inpcb *, struct ucred *,
+ struct ifnet **, struct in6_addr *);
static struct in6_addrpolicy *lookup_addrsel_policy(struct sockaddr_in6 *);
@@ -175,9 +178,9 @@ static struct in6_addrpolicy *match_addrsel_policy(struct sockaddr_in6 *);
goto out; /* XXX: we can't use 'break' here */ \
} while(0)
-int
-in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
- struct inpcb *inp, struct ucred *cred,
+static int
+in6_selectsrc(uint32_t fibnum, struct sockaddr_in6 *dstsock,
+ struct ip6_pktopts *opts, struct inpcb *inp, struct ucred *cred,
struct ifnet **ifpp, struct in6_addr *srcp)
{
struct rm_priotracker in6_ifa_tracker;
@@ -228,7 +231,7 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
/* get the outgoing interface */
if ((error = in6_selectif(dstsock, opts, mopts, &ifp, oifp,
- (inp != NULL) ? inp->inp_inc.inc_fibnum : RT_DEFAULT_FIB))
+ fibnum))
!= 0)
return (error);
@@ -545,6 +548,79 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
}
/*
+ * Select source address based on @inp, @dstsock and @opts.
+ * Stores selected address to @srcp. If @scope_ambiguous is set,
+ * embed scope from selected outgoing interface. If @hlim pointer
+ * is provided, stores calculated hop limit there.
+ * Returns 0 on success.
+ */
+int
+in6_selectsrc_socket(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
+ struct inpcb *inp, struct ucred *cred, int scope_ambiguous,
+ struct in6_addr *srcp, int *hlim)
+{
+ struct ifnet *retifp;
+ uint32_t fibnum;
+ int error;
+
+ fibnum = (inp != NULL) ? inp->inp_inc.inc_fibnum : RT_DEFAULT_FIB;
+ retifp = NULL;
+
+ error = in6_selectsrc(fibnum, dstsock, opts, inp, cred, &retifp, srcp);
+ if (error != 0)
+ return (error);
+
+ if (hlim != NULL)
+ *hlim = in6_selecthlim(inp, retifp);
+
+ if (retifp == NULL || scope_ambiguous == 0)
+ return (0);
+
+ /*
+ * Application should provide a proper zone ID or the use of
+ * default zone IDs should be enabled. Unfortunately, some
+ * applications do not behave as it should, so we need a
+ * workaround. Even if an appropriate ID is not determined
+ * (when it's required), if we can determine the outgoing
+ * interface. determine the zone ID based on the interface.
+ */
+ error = in6_setscope(&dstsock->sin6_addr, retifp, NULL);
+
+ return (error);
+}
+
+/*
+ * Select source address based on @fibnum, @dst and @scopeid.
+ * Stores selected address to @srcp.
+ * Returns 0 on success.
+ *
+ * Used by non-socket based consumers (ND code mostly)
+ */
+int
+in6_selectsrc_addr(uint32_t fibnum, const struct in6_addr *dst,
+ uint32_t scopeid, struct ifnet *ifp, struct in6_addr *srcp,
+ int *hlim)
+{
+ struct ifnet *retifp;
+ struct sockaddr_in6 dst_sa;
+ int error;
+
+ retifp = ifp;
+ bzero(&dst_sa, sizeof(dst_sa));
+ dst_sa.sin6_family = AF_INET6;
+ dst_sa.sin6_len = sizeof(dst_sa);
+ dst_sa.sin6_addr = *dst;
+ dst_sa.sin6_scope_id = scopeid;
+ sa6_embedscope(&dst_sa, 0);
+
+ error = in6_selectsrc(fibnum, &dst_sa, NULL, NULL, NULL, &retifp, srcp);
+ if (hlim != NULL)
+ *hlim = in6_selecthlim(NULL, retifp);
+
+ return (error);
+}
+
+/*
* clone - meaningful only for bsdi and freebsd
*/
static int
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index f43a28c..ef86eca 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -418,9 +418,10 @@ int rip6_usrreq(struct socket *,
int dest6_input(struct mbuf **, int *, int);
int none_input(struct mbuf **, int *, int);
-int in6_selectsrc(struct sockaddr_in6 *, struct ip6_pktopts *,
- struct inpcb *inp, struct ucred *cred,
- struct ifnet **, struct in6_addr *);
+int in6_selectsrc_socket(struct sockaddr_in6 *, struct ip6_pktopts *,
+ struct inpcb *, struct ucred *, int, struct in6_addr *, int *);
+int in6_selectsrc_addr(uint32_t, const struct in6_addr *,
+ uint32_t, struct ifnet *, struct in6_addr *, int *);
int in6_selectroute(struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct route_in6 *, struct ifnet **,
struct rtentry **);
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
index b7ba0b2..3a4e8a0 100644
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -481,27 +481,21 @@ nd6_ns_output_fib(struct ifnet *ifp, const struct in6_addr *saddr6,
ifa_free(ifa);
} else {
int error;
- struct sockaddr_in6 dst_sa;
- struct in6_addr src_in;
- struct ifnet *oifp;
-
- bzero(&dst_sa, sizeof(dst_sa));
- dst_sa.sin6_family = AF_INET6;
- dst_sa.sin6_len = sizeof(dst_sa);
- dst_sa.sin6_addr = ip6->ip6_dst;
-
- oifp = ifp;
- error = in6_selectsrc(&dst_sa, NULL,
- NULL, NULL, &oifp, &src_in);
+ struct in6_addr dst6, src6;
+ uint32_t scopeid;
+
+ in6_splitscope(&ip6->ip6_dst, &dst6, &scopeid);
+ error = in6_selectsrc_addr(RT_DEFAULT_FIB, &dst6,
+ scopeid, ifp, &src6, NULL);
if (error) {
char ip6buf[INET6_ADDRSTRLEN];
nd6log((LOG_DEBUG, "%s: source can't be "
"determined: dst=%s, error=%d\n", __func__,
- ip6_sprintf(ip6buf, &dst_sa.sin6_addr),
+ ip6_sprintf(ip6buf, &dst6),
error));
goto bad;
}
- ip6->ip6_src = src_in;
+ ip6->ip6_src = src6;
}
} else {
/*
@@ -941,12 +935,12 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0,
{
struct mbuf *m;
struct m_tag *mtag;
- struct ifnet *oifp;
struct ip6_hdr *ip6;
struct nd_neighbor_advert *nd_na;
struct ip6_moptions im6o;
- struct in6_addr src, daddr6;
- struct sockaddr_in6 dst_sa;
+ struct in6_addr daddr6, dst6, src6;
+ uint32_t scopeid;
+
int icmp6len, maxlen, error;
caddr_t mac = NULL;
@@ -998,24 +992,21 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0,
flags &= ~ND_NA_FLAG_SOLICITED;
}
ip6->ip6_dst = daddr6;
- bzero(&dst_sa, sizeof(struct sockaddr_in6));
- dst_sa.sin6_family = AF_INET6;
- dst_sa.sin6_len = sizeof(struct sockaddr_in6);
- dst_sa.sin6_addr = daddr6;
/*
* Select a source whose scope is the same as that of the dest.
*/
- oifp = ifp;
- error = in6_selectsrc(&dst_sa, NULL, NULL, NULL, &oifp, &src);
+ in6_splitscope(&daddr6, &dst6, &scopeid);
+ error = in6_selectsrc_addr(RT_DEFAULT_FIB, &dst6,
+ scopeid, ifp, &src6, NULL);
if (error) {
char ip6buf[INET6_ADDRSTRLEN];
nd6log((LOG_DEBUG, "nd6_na_output: source can't be "
"determined: dst=%s, error=%d\n",
- ip6_sprintf(ip6buf, &dst_sa.sin6_addr), error));
+ ip6_sprintf(ip6buf, &daddr6), error));
goto bad;
}
- ip6->ip6_src = src;
+ ip6->ip6_src = src6;
nd_na = (struct nd_neighbor_advert *)(ip6 + 1);
nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
nd_na->nd_na_code = 0;
diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c
index 0725842..972eb9f 100644
--- a/sys/netinet6/raw_ip6.c
+++ b/sys/netinet6/raw_ip6.c
@@ -397,6 +397,7 @@ rip6_output(struct mbuf *m, struct socket *so, ...)
int type = 0, code = 0; /* for ICMPv6 output statistics only */
int scope_ambiguous = 0;
int use_defzone = 0;
+ int hlim = 0;
struct in6_addr in6a;
va_list ap;
@@ -460,8 +461,9 @@ rip6_output(struct mbuf *m, struct socket *so, ...)
/*
* Source address selection.
*/
- error = in6_selectsrc(dstsock, optp, in6p, so->so_cred,
- &oifp, &in6a);
+ error = in6_selectsrc_socket(dstsock, optp, in6p, so->so_cred,
+ scope_ambiguous, &in6a, &hlim);
+
if (error)
goto bad;
error = prison_check_ip6(in6p->inp_cred, &in6a);
@@ -469,19 +471,6 @@ rip6_output(struct mbuf *m, struct socket *so, ...)
goto bad;
ip6->ip6_src = in6a;
- if (oifp && scope_ambiguous) {
- /*
- * Application should provide a proper zone ID or the use of
- * default zone IDs should be enabled. Unfortunately, some
- * applications do not behave as it should, so we need a
- * workaround. Even if an appropriate ID is not determined
- * (when it's required), if we can determine the outgoing
- * interface. determine the zone ID based on the interface.
- */
- error = in6_setscope(&dstsock->sin6_addr, oifp, NULL);
- if (error != 0)
- goto bad;
- }
ip6->ip6_dst = dstsock->sin6_addr;
/*
@@ -496,7 +485,7 @@ rip6_output(struct mbuf *m, struct socket *so, ...)
* ip6_plen will be filled in ip6_output, so not fill it here.
*/
ip6->ip6_nxt = in6p->inp_ip_p;
- ip6->ip6_hlim = in6_selecthlim(in6p, oifp);
+ ip6->ip6_hlim = hlim;
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 ||
in6p->in6p_cksum != -1) {
@@ -784,7 +773,6 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
struct inpcb *inp;
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
struct in6_addr in6a;
- struct ifnet *ifp = NULL;
int error = 0, scope_ambiguous = 0;
inp = sotoinpcb(so);
@@ -813,21 +801,14 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
INP_INFO_WLOCK(&V_ripcbinfo);
INP_WLOCK(inp);
/* Source address selection. XXX: need pcblookup? */
- error = in6_selectsrc(addr, inp->in6p_outputopts,
- inp, so->so_cred, &ifp, &in6a);
+ error = in6_selectsrc_socket(addr, inp->in6p_outputopts,
+ inp, so->so_cred, scope_ambiguous, &in6a, NULL);
if (error) {
INP_WUNLOCK(inp);
INP_INFO_WUNLOCK(&V_ripcbinfo);
return (error);
}
- /* XXX: see above */
- if (ifp && scope_ambiguous &&
- (error = in6_setscope(&addr->sin6_addr, ifp, NULL)) != 0) {
- INP_WUNLOCK(inp);
- INP_INFO_WUNLOCK(&V_ripcbinfo);
- return (error);
- }
inp->in6p_faddr = addr->sin6_addr;
inp->in6p_laddr = in6a;
soisconnected(so);
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
index 353b260..9768df5 100644
--- a/sys/netinet6/udp6_usrreq.c
+++ b/sys/netinet6/udp6_usrreq.c
@@ -631,7 +631,6 @@ udp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6,
struct udphdr *udp6;
struct in6_addr *laddr, *faddr, in6a;
struct sockaddr_in6 *sin6 = NULL;
- struct ifnet *oifp = NULL;
int cscov_partial = 0;
int scope_ambiguous = 0;
u_short fport;
@@ -731,15 +730,10 @@ udp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6,
}
if (!IN6_IS_ADDR_V4MAPPED(faddr)) {
- error = in6_selectsrc(sin6, optp, inp,
- td->td_ucred, &oifp, &in6a);
+ error = in6_selectsrc_socket(sin6, optp, inp,
+ td->td_ucred, scope_ambiguous, &in6a, NULL);
if (error)
goto release;
- if (oifp && scope_ambiguous &&
- (error = in6_setscope(&sin6->sin6_addr,
- oifp, NULL))) {
- goto release;
- }
laddr = &in6a;
} else
laddr = &inp->in6p_laddr; /* XXX */
OpenPOWER on IntegriCloud