Use sa6_checkzone() to check user's input. Remove sa6_embedscope() and in6_clearscope() functions. Properly initialize inc.inc6_zoneid. Use in_conninfo from PCB to be able disambiguate a possibly ambiguous address. Pass ifnet pointer to in6_pcblookup to be able disambiguate a possibly ambiguous address. --- sys/netinet/tcp_subr.c (svn+ssh://svn.freebsd.org/base/head) (revision 261548) +++ sys/netinet/tcp_subr.c (working copy) @@ -1344,7 +1344,9 @@ tcp6_getcred(SYSCTL_HANDLER_ARGS) { struct xucred xuc; struct sockaddr_in6 addrs[2]; + struct ifnet *ifp; struct inpcb *inp; + uint32_t zoneid; int error; #ifdef INET int mapped = 0; @@ -1356,10 +1358,26 @@ tcp6_getcred(SYSCTL_HANDLER_ARGS) error = SYSCTL_IN(req, addrs, sizeof(addrs)); if (error) return (error); - if ((error = sa6_embedscope(&addrs[0], V_ip6_use_defzone)) != 0 || - (error = sa6_embedscope(&addrs[1], V_ip6_use_defzone)) != 0) { + error = sa6_checkzone(&addrs[0]); + if (error) return (error); + error = sa6_checkzone(&addrs[1]); + if (error) + return (error); + ifp = NULL; + zoneid = 0; + if (addrs[0].sin6_scope_id != 0) + zoneid = addrs[0].sin6_scope_id; + if (addrs[1].sin6_scope_id != 0) { + if (zoneid != 0 && zoneid != addrs[1].sin6_scope_id) + return (EINVAL); + zoneid = addrs[1].sin6_scope_id; } + if (zoneid != 0) { + ifp = in6_getlinkifnet(zoneid); + if (ifp == NULL) + return (ENOENT); + } if (IN6_IS_ADDR_V4MAPPED(&addrs[0].sin6_addr)) { #ifdef INET if (IN6_IS_ADDR_V4MAPPED(&addrs[1].sin6_addr)) @@ -1381,7 +1399,7 @@ tcp6_getcred(SYSCTL_HANDLER_ARGS) inp = in6_pcblookup(&V_tcbinfo, &addrs[1].sin6_addr, addrs[1].sin6_port, &addrs[0].sin6_addr, addrs[0].sin6_port, - INPLOOKUP_RLOCKPCB, NULL); + INPLOOKUP_RLOCKPCB, ifp); if (inp != NULL) { if (inp->inp_socket == NULL) error = ENOENT; @@ -1584,6 +1602,10 @@ tcp6_ctlinput(int cmd, struct sockaddr *sa, void * inc.inc6_faddr = ((struct sockaddr_in6 *)sa)->sin6_addr; inc.inc6_laddr = ip6cp->ip6c_src->sin6_addr; inc.inc_flags |= INC_ISIPV6; + if (IN6_IS_ADDR_LINKLOCAL(&inc.inc6_laddr) || + IN6_IS_ADDR_LINKLOCAL(&inc.inc6_faddr)) + inc.inc6_zoneid = in6_getscopezone(m->m_pkthdr.rcvif, + IPV6_ADDR_SCOPE_LINKLOCAL); INP_INFO_WLOCK(&V_tcbinfo); syncache_unreach(&inc, &th); INP_INFO_WUNLOCK(&V_tcbinfo); @@ -1839,21 +1861,26 @@ tcp_maxmtu6(struct in_conninfo *inc, struct tcp_if KASSERT(inc != NULL, ("tcp_maxmtu6 with NULL in_conninfo pointer")); - bzero(&sro6, sizeof(sro6)); - if (!IN6_IS_ADDR_UNSPECIFIED(&inc->inc6_faddr)) { + ifp = NULL; + if (inc->inc6_zoneid != 0) { + ifp = in6_getlinkifnet(inc->inc6_zoneid); + } else if (!IN6_IS_ADDR_UNSPECIFIED(&inc->inc6_faddr)) { + bzero(&sro6, sizeof(sro6)); sro6.ro_dst.sin6_family = AF_INET6; sro6.ro_dst.sin6_len = sizeof(struct sockaddr_in6); sro6.ro_dst.sin6_addr = inc->inc6_faddr; in6_rtalloc_ign(&sro6, 0, inc->inc_fibnum); + if (sro6.ro_rt != NULL) { + ifp = sro6.ro_rt->rt_ifp; + maxmtu = sro6.ro_rt->rt_rmx.rmx_mtu; + RTFREE(sro6.ro_rt); + } } - if (sro6.ro_rt != NULL) { - ifp = sro6.ro_rt->rt_ifp; - if (sro6.ro_rt->rt_rmx.rmx_mtu == 0) - maxmtu = IN6_LINKMTU(sro6.ro_rt->rt_ifp); + if (ifp != NULL) { + if (maxmtu != 0) + maxmtu = min(maxmtu, IN6_LINKMTU(ifp)); else - maxmtu = min(sro6.ro_rt->rt_rmx.rmx_mtu, - IN6_LINKMTU(sro6.ro_rt->rt_ifp)); - + maxmtu = IN6_LINKMTU(ifp); /* Report additional interface capabilities. */ if (cap != NULL) { if (ifp->if_capenable & IFCAP_TSO6 && @@ -1861,7 +1888,6 @@ tcp_maxmtu6(struct in_conninfo *inc, struct tcp_if cap->ifcap |= CSUM_TSO; cap->tsomax = ifp->if_hw_tsomax; } - RTFREE(sro6.ro_rt); } return (maxmtu); @@ -1929,7 +1955,7 @@ tcp_signature_apply(void *fstate, void *data, u_in * * Parameters: * m pointer to head of mbuf chain - * _unused + * inc pointer to struct in_conninfo * len length of TCP segment data, excluding options * optlen length of TCP segment options * buf pointer to storage for computed MD5 digest @@ -1948,8 +1974,8 @@ tcp_signature_apply(void *fstate, void *data, u_in * specify per-application flows but it is unstable. */ int -tcp_signature_compute(struct mbuf *m, int _unused, int len, int optlen, - u_char *buf, u_int direction) +tcp_signature_compute(struct mbuf *m, struct in_conninfo *inc, int len, + int optlen, u_char *buf, u_int direction) { union sockaddr_union dst; #ifdef INET @@ -1965,7 +1991,6 @@ int struct tcphdr *th; #ifdef INET6 struct ip6_hdr *ip6; - struct in6_addr in6; char ip6buf[INET6_ADDRSTRLEN]; uint32_t plen; uint16_t nhdr; @@ -1997,6 +2022,8 @@ int dst.sa.sa_family = AF_INET6; dst.sin6.sin6_addr = (direction == IPSEC_DIR_INBOUND) ? ip6->ip6_src : ip6->ip6_dst; + if (IN6_IS_ADDR_LINKLOCAL(&dst.sin6.sin6_addr)) + dst.sin6.sin6_scope_id = inc->inc6_zoneid; break; #endif default: @@ -2054,12 +2081,10 @@ int * Note: Upper-Layer Packet Length comes before Next Header. */ case (IPV6_VERSION >> 4): - in6 = ip6->ip6_src; - in6_clearscope(&in6); - MD5Update(&ctx, (char *)&in6, sizeof(struct in6_addr)); - in6 = ip6->ip6_dst; - in6_clearscope(&in6); - MD5Update(&ctx, (char *)&in6, sizeof(struct in6_addr)); + MD5Update(&ctx, (char *)&ip6->ip6_src, + sizeof(struct in6_addr)); + MD5Update(&ctx, (char *)&ip6->ip6_dst, + sizeof(struct in6_addr)); plen = htonl(len + sizeof(struct tcphdr) + optlen); MD5Update(&ctx, (char *)&plen, sizeof(uint32_t)); nhdr = 0; @@ -2120,8 +2145,8 @@ int * Return 1 if successful, otherwise return 0. */ int -tcp_signature_verify(struct mbuf *m, int off0, int tlen, int optlen, - struct tcpopt *to, struct tcphdr *th, u_int tcpbflag) +tcp_signature_verify(struct mbuf *m, struct in_conninfo *inc, int tlen, + int optlen, struct tcpopt *to, struct tcphdr *th, u_int tcpbflag) { char tmpdigest[TCP_SIGLEN]; @@ -2152,7 +2177,7 @@ int TCPSTAT_INC(tcps_sig_rcvbadsig); return (0); } - if (tcp_signature_compute(m, off0, tlen, optlen, &tmpdigest[0], + if (tcp_signature_compute(m, inc, tlen, optlen, &tmpdigest[0], IPSEC_DIR_INBOUND) == -1) { TCPSTAT_INC(tcps_sig_err_buildsig); TCPSTAT_INC(tcps_sig_rcvbadsig); @@ -2178,7 +2203,9 @@ sysctl_drop(SYSCTL_HANDLER_ARGS) struct tcptw *tw; struct sockaddr_in *fin, *lin; #ifdef INET6 + struct ifnet *ifp; struct sockaddr_in6 *fin6, *lin6; + uint32_t zoneid; #endif int error; @@ -2216,12 +2243,26 @@ sysctl_drop(SYSCTL_HANDLER_ARGS) lin = (struct sockaddr_in *)&addrs[1]; break; } - error = sa6_embedscope(fin6, V_ip6_use_defzone); + error = sa6_checkzone(fin6); if (error) return (error); - error = sa6_embedscope(lin6, V_ip6_use_defzone); + error = sa6_checkzone(lin6); if (error) return (error); + ifp = NULL; + zoneid = 0; + if (fin6->sin6_scope_id != 0) + zoneid = fin6->sin6_scope_id; + if (lin6->sin6_scope_id != 0) { + if (zoneid != 0 && zoneid != lin6->sin6_scope_id) + return (EINVAL); + zoneid = lin6->sin6_scope_id; + } + if (zoneid != 0) { + ifp = in6_getlinkifnet(zoneid); + if (ifp == NULL) + return (ENOENT); + } break; #endif #ifdef INET @@ -2242,7 +2283,7 @@ sysctl_drop(SYSCTL_HANDLER_ARGS) case AF_INET6: inp = in6_pcblookup(&V_tcbinfo, &fin6->sin6_addr, fin6->sin6_port, &lin6->sin6_addr, lin6->sin6_port, - INPLOOKUP_WLOCKPCB, NULL); + INPLOOKUP_WLOCKPCB, ifp); break; #endif #ifdef INET