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

Reply via email to