From: Kristian Evensen <[email protected]>
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
[email protected]
https://lists.openwrt.org/mailman/listinfo/openwrt-devel