diff mbox series

[netifd,v2] interface-ip: fix ipv6 routing loop

Message ID 20210103200744.240982-1-dedeckeh@gmail.com
State Rejected
Delegated to: Hans Dedecker
Headers show
Series [netifd,v2] interface-ip: fix ipv6 routing loop | expand

Commit Message

Hans Dedecker Jan. 3, 2021, 8:07 p.m. UTC
In case of prefix delegation an upstream ISP will route the complete
delegated prefix (e.g 2001:DB8:BEEF::/56) to an OpenWrt device, OpenWrt
will route back the complete /56 not matching a local or subdelegated
prefix and with as source an address from the delegated prefix
causing a routing loop.
Fix this by using an ip rule which directs traffic matching the
subdelegated prefix and coming from the wan interface to the main or
user configured routing table.
An ip rule with lower priority will make sure the traffic not matching
the subdelegated prefix(es) will be dropped with an ICMPv6 unreachable
fixing the potential routing loop.

This will result into the following typical IPv6 rules :

0:      from all lookup local
30000:  from all to 2001:DB8:BEEF::/64 iif eth4 lookup main
30001:  from all to 2001:DB8:BEEF::/56 iif eth4 unreachable
32766:  from all lookup main
4200000000:     from 2001:DB8:BEEF::1/64 iif br-lan unreachable
4200000001:     from all iif lo failed_policy
4200000011:     from all iif eth0 failed_policy
4200000015:     from all iif eth4 failed_policy
4200000015:     from all iif eth4 failed_policy
4200000019:     from all iif br-lan failed_policy

Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
---
v2: Keep unreachable route in the routing table dropping traffic from the lan 
not matching any routing rules with an ICMPv6 unreachable

 interface-ip.c | 77 ++++++++++++++++++++++++++++++++++++++++++--------
 iprule.h       | 10 ++++---
 2 files changed, 71 insertions(+), 16 deletions(-)

Comments

Maxim Mikityanskiy March 29, 2023, 2:44 p.m. UTC | #1
Hello Hans!

On Sun, 03 Jan 2021 12:14:18 -0800, Hans Dedecker wrote:
> In case of prefix delegation an upstream ISP will route the complete
> delegated prefix (e.g 2001:DB8:BEEF::/56) to an OpenWrt device, OpenWrt
> will route back the complete /56 not matching a local or subdelegated
> prefix and with as source an address from the delegated prefix
> causing a routing loop.
> Fix this by using an ip rule which directs traffic matching the
> subdelegated prefix and coming from the wan interface to the main or
> user configured routing table.
> An ip rule with lower priority will make sure the traffic not matching
> the subdelegated prefix(es) will be dropped with an ICMPv6 unreachable
> fixing the potential routing loop.
> 
> 
> This will result into the following typical IPv6 rules :
> 
> 0:      from all lookup local
> 30000:  from all to 2001:DB8:BEEF::/64 iif eth4 lookup main
> 30001:  from all to 2001:DB8:BEEF::/56 iif eth4 unreachable
> 32766:  from all lookup main
> 4200000000:     from 2001:DB8:BEEF::1/64 iif br-lan unreachable

Could you please hint me why the rule with ID 4200000000 is useful? I
understand the purpose of rule 30001 explained in this commit message,
but I can't imagine the situation in which rule 4200000000 would be
triggered, because the main routing table has the default route that
would be the final match.

Thanks,
Max

> 4200000001:     from all iif lo failed_policy
> 4200000011:     from all iif eth0 failed_policy
> 4200000015:     from all iif eth4 failed_policy
> 4200000015:     from all iif eth4 failed_policy
> 4200000019:     from all iif br-lan failed_policy
> 
> Signed-off-by: Hans Dedecker <dedec...@gmail.com>
> ---
> v2: Keep unreachable route in the routing table dropping traffic from the lan
> not matching any routing rules with an ICMPv6 unreachable
Hans Dedecker March 29, 2023, 7:14 p.m. UTC | #2
Hi,

