From: Kristian Evensen <kristian.even...@gmail.com> This patch implements the comments received for version 1 (see https://lists.openwrt.org/pipermail/openwrt-devel/2013-May/020119.html).
* Routes are only added to the specified table. The only exception are the IPv4-routes Linux generates automatically when an address with a mask less than 32 comes up. These routes are added to the default table and in order to avoid depending on OS-behavior, the routes are not deleted. Also, they are needed (in the default table) to for example be able to add default routes to interfaces. * The rules are divided into three 'parts', in order to support the case where a client is connected to networks with overlapping subnets. The first part consists of the "from IP-address"-rules, so that packets from sockets bound to IPs on the device go through the intended interface. Then follows lookups in main and default, before the networks are checked. Finally, the "from lo"-rules are check so that traffic from sockets not bound to an IP also is routed correctly. * The priority of the "from lo"-rules is set to 65535 + table_id. When there are multiple rules with the same priority and same source, the kernel seems to display undefined behavior. Even though table and priority was specified when netifd sends DELRULE, it seems random which rule is actually deleted. I have been testing this patch on my router the whole day today and it behaves as intended. However, all my WAN-connections are IPv4 and I was only able to test IPv6 in a small, private network. If anyone has time to check the behavior in a full IPv6-network, I would be very grateful. Fixed a whitespace --- interface-ip.c | 75 +++++++++++++++++++++++++++++++++++++++------------------- interface-ip.h | 2 +- iprule.h | 3 +++ proto.c | 6 ++--- 4 files changed, 57 insertions(+), 29 deletions(-) diff --git a/interface-ip.c b/interface-ip.c index e265563..7cedf3f 100644 --- a/interface-ip.c +++ b/interface-ip.c @@ -90,28 +90,39 @@ match_if_addr(union if_addr *a1, union if_addr *a2, int mask) return !memcmp(p1, p2, sizeof(*p1)); } -static int set_ipv6_source_policy(bool add, const union if_addr *addr, uint8_t mask, int ifindex) +static int set_ip_source_policy(bool add, bool v4, unsigned int priority, + const union if_addr *addr, uint8_t mask, int ifindex) { struct iprule rule = { - .flags = IPRULE_INET6 | IPRULE_SRC | IPRULE_LOOKUP | IPRULE_PRIORITY, - .priority = 65535, - .lookup = interface_ip_resolve_v6_rtable(ifindex), + .flags = IPRULE_SRC | IPRULE_LOOKUP | IPRULE_PRIORITY, + .priority = priority, + .lookup = interface_ip_resolve_rtable(ifindex), .src_addr = *addr, .src_mask = mask, }; + if(v4) + rule.flags |= IPRULE_INET4; + else + rule.flags |= IPRULE_INET6; + return (add) ? system_add_iprule(&rule) : system_del_iprule(&rule); } -static int set_ipv6_lo_policy(bool add, int ifindex) +static int set_ip_lo_policy(bool add, bool v4, int ifindex) { struct iprule rule = { - .flags = IPRULE_INET6 | IPRULE_IN | IPRULE_LOOKUP | IPRULE_PRIORITY, - .priority = 65535, - .lookup = interface_ip_resolve_v6_rtable(ifindex), + .flags = IPRULE_IN | IPRULE_LOOKUP | IPRULE_PRIORITY, + .priority = IPRULE_PRIORITY_NW + interface_ip_resolve_rtable(ifindex), + .lookup = interface_ip_resolve_rtable(ifindex), .in_dev = "lo" }; + if(v4) + rule.flags |= IPRULE_INET4; + else + rule.flags |= IPRULE_INET6; + return (add) ? system_add_iprule(&rule) : system_del_iprule(&rule); } @@ -246,7 +257,6 @@ interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6) struct blob_attr *tb[__ROUTE_MAX], *cur; struct device_route *route; int af = v6 ? AF_INET6 : AF_INET; - bool is_v6_proto_route = v6 && iface; blobmsg_parse(route_attr, __ROUTE_MAX, tb, blobmsg_data(attr), blobmsg_data_len(attr)); @@ -300,10 +310,8 @@ interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6) } // Use source-based routing - if (is_v6_proto_route) { - route->table = interface_ip_resolve_v6_rtable(iface->l3_dev.dev->ifindex); - route->flags |= DEVROUTE_SRCTABLE; - } + route->table = interface_ip_resolve_rtable(iface->l3_dev.dev->ifindex); + route->flags |= DEVROUTE_SRCTABLE; if ((cur = tb[ROUTE_TABLE]) != NULL) { if (!system_resolve_rt_table(blobmsg_data(cur), &route->table)) { @@ -364,9 +372,11 @@ interface_handle_subnet_route(struct interface *iface, struct device_addr *addr, memset(&route, 0, sizeof(route)); route.iface = iface; - route.flags = addr->flags; + route.flags = addr->flags | DEVROUTE_SRCTABLE; route.mask = addr->mask; + route.table = interface_ip_resolve_rtable(dev->ifindex); memcpy(&route.addr, &addr->addr, sizeof(route.addr)); + clear_if_addr(&route.addr, route.mask); if (add) { @@ -394,6 +404,7 @@ interface_update_proto_addr(struct vlist_tree *tree, struct device *dev; struct device_addr *a_new = NULL, *a_old = NULL; bool keep = false; + bool v4 = false; ip = container_of(tree, struct interface_ip_settings, addr); iface = ip->iface; @@ -433,8 +444,17 @@ interface_update_proto_addr(struct vlist_tree *tree, if (!(a_old->flags & DEVADDR_EXTERNAL) && a_old->enabled && !keep) { interface_handle_subnet_route(iface, a_old, false); - if ((a_old->flags & DEVADDR_FAMILY) == DEVADDR_INET6) - set_ipv6_source_policy(false, &a_old->addr, a_old->mask, dev->ifindex); + if ((a_old->flags & DEVADDR_FAMILY) == DEVADDR_INET4) + v4 = true; + + //This is needed for source routing to work correctly. If a device + //has two connections to a network using the same subnet, adding + //only the network-rule will cause packets to be routed through the + //first matching network (source IP matches both masks). + set_ip_source_policy(false, v4, IPRULE_PRIORITY_ADDR, &a_old->addr, + 32, dev->ifindex); + set_ip_source_policy(false, v4, IPRULE_PRIORITY_NW, &a_old->addr, + a_old->mask, dev->ifindex); system_del_address(dev, a_old); } @@ -446,11 +466,17 @@ interface_update_proto_addr(struct vlist_tree *tree, if (!(a_new->flags & DEVADDR_EXTERNAL) && !keep) { system_add_address(dev, a_new); - if ((a_new->flags & DEVADDR_FAMILY) == DEVADDR_INET6) - set_ipv6_source_policy(true, &a_new->addr, a_new->mask, dev->ifindex); + if ((a_new->flags & DEVADDR_FAMILY) == DEVADDR_INET4) + v4 = true; + + set_ip_source_policy(true, v4, IPRULE_PRIORITY_ADDR, &a_new->addr, + 32, dev->ifindex); + set_ip_source_policy(true, v4, IPRULE_PRIORITY_NW, &a_new->addr, + a_new->mask, dev->ifindex); if ((a_new->flags & DEVADDR_OFFLINK) || iface->metric) interface_handle_subnet_route(iface, a_new, true); + } } } @@ -758,8 +784,8 @@ interface_update_prefix(struct vlist_tree *tree, // Set null-route to avoid routing loops and set routing policy system_add_route(NULL, &route); if (prefix_new->iface) - set_ipv6_source_policy(true, &route.addr, route.mask, - prefix_new->iface->l3_dev.dev->ifindex); + set_ip_source_policy(true, false, IPRULE_PRIORITY_NW ,&route.addr, + route.mask, prefix_new->iface->l3_dev.dev->ifindex); interface_update_prefix_assignments(prefix_new, true); @@ -768,8 +794,8 @@ interface_update_prefix(struct vlist_tree *tree, // Remove null-route if (prefix_old->iface) - set_ipv6_source_policy(false, &route.addr, route.mask, - prefix_old->iface->l3_dev.dev->ifindex); + set_ip_source_policy(false, false, IPRULE_PRIORITY_NW, &route.addr, + route.mask, prefix_old->iface->l3_dev.dev->ifindex); system_del_route(NULL, &route); } @@ -841,7 +867,7 @@ interface_ip_set_ula_prefix(const char *prefix) } } -int interface_ip_resolve_v6_rtable(int ifindex) +int interface_ip_resolve_rtable(int ifindex) { return ifindex + 1000; } @@ -1041,7 +1067,8 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled) if (!strcmp(a->name, ip->iface->name)) interface_set_prefix_address(a, c, ip->iface, enabled); - set_ipv6_lo_policy(enabled, dev->ifindex); + set_ip_lo_policy(enabled, true, dev->ifindex); + set_ip_lo_policy(enabled, false, dev->ifindex); } void diff --git a/interface-ip.h b/interface-ip.h index c2213fd..e2ced65 100644 --- a/interface-ip.h +++ b/interface-ip.h @@ -144,6 +144,6 @@ struct device_prefix* interface_ip_add_device_prefix(struct interface *iface, struct in6_addr *excl_addr, uint8_t excl_length); void interface_ip_set_ula_prefix(const char *prefix); void interface_refresh_assignments(bool hint); -int interface_ip_resolve_v6_rtable(int ifindex); +int interface_ip_resolve_rtable(int ifindex); #endif diff --git a/iprule.h b/iprule.h index 4b10760..1e7cbe7 100644 --- a/iprule.h +++ b/iprule.h @@ -17,6 +17,9 @@ #include "interface-ip.h" +#define IPRULE_PRIORITY_ADDR 32764 +#define IPRULE_PRIORITY_NW 65535 + enum iprule_flags { /* address family for rule */ IPRULE_INET4 = (0 << 0), diff --git a/proto.c b/proto.c index 676852d..fc49458 100644 --- a/proto.c +++ b/proto.c @@ -252,10 +252,8 @@ parse_gateway_option(struct interface *iface, struct blob_attr *attr, bool v6) route->mask = 0; route->flags = (v6 ? DEVADDR_INET6 : DEVADDR_INET4); - if (v6) { - route->table = interface_ip_resolve_v6_rtable(iface->l3_dev.dev->ifindex); - route->flags |= DEVROUTE_SRCTABLE; - } + route->table = interface_ip_resolve_rtable(iface->l3_dev.dev->ifindex); + route->flags |= DEVROUTE_SRCTABLE; vlist_add(&iface->proto_ip.route, &route->node, route); -- 1.8.1.2 _______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org https://lists.openwrt.org/mailman/listinfo/openwrt-devel