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