Re: rtdeletemsg & KASSERT

2015-12-17 Thread Martin Pieuchot
On 16/12/15(Wed) 23:57, Alexander Bluhm wrote:
> On Wed, Dec 16, 2015 at 09:46:26PM +0100, Alexander Bluhm wrote:
> > 10.188.70.17   fe:e1:ba:d0:d5:6d  UHLS   03 - 8 vio0
> 
> This is this route that crashed the machine when the arp entry expired.
> 
> When I move the rtref()/rtfree() calls into rtdeletemsg() it also
> protects the calls from arptfree().
> 
> > __assert() at __assert+0x25
> > rtfree() at rtfree+0x11e
> > rtable_delete() at rtable_delete+0x5d
> > rt_delete() at rt_delete+0x6e
> > rtdeletemsg() at rtdeletemsg+0x2d
> > arptfree() at arptfree+0x4f
> > arptimer() at arptimer+0x63
> 
> After rt_delete() has called rtable_delete(), the route is still
> used and should not be destroyed.

The diff below indeed corrects that, but I found another bug in it.  The
problem is that rtable_lookup() might return a non perfect match so we
would now end up deleting the RTF_CLONING route instead of failing.

The proper solution would be to always pass a mask (or prefix length) to
rtable_lookup().

> Index: net/route.c
> ===
> RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.c,v
> retrieving revision 1.292
> diff -u -p -r1.292 route.c
> --- net/route.c   11 Dec 2015 08:58:23 -  1.292
> +++ net/route.c   16 Dec 2015 22:42:34 -
> @@ -159,8 +159,7 @@ struct sockaddr *rt_plentosa(sa_family_t
>  
>  struct   ifaddr *ifa_ifwithroute(int, struct sockaddr *, struct sockaddr 
> *,
>   u_int);
> -int  rtrequest_delete(struct rt_addrinfo *, u_int8_t, struct ifnet *,
> - struct rtentry **, u_int);
> +int  rt_delete(struct rtentry *, struct ifnet *, unsigned int);
>  
>  #ifdef DDB
>  void db_print_sa(struct sockaddr *);
> @@ -613,34 +612,22 @@ out:
>  }
>  
>  /*
> - * Delete a route and generate a message
> + * Delete a route and generate a message.  The caller must hold a reference
> + * on ``rt''.
>   */
>  int
> -rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, u_int tableid)
> +rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid)
>  {
>   int error;
> - struct rt_addrinfo  info;
> - unsigned intifidx;
> - struct sockaddr_in6 sa_mask;
>  
>   KASSERT(rt->rt_ifidx == ifp->if_index);
>  
> - /*
> -  * Request the new route so that the entry is not actually
> -  * deleted.  That will allow the information being reported to
> -  * be accurate (and consistent with route_output()).
> -  */
> - bzero((caddr_t), sizeof(info));
> - info.rti_info[RTAX_DST] = rt_key(rt);
> - if (!ISSET(rt->rt_flags, RTF_HOST))
> - info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt, _mask);
> - info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
> - info.rti_flags = rt->rt_flags;
> - ifidx = rt->rt_ifidx;
> - error = rtrequest_delete(, rt->rt_priority, ifp, , tableid);
> - rt_missmsg(RTM_DELETE, , info.rti_flags, ifidx, error, tableid);
> + rtref(rt);
> + error = rt_delete(rt, ifp, rtableid);
>   if (error == 0)
> - rtfree(rt);
> + rt_sendmsg(rt, RTM_DELETE, rtableid);
> + rtfree(rt);
> +
>   return (error);
>  }
>  
> @@ -671,10 +658,10 @@ rtflushclone1(struct rtentry *rt, void *
>* the routes are being purged by rt_if_remove().
>*/
>   if (ifp == NULL)
> - return 0;
> + return 0;
>  
>   if (ISSET(rt->rt_flags, RTF_CLONED) && rtequal(rt->rt_parent, parent))
> - rtdeletemsg(rt, ifp, id);
> + rtdeletemsg(rt, ifp, id);
>  
>   if_put(ifp);
>   return 0;
> @@ -818,60 +805,20 @@ rt_getifa(struct rt_addrinfo *info, u_in
>  }
>  
>  int
> -rtrequest_delete(struct rt_addrinfo *info, u_int8_t prio, struct ifnet *ifp,
> -struct rtentry **ret_nrt, u_int tableid)
> +rt_delete(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid)
>  {
> - struct rtentry  *rt;
> - int  error;
> -
> - splsoftassert(IPL_SOFTNET);
> -
> - if (!rtable_exists(tableid))
> - return (EAFNOSUPPORT);
> - rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
> - info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY], prio);
> - if (rt == NULL)
> - return (ESRCH);
> -
> - /* Make sure that's the route the caller want to delete. */
> - if (ifp != NULL && ifp->if_index != rt->rt_ifidx) {
> - rtfree(rt);
> - return (ESRCH);
> - }
> + struct sockaddr_in6  sa_mask;
>  
> -#ifndef SMALL_KERNEL
> - /*
> -  * If we got multipath routes, we require users to specify
> -  * a matching gateway.
> -  */
> - if ((rt->rt_flags & RTF_MPATH) &&
> - info->rti_info[RTAX_GATEWAY] == NULL) {
> - rtfree(rt);
> - return (ESRCH);
> - }
> -#endif
> + KASSERT(ifp->if_index == rt->rt_ifidx);
>  
> - /*
> -  * Since RTP_LOCAL 

Re: rtdeletemsg & KASSERT

2015-12-16 Thread Alexander Bluhm
Now I got a panic with this diff.

login: panic: kernel diagnostic assertion "!ISSET(rt->rt_flags, RTF_UP)" 
failed: file "../../../../net/route.c", line 444
Stopped at  Debugger+0x9:   leave
   TIDPIDUID PRFLAGS PFLAGS  CPU  COMMAND
Debugger() at Debugger+0x9
panic() at panic+0xfe
__assert() at __assert+0x25
rtfree() at rtfree+0x11e
rtable_delete() at rtable_delete+0x5d
rt_delete() at rt_delete+0x6e
rtdeletemsg() at rtdeletemsg+0x2d
arptfree() at arptfree+0x4f
arptimer() at arptimer+0x63
softclock() at softclock+0x315
softintr_dispatch() at softintr_dispatch+0x72
Xsoftclock() at Xsoftclock+0x1f
--- interrupt ---
end of kernel
end trace frame: 0x2710, count: 3
0x8:
http://www.openbsd.org/ddb.html describes the minimum info required in bug
reports.  Insufficient info makes it difficult to find and fix bugs.

It happened after running the ARP regression test.  I cannot reproduce
it by running the test.  Perhaps I have to wait for the arp timer.
The kernel is running on a single CPU qemu.

bluhm

On Wed, Dec 16, 2015 at 07:59:40PM +0100, Alexander Bluhm wrote:
> I have merged the diff to -current before reviewing.  There it looks
> like this.
> 
> Index: net/route.c
> ===
> RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.c,v
> retrieving revision 1.292
> diff -u -p -r1.292 route.c
> --- net/route.c   11 Dec 2015 08:58:23 -  1.292
> +++ net/route.c   16 Dec 2015 18:40:16 -
> @@ -159,8 +159,7 @@ struct sockaddr *rt_plentosa(sa_family_t
>  
>  struct   ifaddr *ifa_ifwithroute(int, struct sockaddr *, struct sockaddr 
> *,
>   u_int);
> -int  rtrequest_delete(struct rt_addrinfo *, u_int8_t, struct ifnet *,
> - struct rtentry **, u_int);
> +int  rt_delete(struct rtentry *, struct ifnet *, unsigned int);
>  
>  #ifdef DDB
>  void db_print_sa(struct sockaddr *);
> @@ -613,34 +612,20 @@ out:
>  }
>  
>  /*
> - * Delete a route and generate a message
> + * Delete a route and generate a message.  The caller must hold a reference
> + * on ``rt''.
>   */
>  int
> -rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, u_int tableid)
> +rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid)
>  {
>   int error;
> - struct rt_addrinfo  info;
> - unsigned intifidx;
> - struct sockaddr_in6 sa_mask;
>  
>   KASSERT(rt->rt_ifidx == ifp->if_index);
>  
> - /*
> -  * Request the new route so that the entry is not actually
> -  * deleted.  That will allow the information being reported to
> -  * be accurate (and consistent with route_output()).
> -  */
> - bzero((caddr_t), sizeof(info));
> - info.rti_info[RTAX_DST] = rt_key(rt);
> - if (!ISSET(rt->rt_flags, RTF_HOST))
> - info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt, _mask);
> - info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
> - info.rti_flags = rt->rt_flags;
> - ifidx = rt->rt_ifidx;
> - error = rtrequest_delete(, rt->rt_priority, ifp, , tableid);
> - rt_missmsg(RTM_DELETE, , info.rti_flags, ifidx, error, tableid);
> + error = rt_delete(rt, ifp, rtableid);
>   if (error == 0)
> - rtfree(rt);
> + rt_sendmsg(rt, RTM_DELETE, rtableid);
> +
>   return (error);
>  }
>  
> @@ -671,10 +656,13 @@ rtflushclone1(struct rtentry *rt, void *
>* the routes are being purged by rt_if_remove().
>*/
>   if (ifp == NULL)
> - return 0;
> + return 0;
>  
> - if (ISSET(rt->rt_flags, RTF_CLONED) && rtequal(rt->rt_parent, parent))
> - rtdeletemsg(rt, ifp, id);
> + if (ISSET(rt->rt_flags, RTF_CLONED) && rtequal(rt->rt_parent, parent)) {
> + rtref(rt);
> + rtdeletemsg(rt, ifp, id);
> + rtfree(rt);
> + }
>  
>   if_put(ifp);
>   return 0;
> @@ -818,60 +806,20 @@ rt_getifa(struct rt_addrinfo *info, u_in
>  }
>  
>  int
> -rtrequest_delete(struct rt_addrinfo *info, u_int8_t prio, struct ifnet *ifp,
> -struct rtentry **ret_nrt, u_int tableid)
> +rt_delete(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid)
>  {
> - struct rtentry  *rt;
> - int  error;
> -
> - splsoftassert(IPL_SOFTNET);
> + struct sockaddr_in6  sa_mask;
>  
> - if (!rtable_exists(tableid))
> - return (EAFNOSUPPORT);
> - rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
> - info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY], prio);
> - if (rt == NULL)
> - return (ESRCH);
> + KASSERT(ifp->if_index == rt->rt_ifidx);
>  
> - /* Make sure that's the route the caller want to delete. */
> - if (ifp != NULL && ifp->if_index != rt->rt_ifidx) {
> - rtfree(rt);
> - return (ESRCH);
> - }
> -
> -#ifndef SMALL_KERNEL
> - /*
> -  * If we got multipath routes, we require users to specify
> - 

Re: rtdeletemsg & KASSERT

2015-12-16 Thread Alexander Bluhm
On Wed, Dec 16, 2015 at 08:47:02PM +0100, Alexander Bluhm wrote:
> It happened after running the ARP regression test.  I cannot reproduce
> it by running the test.  Perhaps I have to wait for the arp timer.

Reproduceable by waiting for the arp timeout, then it crashes.
I will investigate.

bluhm

root@q70:.../~# netstat -nrf inet
Routing tables

Internet:
DestinationGatewayFlags   Refs  Use   Mtu  Prio Iface
10.188.70/24   10.188.70.70   UC 01 - 4 vio0 
10.188.70.17   fe:e1:ba:d0:d5:6d  UHLS   03 - 8 vio0 
10.188.70.70   70:5f:ca:21:8d:70  UHLl   0   14 - 1 vio0 
10.188.70.255  10.188.70.70   UHb00 - 1 vio0 
10.188.211/24  10.188.211.70  UC 00 - 4 vio1 
10.188.211.70  70:5f:ca:21:8d:80  UHLl   01 - 1 vio1 
10.188.211.255 10.188.211.70  UHb00 - 1 vio1 
10.188.212/24  10.188.211.71  UGS00 - 8 vio1 
10.188.213/24  10.188.211.71  UGS00 - 8 vio1 
10.188.217/24  127.0.0.1  UGRS   00 32768 8 lo0  
10.188.218/24  127.0.0.1  UGRS   00 32768 8 lo0  
127/8  127.0.0.1  UGRS   00 32768 8 lo0  
127.0.0.1  127.0.0.1  UHl00 32768 1 lo0  
224/4  127.0.0.1  URS00 32768 8 lo0  
root@q70:.../~# panic: kernel diagnostic assertion "!ISSET(rt->rt_flags, 
RTF_UP)" failed: file "../../../../net/route.c", line 444
Stopped at  Debugger+0x9:   leave
   TIDPIDUID PRFLAGS PFLAGS  CPU  COMMAND
Debugger() at Debugger+0x9
panic() at panic+0xfe
__assert() at __assert+0x25
rtfree() at rtfree+0x11e
rtable_delete() at rtable_delete+0x5d
rt_delete() at rt_delete+0x6e
rtdeletemsg() at rtdeletemsg+0x2d
arptfree() at arptfree+0x4f
arptimer() at arptimer+0x63
softclock() at softclock+0x315
softintr_dispatch() at softintr_dispatch+0x72
Xsoftclock() at Xsoftclock+0x1f
--- interrupt ---
end of kernel
end trace frame: 0x2710, count: 3
0x8:
http://www.openbsd.org/ddb.html describes the minimum info required in bug
reports.  Insufficient info makes it difficult to find and fix bugs.
ddb> 



Re: rtdeletemsg & KASSERT

2015-12-16 Thread Alexander Bluhm
On Mon, Dec 07, 2015 at 04:36:17PM +0100, Martin Pieuchot wrote:
> The rtrequest_delete() refactoring exposed an existing bug and
> introduced a regression, both triggered by the same KASSERT().
> 
> The regression has been reported there:
>   https://marc.info/?l=openbsd-bugs=144943901304713=2
> 
> The problem is that rt_if_remove() will triggers a rtflushclone1() if a
> RTF_CLONING route is attached to an interface.  In this case
> rtdeletemsg() tries to get the interface pointer from rt_ifidx and the
> KASSERT() fires.
> 
> The bug exposed by the same KASSERT() can be triggered by a call to
> rt_ifa_del() when dhclient(8) tries to add an address already configured
> on the system.  In this case the kernel calls rtrequest_delete() with
> a wrong ifp.
> 
> Diff below fixes these two bugs.  It's big because I don't want to put
> two customs checks in a generic function.  This diff is built upon my
> previous rtdeletemsg() one.
> 
> It introduces rt_delete() which deletes a route *without* doing a
> lookup.  There's various good reasons for that.
>   - First of all I don't want to take the risk of matching a similar
> multipath route.  The rt_ifa_del() bug is an example. 
>   - Secondly I'd like to avoid a recursive rtable_* call when routes
> are deleted from rtable_walk().
>   - Then I'd rather keep userland-specific checks inside rtrequest()
> which will ends up in rtsock.c one day.
>   - Finally it cleans one use of "struct rt_addrinfo" which should be
> limited to userland in order to get rid of RTA_NETMASK in kernel.
> 
> Note that it introduces a behavior change.  If rtdeletemsg() fails to
> delete a route no message is reported to userland.  I believe this was
> only used for debugging.

OK bluhm@

I have merged the diff to -current before reviewing.  There it looks
like this.

Index: net/route.c
===
RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.c,v
retrieving revision 1.292
diff -u -p -r1.292 route.c
--- net/route.c 11 Dec 2015 08:58:23 -  1.292
+++ net/route.c 16 Dec 2015 18:40:16 -
@@ -159,8 +159,7 @@ struct sockaddr *rt_plentosa(sa_family_t
 
 struct ifaddr *ifa_ifwithroute(int, struct sockaddr *, struct sockaddr *,
u_int);
-intrtrequest_delete(struct rt_addrinfo *, u_int8_t, struct ifnet *,
-   struct rtentry **, u_int);
+intrt_delete(struct rtentry *, struct ifnet *, unsigned int);
 
 #ifdef DDB
 void   db_print_sa(struct sockaddr *);
@@ -613,34 +612,20 @@ out:
 }
 
 /*
- * Delete a route and generate a message
+ * Delete a route and generate a message.  The caller must hold a reference
+ * on ``rt''.
  */
 int
-rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, u_int tableid)
+rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid)
 {
int error;
-   struct rt_addrinfo  info;
-   unsigned intifidx;
-   struct sockaddr_in6 sa_mask;
 
KASSERT(rt->rt_ifidx == ifp->if_index);
 
-   /*
-* Request the new route so that the entry is not actually
-* deleted.  That will allow the information being reported to
-* be accurate (and consistent with route_output()).
-*/
-   bzero((caddr_t), sizeof(info));
-   info.rti_info[RTAX_DST] = rt_key(rt);
-   if (!ISSET(rt->rt_flags, RTF_HOST))
-   info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt, _mask);
-   info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
-   info.rti_flags = rt->rt_flags;
-   ifidx = rt->rt_ifidx;
-   error = rtrequest_delete(, rt->rt_priority, ifp, , tableid);
-   rt_missmsg(RTM_DELETE, , info.rti_flags, ifidx, error, tableid);
+   error = rt_delete(rt, ifp, rtableid);
if (error == 0)
-   rtfree(rt);
+   rt_sendmsg(rt, RTM_DELETE, rtableid);
+
return (error);
 }
 
@@ -671,10 +656,13 @@ rtflushclone1(struct rtentry *rt, void *
 * the routes are being purged by rt_if_remove().
 */
if (ifp == NULL)
-   return 0;
+   return 0;
 
-   if (ISSET(rt->rt_flags, RTF_CLONED) && rtequal(rt->rt_parent, parent))
-   rtdeletemsg(rt, ifp, id);
+   if (ISSET(rt->rt_flags, RTF_CLONED) && rtequal(rt->rt_parent, parent)) {
+   rtref(rt);
+   rtdeletemsg(rt, ifp, id);
+   rtfree(rt);
+   }
 
if_put(ifp);
return 0;
@@ -818,60 +806,20 @@ rt_getifa(struct rt_addrinfo *info, u_in
 }
 
 int
-rtrequest_delete(struct rt_addrinfo *info, u_int8_t prio, struct ifnet *ifp,
-struct rtentry **ret_nrt, u_int tableid)
+rt_delete(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid)
 {
-   struct rtentry  *rt;
-   int  error;
-
-   splsoftassert(IPL_SOFTNET);
+   struct sockaddr_in6  sa_mask;
 
-   if (!rtable_exists(tableid))
-   return (EAFNOSUPPORT);
-   rt = 

Re: rtdeletemsg & KASSERT

2015-12-16 Thread Alexander Bluhm
On Wed, Dec 16, 2015 at 09:46:26PM +0100, Alexander Bluhm wrote:
> 10.188.70.17   fe:e1:ba:d0:d5:6d  UHLS   03 - 8 vio0 

This is this route that crashed the machine when the arp entry expired.

When I move the rtref()/rtfree() calls into rtdeletemsg() it also
protects the calls from arptfree().

> __assert() at __assert+0x25
> rtfree() at rtfree+0x11e
> rtable_delete() at rtable_delete+0x5d
> rt_delete() at rt_delete+0x6e
> rtdeletemsg() at rtdeletemsg+0x2d
> arptfree() at arptfree+0x4f
> arptimer() at arptimer+0x63

After rt_delete() has called rtable_delete(), the route is still
used and should not be destroyed.

bluhm

Index: net/route.c
===
RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.c,v
retrieving revision 1.292
diff -u -p -r1.292 route.c
--- net/route.c 11 Dec 2015 08:58:23 -  1.292
+++ net/route.c 16 Dec 2015 22:42:34 -
@@ -159,8 +159,7 @@ struct sockaddr *rt_plentosa(sa_family_t
 
 struct ifaddr *ifa_ifwithroute(int, struct sockaddr *, struct sockaddr *,
u_int);
-intrtrequest_delete(struct rt_addrinfo *, u_int8_t, struct ifnet *,
-   struct rtentry **, u_int);
+intrt_delete(struct rtentry *, struct ifnet *, unsigned int);
 
 #ifdef DDB
 void   db_print_sa(struct sockaddr *);
@@ -613,34 +612,22 @@ out:
 }
 
 /*
- * Delete a route and generate a message
+ * Delete a route and generate a message.  The caller must hold a reference
+ * on ``rt''.
  */
 int
-rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, u_int tableid)
+rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid)
 {
int error;
-   struct rt_addrinfo  info;
-   unsigned intifidx;
-   struct sockaddr_in6 sa_mask;
 
KASSERT(rt->rt_ifidx == ifp->if_index);
 
-   /*
-* Request the new route so that the entry is not actually
-* deleted.  That will allow the information being reported to
-* be accurate (and consistent with route_output()).
-*/
-   bzero((caddr_t), sizeof(info));
-   info.rti_info[RTAX_DST] = rt_key(rt);
-   if (!ISSET(rt->rt_flags, RTF_HOST))
-   info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt, _mask);
-   info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
-   info.rti_flags = rt->rt_flags;
-   ifidx = rt->rt_ifidx;
-   error = rtrequest_delete(, rt->rt_priority, ifp, , tableid);
-   rt_missmsg(RTM_DELETE, , info.rti_flags, ifidx, error, tableid);
+   rtref(rt);
+   error = rt_delete(rt, ifp, rtableid);
if (error == 0)
-   rtfree(rt);
+   rt_sendmsg(rt, RTM_DELETE, rtableid);
+   rtfree(rt);
+
return (error);
 }
 
