Author: melifaro
Date: Sun Jan 10 13:40:29 2016
New Revision: 293633
URL: https://svnweb.freebsd.org/changeset/base/293633

Log:
  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)

Modified:
  head/sys/netinet6/icmp6.c
  head/sys/netinet6/in6_pcb.c
  head/sys/netinet6/in6_src.c
  head/sys/netinet6/ip6_var.h
  head/sys/netinet6/nd6_nbr.c
  head/sys/netinet6/raw_ip6.c
  head/sys/netinet6/udp6_usrreq.c

Modified: head/sys/netinet6/icmp6.c
==============================================================================
--- head/sys/netinet6/icmp6.c   Sun Jan 10 11:59:55 2016        (r293632)
+++ head/sys/netinet6/icmp6.c   Sun Jan 10 13:40:29 2016        (r293633)
@@ -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,

Modified: head/sys/netinet6/in6_pcb.c
==============================================================================
--- head/sys/netinet6/in6_pcb.c Sun Jan 10 11:59:55 2016        (r293632)
+++ head/sys/netinet6/in6_pcb.c Sun Jan 10 13:40:29 2016        (r293633)
@@ -328,7 +328,6 @@ in6_pcbladdr(register struct inpcb *inp,
 {
        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,
        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?
         */

Modified: head/sys/netinet6/in6_src.c
==============================================================================
--- head/sys/netinet6/in6_src.c Sun Jan 10 11:59:55 2016        (r293632)
+++ head/sys/netinet6/in6_src.c Sun Jan 10 13:40:29 2016        (r293633)
@@ -136,6 +136,9 @@ static int selectroute(struct sockaddr_i
 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_addr
        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 *dstso
 
                /* 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);
 
@@ -545,6 +548,79 @@ in6_selectsrc(struct sockaddr_in6 *dstso
 }
 
 /*
+ * 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
  */
 static int

Modified: head/sys/netinet6/ip6_var.h
==============================================================================
--- head/sys/netinet6/ip6_var.h Sun Jan 10 11:59:55 2016        (r293632)
+++ head/sys/netinet6/ip6_var.h Sun Jan 10 13:40:29 2016        (r293633)
@@ -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 **);

Modified: head/sys/netinet6/nd6_nbr.c
==============================================================================
--- head/sys/netinet6/nd6_nbr.c Sun Jan 10 11:59:55 2016        (r293632)
+++ head/sys/netinet6/nd6_nbr.c Sun Jan 10 13:40:29 2016        (r293633)
@@ -481,27 +481,21 @@ nd6_ns_output_fib(struct ifnet *ifp, con
                        ifa_free(ifa);
                } else {
                        int error;
-                       struct sockaddr_in6 dst_sa;
-                       struct in6_addr src_in;
-                       struct ifnet *oifp;
-
-                       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);
+                       struct in6_addr dst6, src6;
+                       uint32_t scopeid;
+
+                       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, con
 {
        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, con
                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;

Modified: head/sys/netinet6/raw_ip6.c
==============================================================================
--- head/sys/netinet6/raw_ip6.c Sun Jan 10 11:59:55 2016        (r293632)
+++ head/sys/netinet6/raw_ip6.c Sun Jan 10 13:40:29 2016        (r293633)
@@ -397,6 +397,7 @@ rip6_output(struct mbuf *m, struct socke
        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 socke
        /*
         * 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 socke
                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 socke
         * 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 s
        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 s
        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);

Modified: head/sys/netinet6/udp6_usrreq.c
==============================================================================
--- head/sys/netinet6/udp6_usrreq.c     Sun Jan 10 11:59:55 2016        
(r293632)
+++ head/sys/netinet6/udp6_usrreq.c     Sun Jan 10 13:40:29 2016        
(r293633)
@@ -631,7 +631,6 @@ udp6_output(struct inpcb *inp, struct mb
        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 mb
                }
 
                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 */
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to