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 casusing 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 also makes the unreachable route for the delegated prefix in the
routing table superfluous; before this change the unreachable route was
inefficient as it was overshadowed by the default route installed by
the protocol handler scripts.

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 <dedec...@gmail.com>
---
 interface-ip.c | 70 ++++++++++++++++++++++++++++++++++++--------------
 iprule.h       | 10 +++++---
 2 files changed, 57 insertions(+), 23 deletions(-)

diff --git a/interface-ip.c b/interface-ip.c
index 024c5b8..91f7e8e 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,9 @@ 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;
+
        prefix_old = container_of(node_old, struct device_prefix, node);
        prefix_new = container_of(node_new, struct device_prefix, node);
 
@@ -1220,17 +1250,6 @@ 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 
*/
                list_splice(&prefix_old->assignments, &prefix_new->assignments);
@@ -1243,15 +1262,28 @@ interface_update_prefix(struct vlist_tree *tree,
                                prefix_new->valid_until != 
prefix_old->valid_until)
                        ip->iface->updated |= IUF_PREFIX;
        } else if (node_new) {
-               /* Set null-route to avoid routing loops */
-               system_add_route(NULL, &route);
+               /* Set unreachable rule 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);
-               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 */
-- 
2.25.1


_______________________________________________
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to