@@ -671,10 +658,10 @@ rtflushclone1(struct rtentry *rt, void *
 * the routes are being purged by rt_if_remove().
 */
if (ifp == NULL)
-   return 0;
+   return 0;
 
if (ISSET(rt->rt_flags, RTF_CLONED) && rtequal(rt->rt_parent, parent))
-   rtdeletemsg(rt, ifp, id);
+   rtdeletemsg(rt, ifp, id);
 
if_put(ifp);
return 0;
@@ -818,60 +805,20 @@ rt_getifa(struct rt_addrinfo *info, u_in
 }
 
 int
-rtrequest_delete(struct rt_addrinfo *info, u_int8_t prio, struct ifnet *ifp,
-struct rtentry **ret_nrt, u_int tableid)
+rt_delete(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid)
 {
-   struct rtentry  *rt;
-   int  error;
-
-   splsoftassert(IPL_SOFTNET);
-
-   if (!rtable_exists(tableid))
-   return (EAFNOSUPPORT);
-   rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
-   info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY], prio);
-   if (rt == NULL)
-   return (ESRCH);
-
-   /* Make sure that's the route the caller want to delete. */
-   if (ifp != NULL && ifp->if_index != rt->rt_ifidx) {
-   rtfree(rt);
-   return (ESRCH);
-   }
+   struct sockaddr_in6  sa_mask;
 
