Re: mpath cloning routes and cloned routes

2018-02-20 Thread Florian Riehm

On 02/19/18 11:01, Martin Pieuchot wrote:

On 14/02/18(Wed) 21:53, Florian Riehm wrote:

If we delete cloning routes, we also delete their cloned routes.
This doesn't make sense if we delete a multipath cloning route and may result in
broken gateway routes:


That's a bug!


# netstat -rn | grep 192.168.178
default192.168.178.1  UGS5 4939 -12 iwn0
192.168.178/24 192.168.178.52 UCPn   1   51 - 8 iwn0
192.168.178/24 192.168.178.53 UCPn   00 - 8 iwn0
192.168.178.1  34:31:c4:24:83:d4  UHLch  1  118 - 7 iwn0
192.168.178.52 a4:4e:31:38:70:7c  UHLl   0 3749 - 1 iwn0
192.168.178.53 a4:4e:31:38:70:7c  UHLl   00 - 1 iwn0
192.168.178.255192.168.178.52 UHPb   00 - 1 iwn0
192.168.178.255192.168.178.53 UHPb   00 - 1 iwn0
As you can see above, iwn0 has 192.168.178.52/24 and 192.168.178.53/24 assigned
and therefore we have 2 mpath cloning routes (P). Their is a cloned route to
192.168.178.1 with RTF_CACHED (h) to reach the default gateway.

# ifconfig iwn0 inet 192.168.178.53 delete
# netstat -rn | grep 192.168.178
default192.168.178.1  UGS5 4955 -12 iwn0
192.168.178/24 192.168.178.52 UCn0   51 - 8 iwn0
192.168.178.52 a4:4e:31:38:70:7c  UHLl   0 3754 - 1 iwn0
192.168.178.255192.168.178.52 UHb00 - 1 iwn0
Now 192.168.178.53/24 was deleted, therefore the cloned route to the gateway
(192.168.178.1) is also gone and the default route is 'broken':

# ping 8.8.8.8
# dmesg | tail
arpresolve: 192.168.178.1: route contains no arp information
arpresolve: 192.168.178.1: route contains no arp information
arpresolve: 192.168.178.1: route contains no arp information
arpresolve: 192.168.178.1: route contains no arp information

I think there is no need to delete cloned routes as long as we don't delete
the last cloning route to a network.


Not flushing any RTF_CLONED is a too big hammer and will break setups
where the cloning routes are on two different interfaces.

A better fix is to not delete RTF_CACHED routes until their own parent
is going away.  Diff below does that and includes a regression test.
Does that work for you?  ok?


Successfully tested.

Thanks & ok

friehm