On Wed, Mar 29, 2023 at 4:44 PM Maxim Mikityanskiy <maxtram95@gmail.com> wrote:
>
> Hello Hans!
>
> On Sun, 03 Jan 2021 12:14:18 -0800, Hans Dedecker wrote:
> > In case of prefix delegation an upstream ISP will route the complete
> > delegated prefix (e.g 2001:DB8:BEEF::/56) to an OpenWrt device, OpenWrt
> > will route back the complete /56 not matching a local or subdelegated
> > prefix and with as source an address from the delegated prefix
> > causing a routing loop.
> > Fix this by using an ip rule which directs traffic matching the
> > subdelegated prefix and coming from the wan interface to the main or
> > user configured routing table.
> > An ip rule with lower priority will make sure the traffic not matching
> > the subdelegated prefix(es) will be dropped with an ICMPv6 unreachable
> > fixing the potential routing loop.
> >
> >
> > This will result into the following typical IPv6 rules :
> >
> > 0:      from all lookup local
> > 30000:  from all to 2001:DB8:BEEF::/64 iif eth4 lookup main
> > 30001:  from all to 2001:DB8:BEEF::/56 iif eth4 unreachable
> > 32766:  from all lookup main
> > 4200000000:     from 2001:DB8:BEEF::1/64 iif br-lan unreachable
>
> Could you please hint me why the rule with ID 4200000000 is useful? I
> understand the purpose of rule 30001 explained in this commit message,
> but I can't imagine the situation in which rule 4200000000 would be
> triggered, because the main routing table has the default route that
> would be the final match.
If IPv6 source based routing is used the default route will only be
hit when the source IP matches the source attached to the default
route.
If this is not the case the unreachable ip rule will be hit if the
source IP matches the source attached to the rule

Hans
>
> Thanks,
> Max
>
> > 4200000001:     from all iif lo failed_policy
> > 4200000011:     from all iif eth0 failed_policy
> > 4200000015:     from all iif eth4 failed_policy
> > 4200000015:     from all iif eth4 failed_policy
> > 4200000019:     from all iif br-lan failed_policy
> >
> > Signed-off-by: Hans Dedecker <dedec...@gmail.com>
> > ---
> > v2: Keep unreachable route in the routing table dropping traffic from the lan
> > not matching any routing rules with an ICMPv6 unreachable
Maxim Mikityanskiy March 30, 2023, 11:25 a.m. UTC | #3
On Wed, 29 Mar 2023 at 21:14:28 +0200, Hans Dedecker wrote:
> Hi,
> 
> On Wed, Mar 29, 2023 at 4:44 PM Maxim Mikityanskiy <maxtram95@gmail.com> wrote:
> >
> > Hello Hans!
> >
> > On Sun, 03 Jan 2021 12:14:18 -0800, Hans Dedecker wrote:
> > > In case of prefix delegation an upstream ISP will route the complete
> > > delegated prefix (e.g 2001:DB8:BEEF::/56) to an OpenWrt device, OpenWrt
> > > will route back the complete /56 not matching a local or subdelegated
> > > prefix and with as source an address from the delegated prefix
> > > causing a routing loop.
> > > Fix this by using an ip rule which directs traffic matching the
> > > subdelegated prefix and coming from the wan interface to the main or
> > > user configured routing table.
> > > An ip rule with lower priority will make sure the traffic not matching
> > > the subdelegated prefix(es) will be dropped with an ICMPv6 unreachable
> > > fixing the potential routing loop.
> > >
> > >
> > > This will result into the following typical IPv6 rules :
> > >
> > > 0:      from all lookup local
> > > 30000:  from all to 2001:DB8:BEEF::/64 iif eth4 lookup main
> > > 30001:  from all to 2001:DB8:BEEF::/56 iif eth4 unreachable
> > > 32766:  from all lookup main
> > > 4200000000:     from 2001:DB8:BEEF::1/64 iif br-lan unreachable
> >
> > Could you please hint me why the rule with ID 4200000000 is useful? I
> > understand the purpose of rule 30001 explained in this commit message,
> > but I can't imagine the situation in which rule 4200000000 would be
> > triggered, because the main routing table has the default route that
> > would be the final match.
> If IPv6 source based routing is used the default route will only be
> hit when the source IP matches the source attached to the default
> route.
> If this is not the case the unreachable ip rule will be hit if the
> source IP matches the source attached to the rule