-#ifndef SMALL_KERNEL
-   /*
-* If we got multipath routes, we require users to specify
-* a matching gateway.
-*/
-   if ((rt->rt_flags & RTF_MPATH) &&
-   info->rti_info[RTAX_GATEWAY] == NULL) {
-   rtfree(rt);
-   return (ESRCH);
-   }
-#endif
+   KASSERT(ifp->if_index == rt->rt_ifidx);
 
-   /*
-* Since RTP_LOCAL cannot be set by userland, make
-* sure that local routes are only modified by the
-* kernel.
-*/
-   if ((rt->rt_flags & (RTF_LOCAL|RTF_BROADCAST)) &&
-   prio != RTP_LOCAL) {
-   rtfree(rt);
-   return (EINVAL);
-   }
+   splsoftassert(IPL_SOFTNET);
 
-   error = rtable_delete(tableid, info->rti_info[RTAX_DST],
-   info->rti_info[RTAX_NETMASK], rt);
-   if (error != 0) {
-   rtfree(rt);
+   

rtdeletemsg & KASSERT

2015-12-07 Thread Martin Pieuchot
The rtrequest_delete() refactoring exposed an existing bug and
introduced a regression, both triggered by the same KASSERT().

The regression has been reported there:
  https://marc.info/?l=openbsd-bugs=144943901304713=2

The problem is that rt_if_remove() will triggers a rtflushclone1() if a
RTF_CLONING route is attached to an interface.  In this case
rtdeletemsg() tries to get the interface pointer from rt_ifidx and the
KASSERT() fires.

The bug exposed by the same KASSERT() can be triggered by a call to
rt_ifa_del() when dhclient(8) tries to add an address already configured
on the system.  In this case the kernel calls rtrequest_delete() with
a wrong ifp.

Diff below fixes these two bugs.  It's big because I don't want to put
two customs checks in a generic function.  This diff is built upon my
previous rtdeletemsg() one.

It introduces rt_delete() which deletes a route *without* doing a
lookup.  There's various good reasons for that.
  - First of all I don't want to take the risk of matching a similar
multipath route.  The rt_ifa_del() bug is an example. 
  - Secondly I'd like to avoid a recursive rtable_* call when routes
are deleted from rtable_walk().
  - Then I'd rather keep userland-specific checks inside rtrequest()
which will ends up in rtsock.c one day.
  - Finally it cleans one use of "struct rt_addrinfo" which should be
limited to userland in order to get rid of RTA_NETMASK in kernel.

Note that it introduces a behavior change.  If rtdeletemsg() fails to
delete a route no message is reported to userland.  I believe this was
only used for debugging.

Index: net/route.c
===
RCS file: /cvs/src/sys/net/route.c,v
retrieving revision 1.289
diff -u -p -r1.289 route.c
--- net/route.c 5 Dec 2015 10:07:55 -   1.289
+++ net/route.c 7 Dec 2015 15:10:15 -
@@ -159,8 +159,7 @@ struct sockaddr *rt_plentosa(sa_family_t
 
 struct ifaddr *ifa_ifwithroute(int, struct sockaddr *, struct sockaddr *,
u_int);
-intrtrequest_delete(struct rt_addrinfo *, u_int8_t, struct ifnet *,
-   struct rtentry **, u_int);
+intrt_delete(struct rtentry *, struct ifnet *, unsigned int);
 
 #ifdef DDB
 void   db_print_sa(struct sockaddr *);
@@ -613,38 +612,29 @@ out:
 }
 
 /*
- * Delete a route and generate a message
+ * Delete a route and generate a message.  The caller must hold a reference
+ * on ``rt''.
  */
 int
-rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, u_int tableid)
+rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid)
 {
int error;
-   struct rt_addrinfo  info;
-   unsigned intifidx;
-   struct sockaddr_in6 sa_mask;
 
-   /*
-* Request the new route so that the entry is not actually
-* deleted.  That will allow the information being reported to
-* be accurate (and consistent with route_output()).
-*/
-   bzero((caddr_t), sizeof(info));
-   info.rti_info[RTAX_DST] = rt_key(rt);
-   if (!ISSET(rt->rt_flags, RTF_HOST))
-   info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt, _mask);
-   info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
-   info.rti_flags = rt->rt_flags;
-   ifidx = rt->rt_ifidx;
-   error = rtrequest_delete(, rt->rt_priority, ifp, , tableid);
-   rt_missmsg(RTM_DELETE, , info.rti_flags, ifidx, error, tableid);
+   KASSERT(rt->rt_ifidx == ifp->if_index);
+
+   error = rt_delete(rt, ifp, rtableid);
if (error == 0)
-   rtfree(rt);
+   rt_sendmsg(rt, RTM_DELETE, rtableid);
+
return (error);
 }
 
 static inline int
 rtequal(struct rtentry *a, struct rtentry *b)
 {
+   if (a == b)
+   return 1;
+
if (memcmp(rt_key(a), rt_key(b), rt_key(a)->sa_len) == 0 &&
rt_plen(a) == rt_plen(b))
return 1;
@@ -656,10 +646,25 @@ int
 rtflushclone1(struct rtentry *rt, void *arg, u_int id)
 {
struct rtentry *parent = arg;
+   struct ifnet *ifp;
+
+   ifp = if_get(rt->rt_ifidx);
+
+   /*
+* This happens when an interface with a RTF_CLONING route is
+* being detached.  In this case it's safe to bail because all
+* the routes are being purged by rt_if_remove().
+*/
+   if (ifp == NULL)
+   return 0;
 
-   if ((rt->rt_flags & RTF_CLONED) != 0 && (rt->rt_parent == parent ||
-   rtequal(rt->rt_parent, parent)))
-   rtdeletemsg(rt, NULL, id);
+   if (ISSET(rt->rt_flags, RTF_CLONED) && rtequal(rt->rt_parent, parent)) {
+   rtref(rt);
+   rtdeletemsg(rt, ifp, id);
+   rtfree(rt);
+   }
+
+   if_put(ifp);
return 0;
 }
 
@@ -801,53 +806,20 @@ rt_getifa(struct rt_addrinfo *info, u_in
 }
 
 int
-rtrequest_delete(struct rt_addrinfo *info, u_int8_t prio, struct ifnet *ifp,
-struct rtentry **ret_nrt, u_int tableid)