From: Kristian Evensen <kristian.even...@gmail.com>

Multi-wan support was recently added to netifd, but limited to IPv6. This patch
enables multi-wan for IPv4 as well. In addition, the patch introduces some
changes that make the multi-wan support more robust.

1) Instead of using the interface index to decide on interface metric, a
table-option is added to interfaces. This way, users are sure which tables will
be used for policy routing and can avoid overlaps. The table-option must be set
for an interface to be 'multi-wan', and all routes belonging to the interface
will be added to this table.

2) Routes are added both to the original table (for example main) and the
interface-specific table. This is done to ensure networked applications running
on the node will behave as intended. If routes are only added to the
interface-specific tables, traffic from applications not binding to an interface
will not be routed correctly.

The IPv6 multi-wan support has been converted to use the generic functions.
---
 interface-ip.c | 35 +++++++++++++++++++++++------------
 interface.c    | 10 ++++++++++
 interface.h    |  4 ++++
 proto.c        |  1 +
 system-linux.c | 28 ++++++++++++++++++++++------
 5 files changed, 60 insertions(+), 18 deletions(-)

diff --git a/interface-ip.c b/interface-ip.c
index e265563..f88162b 100644
--- a/interface-ip.c
+++ b/interface-ip.c
@@ -90,16 +90,19 @@ 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, const union if_addr *addr,
+        uint8_t mask, int ifindex, unsigned int table)
 {
        struct iprule rule = {
-               .flags = IPRULE_INET6 | IPRULE_SRC | IPRULE_LOOKUP | 
IPRULE_PRIORITY,
+               .flags = IPRULE_SRC | IPRULE_LOOKUP | IPRULE_PRIORITY,
                .priority = 65535,
-               .lookup = interface_ip_resolve_v6_rtable(ifindex),
+               .lookup = table,
                .src_addr = *addr,
                .src_mask = mask,
        };
 
+       rule.flags |= (v4) ? IPRULE_INET4 : IPRULE_INET6;
+
        return (add) ? system_add_iprule(&rule) : system_del_iprule(&rule);
 }
 
@@ -267,6 +270,7 @@ interface_ip_add_route(struct interface *iface, struct 
blob_attr *attr, bool v6)
        if (!route)
                return;
 
+       route->iface = iface;
        route->flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
        route->mask = v6 ? 128 : 32;
        if ((cur = tb[ROUTE_MASK]) != NULL) {
@@ -433,8 +437,11 @@ 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(iface->interface_table > 0){
+                               bool v4 = (a_old->flags & DEVADDR_FAMILY) == 
DEVADDR_INET4;
+                               set_ip_source_policy(false, v4, &a_old->addr, 
a_old->mask,
+                                               dev->ifindex, 
iface->interface_table);
+                       }
 
                        system_del_address(dev, a_old);
                }
@@ -446,8 +453,11 @@ 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(iface->interface_table > 0){
+                               bool v4 = (a_new->flags & DEVADDR_FAMILY) == 
DEVADDR_INET4;
+                               set_ip_source_policy(true, v4, &a_new->addr, 
a_new->mask,
+                                               dev->ifindex, 
iface->interface_table);
+                       }
 
                        if ((a_new->flags & DEVADDR_OFFLINK) || iface->metric)
                                interface_handle_subnet_route(iface, a_new, 
true);
@@ -758,8 +768,9 @@ 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, &route.addr, 
route.mask,
+                                       prefix_new->iface->l3_dev.dev->ifindex,
+                                       prefix_new->iface->interface_table);
 
 
                interface_update_prefix_assignments(prefix_new, true);
@@ -768,8 +779,9 @@ 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, &route.addr, 
route.mask,
+                                       prefix_old->iface->l3_dev.dev->ifindex,
+                                       prefix_old->iface->interface_table);
                system_del_route(NULL, &route);
        }
 
@@ -968,7 +980,6 @@ interface_write_resolv_conf(void)
                    vlist_simple_empty(&iface->config_ip.dns_servers))
                        continue;
 
-               fprintf(f, "# Interface %s\n", iface->name);
                write_resolv_conf_entries(f, &iface->config_ip);
                if (!iface->proto_ip.no_dns)
                        write_resolv_conf_entries(f, &iface->proto_ip);
diff --git a/interface.c b/interface.c
index a2c3f44..30ef9f8 100644
--- a/interface.c
+++ b/interface.c
@@ -39,6 +39,7 @@ enum {
        IFACE_ATTR_INTERFACE,
        IFACE_ATTR_IP6ASSIGN,
        IFACE_ATTR_IP6HINT,
+       IFACE_ATTR_TABLE,
        IFACE_ATTR_MAX
 };
 
