diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -335,7 +335,7 @@ */ static int in6_pcbladdr(struct inpcb *inp, struct sockaddr_in6 *sin6, - struct in6_addr *plocal_addr6) + struct in6_addr *plocal_addr6, bool sas_required) { int error = 0; int scope_ambiguous = 0; @@ -364,13 +364,25 @@ if ((error = prison_remote_ip6(inp->inp_cred, &sin6->sin6_addr)) != 0) return (error); - error = in6_selectsrc_socket(sin6, inp->in6p_outputopts, - inp, inp->inp_cred, scope_ambiguous, &in6a, NULL); - if (error) - return (error); + if (sas_required) { + error = in6_selectsrc_socket(sin6, inp->in6p_outputopts, + inp, inp->inp_cred, scope_ambiguous, &in6a, NULL); + if (error) + return (error); + } else { + /* + * Source address selection isn't required when syncache + * has already established connection and both source and + * destination addresses was chosen. + * + * This also includes the case when fwd_tag was used to + * select source address in tcp_input(). + */ + in6a = inp->in6p_laddr; + } + if (IN6_IS_ADDR_UNSPECIFIED(&in6a)) return (EHOSTUNREACH); - /* * Do not update this earlier, in case we return with an error. * @@ -398,7 +410,7 @@ */ int in6_pcbconnect(struct inpcb *inp, struct sockaddr_in6 *sin6, struct ucred *cred, - bool rehash __unused) + bool sas_required) { struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; struct sockaddr_in6 laddr6; @@ -432,7 +444,8 @@ * Call inner routine, to assign local interface address. * in6_pcbladdr() may automatically fill in sin6_scope_id. */ - if ((error = in6_pcbladdr(inp, sin6, &laddr6.sin6_addr)) != 0) + if ((error = in6_pcbladdr(inp, sin6, &laddr6.sin6_addr, + sas_required)) != 0) return (error); if (in6_pcblookup_hash_locked(pcbinfo, &sin6->sin6_addr,