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)
This commit is contained in:
Alexander V. Chernikov 2016-01-10 13:40:29 +00:00
parent ab861e6c06
commit 601c0b8bcc
7 changed files with 120 additions and 87 deletions

View File

@ -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,

View File

@ -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?
*/

View File

@ -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);
@ -544,6 +547,79 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
return (0);
}
/*
* 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
*/

View File

@ -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 **);

View File

@ -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;
struct in6_addr dst6, src6;
uint32_t scopeid;
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);
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;

View File

@ -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);

View File

@ -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 */