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:
parent
ab861e6c06
commit
601c0b8bcc
@ -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,
|
||||
|
@ -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?
|
||||
*/
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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 **);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user