@@ -54,6 +55,7 @@ static const struct blobmsg_policy 
iface_attrs[IFACE_ATTR_MAX] = {
        [IFACE_ATTR_INTERFACE] = { .name = "interface", .type = 
BLOBMSG_TYPE_STRING },
        [IFACE_ATTR_IP6ASSIGN] = { .name = "ip6assign", .type = 
BLOBMSG_TYPE_INT32 },
        [IFACE_ATTR_IP6HINT] = { .name = "ip6hint", .type = BLOBMSG_TYPE_STRING 
},
+       [IFACE_ATTR_TABLE] = { .name = "table", .type = BLOBMSG_TYPE_STRING },
 };
 
 static const union config_param_info iface_attr_info[IFACE_ATTR_MAX] = {
@@ -497,6 +499,13 @@ interface_init(struct interface *iface, const char *name,
                iface->config_ip.assignment_hint = 
strtol(blobmsg_get_string(cur), NULL, 16) &
                                ~((1 << (64 - 
iface->config_ip.assignment_length)) - 1);
 
+       if((cur = tb[IFACE_ATTR_TABLE])){
+               if(!system_resolve_rt_table(blobmsg_data(cur), 
&iface->interface_table))
+                       netifd_log_message(L_WARNING, "Could not resolve 
table\n");
+       } else
+               //Routing table 0 is the 'all' table, so is safe to use as 
not-set value
+               iface->interface_table = 0;
+
        iface->config_autostart = iface->autostart;
 }
 
@@ -512,6 +521,7 @@ static bool __interface_add(struct interface *iface, struct 
blob_attr *config, b
                if ((cur = tb[IFACE_ATTR_INTERFACE]))
                        iface->parent_ifname = blobmsg_data(cur);
 
+
                if (!iface->parent_ifname)
                        return false;
        } else {
diff --git a/interface.h b/interface.h
index 0c56b36..ee69e5b 100644
--- a/interface.h
+++ b/interface.h
@@ -119,6 +119,10 @@ struct interface {
 
        int metric;
 
+       /* Proper multihoming support requires policy routing. Store all routes
+        * going through this interface in this table */
+       unsigned int interface_table;
+
        /* errors/warnings while trying to bring up the interface */
        struct list_head errors;
 
diff --git a/proto.c b/proto.c
index 676852d..322a41f 100644
--- a/proto.c
+++ b/proto.c
@@ -249,6 +249,7 @@ parse_gateway_option(struct interface *iface, struct 
blob_attr *attr, bool v6)
                return false;
        }
 
+       route->iface = iface;
        route->mask = 0;
        route->flags = (v6 ? DEVADDR_INET6 : DEVADDR_INET4);
 
diff --git a/system-linux.c b/system-linux.c
index f5c900d..bd169f9 100644
--- a/system-linux.c
+++ b/system-linux.c
@@ -266,6 +266,7 @@ static int system_rtnl_call(struct nl_msg *msg)
        int ret;
 
        ret = nl_send_auto_complete(sock_rtnl, msg);
+
        nlmsg_free(msg);
 
        if (ret < 0)
@@ -972,7 +973,9 @@ static int system_rt(struct device *dev, struct 
device_route *route, int cmd)
        struct rtmsg rtm = {
                .rtm_family = (alen == 4) ? AF_INET : AF_INET6,
                .rtm_dst_len = route->mask,
-               .rtm_table = (table < 256) ? table : RT_TABLE_UNSPEC,
+               //Kernel will only use one route, so no need to set twice 
(RTA_TABLE).
+               //Also, route is automatically added to RT_TABLE_UNSPEC.
+               .rtm_table = table,
                .rtm_protocol = (route->flags & DEVADDR_KERNEL) ? RTPROT_KERNEL 
: RTPROT_STATIC,
                .rtm_scope = scope,
                .rtm_type = (cmd == RTM_DELROUTE) ? 0: RTN_UNICAST,
@@ -980,7 +983,7 @@ static int system_rt(struct device *dev, struct 
device_route *route, int cmd)
        struct nl_msg *msg;
 
        if (cmd == RTM_NEWROUTE) {
-               flags |= NLM_F_CREATE | NLM_F_REPLACE;
+               flags |= NLM_F_CREATE | NLM_F_APPEND | NLM_F_REPLACE;
 
                if (!dev) { // Add null-route
                        rtm.rtm_scope = RT_SCOPE_UNIVERSE;
@@ -1006,10 +1009,23 @@ static int system_rt(struct device *dev, struct 
device_route *route, int cmd)
        if (dev)
                nla_put_u32(msg, RTA_OIF, dev->ifindex);
 
-       if (table >= 256)
-               nla_put_u32(msg, RTA_TABLE, table);
-
-       return system_rtnl_call(msg);
+       //All routes belonging to one interface are added to both the desired 
table
+       //and the interface table. This is required for example for default 
routes.
+       //If they are only present in the interface table, applications running 
on
+       //the device and which does not bind to a specific IP, will not work as
+       //intended.
+       if(route->iface != NULL && route->iface->interface_table &&
+                       table != route->iface->interface_table){
+               nlmsg_get(msg);
+               int retval = system_rtnl_call(msg);
+               struct rtmsg* rtm_ptr = (struct rtmsg*) 
nlmsg_data(nlmsg_hdr(msg));
+
+               rtm_ptr->rtm_table = route->iface->interface_table;
+               retval = system_rtnl_call(msg);
+
+               return retval;
+       } else
+           return system_rtnl_call(msg);
 }
 
 int system_add_route(struct device *dev, struct device_route *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