[OpenWrt-Devel] [PATCH] Netifd: Generic multi-wan support v3

2013-05-17 Thread Kristian Evensen
This patch implements the comments received for version 1 (see
https://lists.openwrt.org/pipermail/openwrt-devel/2013-May/020119.html), and
improves upon version 2 by respecting metrics.

* Routes are only added to the specified table. The only exception are the
IPv4-routes Linux automatically adds to the default table when an address
with a mask less than 32 comes up. In order to respect the metrics, each
automatic routes is replaced by one containing the correct metric (if metric
is set). Prefsrc is set as the source hint is needed in case of overlapping
subnets. The automatic routes, or equivalent, needed to be present (in the
default table) for for example adding default routes to interfaces to work.

* 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. The order here does
not matter.

Then follows lookups in main and default, before the networks and from
lo-rules are checked. If metric is set, the priority of this rules is 65535
+ metric, otherwise the priority is 65535 + ifindex. The from lo .. rules
are needed so that traffic from sockets not bound to an IP also is routed
correctly.

* The from lo-rules all have different priorities. 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 for the last two days 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.
---
 interface-ip.c | 108 -
 interface-ip.h |   3 +-
 iprule.h   |   3 ++
 proto.c|   6 ++--
 system-linux.c |   3 ++
 5 files changed, 94 insertions(+), 29 deletions(-)

diff --git a/interface-ip.c b/interface-ip.c
index e265563..05806af 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, unsigned int pri)
 {
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 = pri,
+   .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 

[OpenWrt-Devel] [PATCH] Netifd: Generic multi-wan support v2

2013-05-16 Thread Kristian Evensen
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, 

[OpenWrt-Devel] [PATCH] Netifd: Generic multi-wan support

2013-05-15 Thread Kristian Evensen
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);