--- sys/netinet6/raw_ip6.c (svn+ssh://svn.freebsd.org/base/head) (revision 261548) +++ sys/netinet6/raw_ip6.c (working copy) @@ -166,6 +166,7 @@ rip6_input(struct mbuf **mp, int *offp, int proto) struct inpcb *last = 0; struct mbuf *opts = NULL; struct sockaddr_in6 fromsa; + uint32_t zoneid; RIP6STAT_INC(rip6s_ipackets); @@ -176,8 +177,8 @@ rip6_input(struct mbuf **mp, int *offp, int proto) } init_sin6(&fromsa, m); /* general init */ - ifp = m->m_pkthdr.rcvif; + zoneid = in6_getscopezone(ifp, IPV6_ADDR_SCOPE_LINKLOCAL); INP_INFO_RLOCK(&V_ripcbinfo); LIST_FOREACH(in6p, &V_ripcb, inp_list) { @@ -200,8 +201,8 @@ rip6_input(struct mbuf **mp, int *offp, int proto) * and fall through into normal filter path if so. */ if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && - prison_check_ip6(in6p->inp_cred, - &ip6->ip6_dst) != 0) + prison_check_in6(in6p->inp_cred, + &ip6->ip6_dst, zoneid) != 0) continue; } INP_RLOCK(in6p); @@ -390,14 +391,9 @@ rip6_ctlinput(int cmd, struct sockaddr *sa, void * * may have setup with control call. */ int -#if __STDC__ rip6_output(struct mbuf *m, ...) -#else -rip6_output(m, va_alist) - struct mbuf *m; - va_dcl -#endif { + struct route_in6 ro; struct mbuf *control; struct m_tag *mtag; struct socket *so; @@ -410,8 +406,6 @@ rip6_output(struct mbuf *m, ...) struct ip6_pktopts opt, *optp; struct ifnet *oifp = NULL; int type = 0, code = 0; /* for ICMPv6 output statistics only */ - int scope_ambiguous = 0; - int use_defzone = 0; struct in6_addr in6a; va_list ap; @@ -421,6 +415,7 @@ rip6_output(struct mbuf *m, ...) control = va_arg(ap, struct mbuf *); va_end(ap); + bzero(&ro, sizeof(ro)); in6p = sotoinpcb(so); INP_WLOCK(in6p); @@ -436,20 +431,12 @@ rip6_output(struct mbuf *m, ...) optp = in6p->in6p_outputopts; /* - * Check and convert scope zone ID into internal form. - * - * XXX: we may still need to determine the zone later. + * Application must provide a proper zone ID or the use of + * default zone IDs should be enabled. */ - if (!(so->so_state & SS_ISCONNECTED)) { - if (!optp || !optp->ip6po_pktinfo || - !optp->ip6po_pktinfo->ipi6_ifindex) - use_defzone = V_ip6_use_defzone; - if (dstsock->sin6_scope_id == 0 && !use_defzone) - scope_ambiguous = 1; - if ((error = sa6_embedscope(dstsock, use_defzone)) != 0) - goto bad; - } - + error = sa6_checkzone_opts(optp, in6p->in6p_moptions, dstsock); + if (error != 0) + goto bad; /* * For an ICMPv6 packet, we should know its type and code to update * statistics. @@ -476,28 +463,11 @@ rip6_output(struct mbuf *m, ...) /* * Source address selection. */ - error = in6_selectsrc(dstsock, optp, in6p, NULL, so->so_cred, + error = in6_selectsrc(dstsock, optp, in6p, &ro, so->so_cred, &oifp, &in6a); if (error) goto bad; - error = prison_check_ip6(in6p->inp_cred, &in6a); - if (error != 0) - 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; /* @@ -560,10 +530,10 @@ rip6_output(struct mbuf *m, ...) } } - error = ip6_output(m, optp, NULL, 0, in6p->in6p_moptions, &oifp, in6p); + error = ip6_output(m, optp, &ro, IPV6_USEROIF, in6p->in6p_moptions, + &oifp, in6p); if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { - if (oifp) - icmp6_ifoutstat_inc(oifp, type, code); + icmp6_ifoutstat_inc(oifp, type, code); ICMP6STAT_INC(icp6s_outhist[type]); } else RIP6STAT_INC(rip6s_opackets); @@ -580,6 +550,7 @@ rip6_output(struct mbuf *m, ...) m_freem(control); } INP_WUNLOCK(in6p); + RO_RTFREE(&ro); return (error); } @@ -759,7 +730,7 @@ rip6_bind(struct socket *so, struct sockaddr *nam, { struct inpcb *inp; struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; - struct ifaddr *ifa = NULL; + struct in6_ifaddr *ifa = NULL; int error = 0; inp = sotoinpcb(so); @@ -767,28 +738,32 @@ rip6_bind(struct socket *so, struct sockaddr *nam, if (nam->sa_len != sizeof(*addr)) return (EINVAL); - if ((error = prison_check_ip6(td->td_ucred, &addr->sin6_addr)) != 0) - return (error); if (TAILQ_EMPTY(&V_ifnet) || addr->sin6_family != AF_INET6) return (EADDRNOTAVAIL); - if ((error = sa6_embedscope(addr, V_ip6_use_defzone)) != 0) + INP_RLOCK(inp); + error = sa6_checkzone_opts(inp->in6p_outputopts, + inp->in6p_moptions, addr); + INP_RUNLOCK(inp); + if (error != 0) return (error); - - if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && - (ifa = ifa_ifwithaddr((struct sockaddr *)addr)) == NULL) + if ((error = prison_check_ip6(td->td_ucred, addr)) != 0) + return (error); + if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr)) { + ifa = in6ifa_ifwithaddr(&addr->sin6_addr, addr->sin6_scope_id); + if (ifa == NULL) + return (EADDRNOTAVAIL); + } + if (ifa != NULL && ifa->ia6_flags & (IN6_IFF_ANYCAST | + IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED)) { + ifa_free(&ifa->ia_ifa); return (EADDRNOTAVAIL); - if (ifa != NULL && - ((struct in6_ifaddr *)ifa)->ia6_flags & - (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| - IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { - ifa_free(ifa); - return (EADDRNOTAVAIL); } if (ifa != NULL) - ifa_free(ifa); + ifa_free(&ifa->ia_ifa); INP_INFO_WLOCK(&V_ripcbinfo); INP_WLOCK(inp); inp->in6p_laddr = addr->sin6_addr; + inp->in6p_zoneid = addr->sin6_scope_id; INP_WUNLOCK(inp); INP_INFO_WUNLOCK(&V_ripcbinfo); return (0); @@ -801,7 +776,7 @@ rip6_connect(struct socket *so, struct sockaddr *n struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; struct in6_addr in6a; struct ifnet *ifp = NULL; - int error = 0, scope_ambiguous = 0; + int error = 0; inp = sotoinpcb(so); KASSERT(inp != NULL, ("rip6_connect: inp == NULL")); @@ -812,18 +787,11 @@ rip6_connect(struct socket *so, struct sockaddr *n return (EADDRNOTAVAIL); if (addr->sin6_family != AF_INET6) return (EAFNOSUPPORT); - - /* - * 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, we'll see if we can determine - * the outgoing interface. If we can, determine the zone ID based on - * the interface below. - */ - if (addr->sin6_scope_id == 0 && !V_ip6_use_defzone) - scope_ambiguous = 1; - if ((error = sa6_embedscope(addr, V_ip6_use_defzone)) != 0) + INP_RLOCK(inp); + error = sa6_checkzone_opts(inp->in6p_outputopts, + inp->in6p_moptions, addr); + INP_RUNLOCK(inp); + if (error != 0) return (error); INP_INFO_WLOCK(&V_ripcbinfo); @@ -836,16 +804,12 @@ rip6_connect(struct socket *so, struct sockaddr *n 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; + if (IN6_IS_ADDR_LINKLOCAL(&inp->in6p_faddr) || + IN6_IS_ADDR_LINKLOCAL(&inp->in6p_laddr)) + inp->in6p_zoneid = in6_getscopezone(ifp, + IPV6_ADDR_SCOPE_LINKLOCAL); soisconnected(so); INP_WUNLOCK(inp); INP_INFO_WUNLOCK(&V_ripcbinfo); @@ -890,8 +854,9 @@ rip6_send(struct socket *so, int flags, struct mbu tmp.sin6_family = AF_INET6; tmp.sin6_len = sizeof(struct sockaddr_in6); INP_RLOCK(inp); - bcopy(&inp->in6p_faddr, &tmp.sin6_addr, - sizeof(struct in6_addr)); + tmp.sin6_addr = inp->in6p_faddr; + if (IN6_IS_ADDR_LINKLOCAL(&tmp.sin6_addr)) + tmp.sin6_scope_id = inp->in6p_zoneid; INP_RUNLOCK(inp); dst = &tmp; } else {