This diff does two things.

First it changes ip_ours() to no longer rely on ``rt_ifa''.  The problem
here is that the route entry reference acts as a proxy for ``ia''.  So
you cannot dereference ``ia'' *after* calling rtfree(9).  I find this
very error prone, so I rewrote the function to not use ``rt_ifa'' at
all.

Second it puts a KERNEL_LOCK() around the ``ifp->if_addrlist''
iteration.  This is a hint that accessing this list isn't MP-safe.  I
don't want to bother turning this list into a SRPLIST at least for the
moment because it isn't use frequently.  Remember that this case is
only execute when you don't have a pf state nor an existing route entry.
There's two others code path accessing this list in ip_output() but
*not* when a route entry is provided, so we will still be running under
KERNEL_LOCK() anyway.

Ok?

Index: netinet/ip_input.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.259
diff -u -p -r1.259 ip_input.c
--- netinet/ip_input.c  26 Oct 2015 15:49:13 -0000      1.259
+++ netinet/ip_input.c  26 Oct 2015 18:27:15 -0000
@@ -646,9 +646,9 @@ bad:
 int
 in_ouraddr(struct mbuf *m, struct ifnet *ifp, struct in_addr ina)
 {
-       struct in_ifaddr        *ia = NULL;
        struct rtentry          *rt;
        struct sockaddr_in       sin;
+       int                      match = 0;
 #if NPF > 0
        struct pf_state_key     *key;
 
@@ -671,11 +671,25 @@ in_ouraddr(struct mbuf *m, struct ifnet 
        sin.sin_family = AF_INET;
        sin.sin_addr = ina;
        rt = rtalloc(sintosa(&sin), 0, m->m_pkthdr.ph_rtableid);
-       if (rtisvalid(rt) && ISSET(rt->rt_flags, RTF_LOCAL|RTF_BROADCAST))
-               ia = ifatoia(rt->rt_ifa);
+       if (rtisvalid(rt)) {
+               if (ISSET(rt->rt_flags, RTF_LOCAL))
+                       match = 1;
+
+               /*
+                * If directedbcast is enabled we only consider it local
+                * if it is received on the interface with that address.
+                */
+               if (ISSET(rt->rt_flags, RTF_BROADCAST) &&
+                   (!ip_directedbcast || rt->rt_ifidx == ifp->if_index)) {
+                       match = 1;
+
+                       /* Make sure M_BCAST is set */
+                       m->m_flags |= M_BCAST;
+               }
+       }
        rtfree(rt);
 
-       if (ia == NULL) {
+       if (!match) {
                struct ifaddr *ifa;
 
                /*
@@ -695,33 +709,21 @@ in_ouraddr(struct mbuf *m, struct ifnet 
                 * interface, and that M_BCAST will only be set on a BROADCAST
                 * interface.
                 */
+               KERNEL_LOCK();
                TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
                        if (ifa->ifa_addr->sa_family != AF_INET)
                                continue;
 
                        if (IN_CLASSFULBROADCAST(ina.s_addr,
-                           ifatoia(ifa)->ia_addr.sin_addr.s_addr))
-                               return (1);
+                           ifatoia(ifa)->ia_addr.sin_addr.s_addr)) {
+                               match = 1;
+                               break;
+                       }
                }
-
-               return (0);
-       }
-
-       if (ina.s_addr != ia->ia_addr.sin_addr.s_addr) {
-               /*
-                * This matches a broadcast address on one of our interfaces.
-                * If directedbcast is enabled we only consider it local if it
-                * is received on the interface with that address.
-                */
-               if (ip_directedbcast && ia->ia_ifp != ifp)
-                       return (0);
-
-               /* Make sure M_BCAST is set */
-               if (m)
-                       m->m_flags |= M_BCAST;
+               KERNEL_UNLOCK();
        }
 
-       return (1);
+       return (match);
 }
 
 /*

Reply via email to