]> git.ipfire.org Git - thirdparty/iproute2.git/commitdiff
route: filter by interface on multipath routes
authorStephen Hemminger <stephen@networkplumber.org>
Fri, 5 Jul 2024 00:26:41 +0000 (17:26 -0700)
committerStephen Hemminger <stephen@networkplumber.org>
Fri, 5 Jul 2024 00:44:55 +0000 (17:44 -0700)
The ip route command would silently hide multipath routes when filter
by interface. The problem was it was not looking for interface when
filter multipath routes.

Example:
ip link add name dummy1 up type dummy
ip link add name dummy2 up type dummy
ip address add 192.0.2.1/28 dev dummy1
ip address add 192.0.2.17/28 dev dummy2
ip route add 198.51.100.0/24 \
nexthop via 192.0.2.2 dev dummy1 \
nexthop via 192.0.2.18 dev dummy2

Before:
ip route show dev dummy1
192.0.2.0/28 proto kernel scope link src 192.0.2.1

After:
ip route show dev dummy1
192.0.2.0/28 proto kernel scope link src 192.0.2.1
198.51.100.0/24
nexthop via 192.0.2.2 dev dummy1 weight 1
nexthop via 192.0.2.18 dev dummy2 weight 1

Reported-by: "Muggeridge, Matt" <matt.muggeridge2@hpe.com>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
ip/iproute.c

index b530461168260a7b1f2989e7d114111e628b1605..4466624049e922f526060e1a64115f20c815e333 100644 (file)
@@ -154,6 +154,24 @@ static int flush_update(void)
        return 0;
 }
 
+static bool filter_multipath(const struct rtattr *rta)
+{
+       const struct rtnexthop *nh = RTA_DATA(rta);
+       int len = RTA_PAYLOAD(rta);
+
+       while (len >= sizeof(*nh)) {
+               if (nh->rtnh_len > len)
+                       break;
+
+               if (!((nh->rtnh_ifindex ^ filter.oif) & filter.oifmask))
+                       return true;
+
+               len -= NLMSG_ALIGN(nh->rtnh_len);
+               nh = RTNH_NEXT(nh);
+       }
+       return false;
+}
+
 static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len)
 {
        struct rtmsg *r = NLMSG_DATA(n);
@@ -310,12 +328,15 @@ static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len)
                        return 0;
        }
        if (filter.oifmask) {
-               int oif = 0;
+               if (tb[RTA_OIF]) {
+                       int oif = rta_getattr_u32(tb[RTA_OIF]);
 
-               if (tb[RTA_OIF])
-                       oif = rta_getattr_u32(tb[RTA_OIF]);
-               if ((oif^filter.oif)&filter.oifmask)
-                       return 0;
+                       if ((oif ^ filter.oif) & filter.oifmask)
+                               return 0;
+               } else if (tb[RTA_MULTIPATH]) {
+                       if (!filter_multipath(tb[RTA_MULTIPATH]))
+                               return 0;
+               }
        }
        if (filter.markmask) {
                int mark = 0;