On Tue, Apr 25, 2023 at 11:44:34AM +0200, Alexander Bluhm wrote:
> Hi,
> 
> Mutex arp_mtx protects the llinfo_arp la_...  fields.  So kernel
> lock is only needed for changing the route rt_flags.
> 
> Of course there is a race between checking and setting rt_flags.
> But the other checks of the RTF_REJECT flags were without kernel
> lock before.  This does not cause trouble, the worst thing that may
> happen is to wait another exprire time for ARP retry.  My diff does
> not make it worse, reading rt_flags and rt_expire is done without
> lock anyway.

This is progress in the right direction, I think.
The XXXSMP comment is incorrect and confusing.

> 
> The kernel lock is needed to change rt_flags.  Testing without
> KERNEL_LOCK() caused crashes.

Still not nice, reducing kernel lock scope helps.
With this diff in, we couldn't break the kernel with further unlock diffs.

A clearer version of this diff would use two new bools `expired' and `reject'
rather than a ternary `reject', but that can be polished and retested later.

OK kn

> 
> ok?
> 
> bluhm
> 
> Index: netinet/if_ether.c
> ===================================================================
> RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/if_ether.c,v
> retrieving revision 1.262
> diff -u -p -r1.262 if_ether.c
> --- netinet/if_ether.c        12 Apr 2023 16:14:42 -0000      1.262
> +++ netinet/if_ether.c        24 Apr 2023 14:44:51 -0000
> @@ -339,7 +339,7 @@ arpresolve(struct ifnet *ifp, struct rte
>       struct rtentry *rt = NULL;
>       char addr[INET_ADDRSTRLEN];
>       time_t uptime;
> -     int refresh = 0;
> +     int refresh = 0, reject = 0;
>  
>       if (m->m_flags & M_BCAST) {     /* broadcast */
>               memcpy(desten, etherbroadcastaddr, sizeof(etherbroadcastaddr));
> @@ -411,16 +411,9 @@ arpresolve(struct ifnet *ifp, struct rte
>       if (ifp->if_flags & (IFF_NOARP|IFF_STATICARP))
>               goto bad;
>  
> -     KERNEL_LOCK();
> -     /*
> -      * Re-check since we grab the kernel lock after the first check.
> -      * rtrequest_delete() can be called with shared netlock.  From
> -      * there arp_rtrequest() is reached which touches RTF_LLINFO
> -      * and rt_llinfo.  As this is called with kernel lock we grab the
> -      * kernel lock here and are safe.  XXXSMP
> -      */
> +     mtx_enter(&arp_mtx);
>       if (!ISSET(rt->rt_flags, RTF_LLINFO)) {
> -             KERNEL_UNLOCK();
> +             mtx_leave(&arp_mtx);
>               goto bad;
>       }
>       la = (struct llinfo_arp *)rt->rt_llinfo;
> @@ -451,13 +444,13 @@ arpresolve(struct ifnet *ifp, struct rte
>       }
>  #endif
>       if (rt->rt_expire) {
> -             rt->rt_flags &= ~RTF_REJECT;
> +             reject = ~RTF_REJECT;
>               if (la->la_asked == 0 || rt->rt_expire != uptime) {
>                       rt->rt_expire = uptime;
>                       if (la->la_asked++ < arp_maxtries)
>                               refresh = 1;
>                       else {
> -                             rt->rt_flags |= RTF_REJECT;
> +                             reject = RTF_REJECT;
>                               rt->rt_expire += arpt_down;
>                               la->la_asked = 0;
>                               la->la_refreshed = 0;
> @@ -466,8 +459,18 @@ arpresolve(struct ifnet *ifp, struct rte
>                       }
>               }
>       }
> +     mtx_leave(&arp_mtx);
>  
> -     KERNEL_UNLOCK();
> +     if (reject == RTF_REJECT && !ISSET(rt->rt_flags, RTF_REJECT)) {
> +             KERNEL_LOCK();
> +             SET(rt->rt_flags, RTF_REJECT);
> +             KERNEL_UNLOCK();
> +     }
> +     if (reject == ~RTF_REJECT && ISSET(rt->rt_flags, RTF_REJECT)) {
> +             KERNEL_LOCK();
> +             CLR(rt->rt_flags, RTF_REJECT);
> +             KERNEL_UNLOCK();
> +     }
>       if (refresh)
>               arprequest(ifp, &satosin(rt->rt_ifa->ifa_addr)->sin_addr.s_addr,
>                   &satosin(dst)->sin_addr.s_addr, ac->ac_enaddr);
> 

Reply via email to