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 |
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
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
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 --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 */
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(-)