Thanks for the explanation! That seems to be the same source IP in my
setup, though. I get a /48 prefix from tunnelbroker (XXXX), of which I
delegate /64 to LAN (YYYY), and I have these routes in the main table:

default from 2001:470:XXXX::/48 dev 6in4-wan6 proto static metric 1024 pref medium
2001:470:TTTT:TTTT::/64 dev 6in4-wan6 proto kernel metric 256 pref medium
2001:470:XXXX:YYYY::/64 dev br-lan proto static metric 1024 pref medium
unreachable 2001:470:XXXX::/48 dev lo proto static metric 2147483647 pref medium

and these rules:

32766:	from all lookup main
4200000000:	from 2001:470:XXXX:YYYY::1/64 iif br-lan unreachable

That is, the default rule should match on a superset of IPs compared to
the unreachable rule 4200000000.

What is the use case when this rule becomes effective?

> 
> Hans
> >
> > Thanks,
> > Max
> >
> > > 4200000001:     from all iif lo failed_policy
> > > 4200000011:     from all iif eth0 failed_policy
> > > 4200000015:     from all iif eth4 failed_policy
> > > 4200000015:     from all iif eth4 failed_policy
> > > 4200000019:     from all iif br-lan failed_policy
> > >
> > > Signed-off-by: Hans Dedecker <dedec...@gmail.com>
> > > ---
> > > v2: Keep unreachable route in the routing table dropping traffic from the lan
> > > not matching any routing rules with an ICMPv6 unreachable
diff mbox series

Patch

diff --git a/interface-ip.c b/interface-ip.c
index 024c5b8..8a68340 100644
--- a/interface-ip.c
+++ b/interface-ip.c
@@ -903,6 +903,8 @@  interface_set_prefix_address(struct device_prefix_assignment *assignment,
 		const struct device_prefix *prefix, struct interface *iface, bool add)
 {
 	const struct interface *uplink = prefix->iface;
+	unsigned int rt_table_id;
+
 	if (!iface->l3_dev.dev)
 		return;
 
@@ -935,9 +937,21 @@  interface_set_prefix_address(struct device_prefix_assignment *assignment,
 					addr.mask < 64 ? 64 : addr.mask, iface->ip6table, NULL, NULL, false);
 
 		if (prefix->iface) {
-			if (prefix->iface->ip6table)
+			rt_table_id = prefix->iface->ip6table;
+
+			if (rt_table_id)
 				set_ip_source_policy(false, true, IPRULE_PRIORITY_NW, &addr.addr,
-						addr.mask, prefix->iface->ip6table, iface, NULL, true);
+						addr.mask, rt_table_id, iface, NULL, true);
+
+			if (!rt_table_id)
+				system_resolve_rt_table("main", &rt_table_id);
+
+			/*
+			 * Remove ip rule allowing traffic coming from the prefix
+			 * interface and directed to the sub delegated prefix
+			 */
+			set_ip_source_policy(false, true, IPRULE_PRIORITY_SUB_PREFIX, &addr.addr,
+						addr.mask, rt_table_id, prefix->iface, NULL, false);
 
 			set_ip_source_policy(false, true, IPRULE_PRIORITY_REJECT, &addr.addr,
 							addr.mask, 0, iface, "unreachable", true);
@@ -972,12 +986,25 @@  interface_set_prefix_address(struct device_prefix_assignment *assignment,
 						addr.mask < 64 ? 64 : addr.mask, iface->ip6table, NULL, NULL, false);
 
 			if (prefix->iface) {
+				rt_table_id = prefix->iface->ip6table;
+
 				set_ip_source_policy(true, true, IPRULE_PRIORITY_REJECT, &addr.addr,
 						addr.mask, 0, iface, "unreachable", true);
 
-				if (prefix->iface->ip6table)
+				if (rt_table_id)
 					set_ip_source_policy(true, true, IPRULE_PRIORITY_NW, &addr.addr,
-							addr.mask, prefix->iface->ip6table, iface, NULL, true);
+							addr.mask, rt_table_id, iface, NULL, true);
+
+				if (!rt_table_id)
+					system_resolve_rt_table("main", &rt_table_id);
+
+				/*
+				 * Add ip rule allowing traffic coming from the prefix
+				 * interface and directed to the sub delegated prefix
+				 */
+				set_ip_source_policy(true, true, IPRULE_PRIORITY_SUB_PREFIX, &addr.addr,
+						addr.mask, rt_table_id, prefix->iface, NULL, false);
+
 			}
 		}
 
@@ -1213,6 +1240,10 @@  interface_update_prefix(struct vlist_tree *tree,
 			     struct vlist_node *node_old)
 {
 	struct device_prefix *prefix_old, *prefix_new;
+	struct device_prefix_assignment *c;
+	struct interface *iface;
+	struct device_route route;
+
 	prefix_old = container_of(node_old, struct device_prefix, node);
 	prefix_new = container_of(node_new, struct device_prefix, node);
 
@@ -1220,16 +1251,9 @@  interface_update_prefix(struct vlist_tree *tree,
 	if (tree && (!node_new || !node_old))
 		ip->iface->updated |= IUF_PREFIX;
 
-	struct device_route route;
 	memset(&route, 0, sizeof(route));
 	route.flags = DEVADDR_INET6;
 	route.metric = INT32_MAX;
-	route.mask = (node_new) ? prefix_new->length : prefix_old->length;
-	route.addr.in6 = (node_new) ? prefix_new->addr : prefix_old->addr;
-
-
-	struct device_prefix_assignment *c;
-	struct interface *iface;
 
 	if (node_old && node_new) {
 		/* Move assignments and refresh addresses to update valid times */
@@ -1244,14 +1268,43 @@  interface_update_prefix(struct vlist_tree *tree,
 			ip->iface->updated |= IUF_PREFIX;
 	} else if (node_new) {
 		/* Set null-route to avoid routing loops */
+		route.mask = prefix_new->length;
+		route.addr.in6 = prefix_new->addr;
+
 		system_add_route(NULL, &route);
 
+		/*
+		 * Set unreachable rule for the delegated prefix with
+		 * as incoming interface the interface on which the
+		 * delegated prefix is received to avoid routing loops
+		 */
+		if (prefix_new->iface)
+		{
+			union if_addr prefix = { .in6 = prefix_new->addr, };
+
+			set_ip_source_policy(true, true, IPRULE_PRIORITY_PREFIX_UNREACHABLE, &prefix,
+					prefix_new->length, 0, prefix_new->iface, "unreachable", true);
+		}
+
 		if (!prefix_new->iface || !prefix_new->iface->proto_ip.no_delegation)
 			interface_update_prefix_assignments(prefix_new, true);
 	} else if (node_old) {
-		/* Remove null-route */
 		interface_update_prefix_assignments(prefix_old, false);
+
+		/* Remove null-route */
+		route.mask = prefix_old->length;
+		route.addr.in6 = prefix_old->addr;
+
 		system_del_route(NULL, &route);
+
+		/* Delete unreachable rule added to avoid routing loops */
+		if (prefix_old->iface)
+		{
+			union if_addr prefix = { .in6 = prefix_old->addr, };
+
+			set_ip_source_policy(false, true, IPRULE_PRIORITY_PREFIX_UNREACHABLE, &prefix,
+					prefix_old->length, 0, prefix_old->iface, "unreachable", true);
+		}
 	}
 
 	if (node_old) {
diff --git a/iprule.h b/iprule.h
index 89b94b4..b6e1953 100644
--- a/iprule.h
+++ b/iprule.h
@@ -17,10 +17,12 @@ 
 
 #include "interface-ip.h"
 
-#define IPRULE_PRIORITY_ADDR		10000
-#define IPRULE_PRIORITY_ADDR_MASK	20000
-#define IPRULE_PRIORITY_NW		90000
-#define IPRULE_PRIORITY_REJECT		4200000000
+#define IPRULE_PRIORITY_ADDR			10000
+#define IPRULE_PRIORITY_ADDR_MASK		20000
+#define IPRULE_PRIORITY_SUB_PREFIX		30000
+#define IPRULE_PRIORITY_PREFIX_UNREACHABLE	30001
+#define IPRULE_PRIORITY_NW			90000
+#define IPRULE_PRIORITY_REJECT			4200000000
 
 enum iprule_flags {
 	/* address family for rule */