Index: sys/net/route.c
===
RCS file: /cvs/src/sys/net/route.c,v
retrieving revision 1.371
diff -u -p -r1.371 route.c
--- sys/net/route.c 10 Feb 2018 09:17:56 -  1.371
+++ sys/net/route.c 19 Feb 2018 09:55:31 -
@@ -707,26 +707,31 @@ rtequal(struct rtentry *a, struct rtentr
  int
  rtflushclone1(struct rtentry *rt, void *arg, u_int id)
  {
-   struct rtentry *parent = arg;
+   struct rtentry *cloningrt = arg;
struct ifnet *ifp;
int error;
  
-	ifp = if_get(rt->rt_ifidx);

+   if (!ISSET(rt->rt_flags, RTF_CLONED))
+   return 0;
+
+   /* Cached route must stay alive as long as their parent are alive. */
+   if (ISSET(rt->rt_flags, RTF_CACHED) && (rt->rt_parent != cloningrt))
+   return 0;
  
+	if (!rtequal(rt->rt_parent, cloningrt))

+   return 0;
/*
 * 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_ifa_purge().
 */
+   ifp = if_get(rt->rt_ifidx);
if (ifp == NULL)
return 0;
  
-	if (ISSET(rt->rt_flags, RTF_CLONED) && rtequal(rt->rt_parent, parent)) {

-   error = rtdeletemsg(rt, ifp, id);
-   if (error == 0)
-   error = EAGAIN;
-   } else
-   error = 0;
+   error = rtdeletemsg(rt, ifp, id);
+   if (error == 0)
+   error = EAGAIN;
  
  	if_put(ifp);

return error;
Index: regress/sbin/route/Makefile
===
RCS file: /cvs/src/regress/sbin/route/Makefile,v
retrieving revision 1.27
diff -u -p -r1.27 Makefile
--- regress/sbin/route/Makefile 14 Feb 2018 08:42:22 -  1.27
+++ regress/sbin/route/Makefile 19 Feb 2018 09:45:43 -
@@ -376,7 +376,24 @@ rttest${n}:
${RCMD} add -net 192.168.67.128/25 130.102.71.67 -priority 3 -ifp vlan99
${RCMD} show -inet 2>&1 | \
sed -e "s,link\#[0-9 ]*U,link#  U," | \
-   cat > ${.CURDIR}/${.TARGET}.ok
+   diff -u ${.CURDIR}/${.TARGET}.ok /dev/stdin
+
+# Check that removing a RTF_CLONING route do not remove children from
+# another cloning route.
+n= 33
+RTTEST_TARGETS+:=rttest${n}
+rttest${n}:
+   # Use vether(4) because we need IFT_ETHER interfaces
+   # for the auto-magic RTF_CLONING routes.
+   ${SUDO} ifconfig vether99 rdomain ${RDOMAIN} lladdr fe:e1:ba:

Re: mpath cloning routes and cloned routes

2018-02-19 Thread Martin Pieuchot
On 14/02/18(Wed) 21:53, Florian Riehm wrote:
> If we delete cloning routes, we also delete their cloned routes.
> This doesn't make sense if we delete a multipath cloning route and may result 
> in
> broken gateway routes:

That's a bug!

> # netstat -rn | grep 192.168.178
> default192.168.178.1  UGS5 4939 -12 iwn0 
> 192.168.178/24 192.168.178.52 UCPn   1   51 - 8 iwn0 
> 192.168.178/24 192.168.178.53 UCPn   00 - 8 iwn0 
> 192.168.178.1  34:31:c4:24:83:d4  UHLch  1  118 - 7 iwn0 
> 192.168.178.52 a4:4e:31:38:70:7c  UHLl   0 3749 - 1 iwn0 
> 192.168.178.53 a4:4e:31:38:70:7c  UHLl   00 - 1 iwn0 
> 192.168.178.255192.168.178.52 UHPb   00 - 1 iwn0 
> 192.168.178.255192.168.178.53 UHPb   00 - 1 iwn0 
> As you can see above, iwn0 has 192.168.178.52/24 and 192.168.178.53/24 
> assigned
> and therefore we have 2 mpath cloning routes (P). Their is a cloned route to
> 192.168.178.1 with RTF_CACHED (h) to reach the default gateway.
> 
> # ifconfig iwn0 inet 192.168.178.53 delete
> # netstat -rn | grep 192.168.178
> default192.168.178.1  UGS5 4955 -12 iwn0 
> 192.168.178/24 192.168.178.52 UCn0   51 - 8 iwn0 
> 192.168.178.52 a4:4e:31:38:70:7c  UHLl   0 3754 - 1 iwn0 
> 192.168.178.255192.168.178.52 UHb00 - 1 iwn0 
> Now 192.168.178.53/24 was deleted, therefore the cloned route to the gateway
> (192.168.178.1) is also gone and the default route is 'broken':
> 
> # ping 8.8.8.8
> # dmesg | tail
> arpresolve: 192.168.178.1: route contains no arp information
> arpresolve: 192.168.178.1: route contains no arp information
> arpresolve: 192.168.178.1: route contains no arp information
> arpresolve: 192.168.178.1: route contains no arp information
> 
> I think there is no need to delete cloned routes as long as we don't delete
> the last cloning route to a network.

Not flushing any RTF_CLONED is a too big hammer and will break setups
where the cloning routes are on two different interfaces.  

A better fix is to not delete RTF_CACHED routes until their own parent
is going away.  Diff below does that and includes a regression test.
Does that work for you?  ok?

Index: sys/net/route.c
===
RCS file: /cvs/src/sys/net/route.c,v
retrieving revision 1.371
diff -u -p -r1.371 route.c
--- sys/net/route.c 10 Feb 2018 09:17:56 -  1.371
+++ sys/net/route.c 19 Feb 2018 09:55:31 -
@@ -707,26 +707,31 @@ rtequal(struct rtentry *a, struct rtentr
 int
 rtflushclone1(struct rtentry *rt, void *arg, u_int id)
 {
-   struct rtentry *parent = arg;
+   struct rtentry *cloningrt = arg;
struct ifnet *ifp;
int error;
 
-   ifp = if_get(rt->rt_ifidx);
+   if (!ISSET(rt->rt_flags, RTF_CLONED))
+   return 0;
+
+   /* Cached route must stay alive as long as their parent are alive. */
+   if (ISSET(rt->rt_flags, RTF_CACHED) && (rt->rt_parent != cloningrt))
+   return 0;
 
+   if (!rtequal(rt->rt_parent, cloningrt))
+   return 0;
/*
 * 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_ifa_purge().
 */
+   ifp = if_get(rt->rt_ifidx);
if (ifp == NULL)
return 0;
 
-   if (ISSET(rt->rt_flags, RTF_CLONED) && rtequal(rt->rt_parent, parent)) {
-   error = rtdeletemsg(rt, ifp, id);
-   if (error == 0)
-   error = EAGAIN;
-   } else
-   error = 0;
+   error = rtdeletemsg(rt, ifp, id);
+   if (error == 0)
+   error = EAGAIN;
 
if_put(ifp);
return error;
Index: regress/sbin/route/Makefile
===
RCS file: /cvs/src/regress/sbin/route/Makefile,v
retrieving revision 1.27
diff -u -p -r1.27 Makefile
--- regress/sbin/route/Makefile 14 Feb 2018 08:42:22 -  1.27
+++ regress/sbin/route/Makefile 19 Feb 2018 09:45:43 -
@@ -376,7 +376,24 @@ rttest${n}:
${RCMD} add -net 192.168.67.128/25 130.102.71.67 -priority 3 -ifp vlan99
${RCMD} show -inet 2>&1 | \
sed -e "s,link\#[0-9 ]*U,link#  U," | \
-   cat > ${.CURDIR}/${.TARGET}.ok
+   diff -u ${.CURDIR}/${.TARGET}.ok /dev/stdin
+
+# Check that removing a RTF_CLONING route do not remove children from
+# another cloning route.
+n= 33
+RTTEST_TARGETS+:=rttest${n}
+rttest${n}:
+   # Use vether(4) because we need IFT_ETHER interfaces
+   # for the auto-magic RTF_CLONING routes.
+   ${SUDO} ifconfig vether99 rdomain ${RDOMAIN} lladdr fe: