Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 74 additions & 32 deletions interface-ip.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,23 +167,37 @@ static int set_ip_source_policy(bool add, bool v6, unsigned int priority,
return (add) ? system_add_iprule(&rule) : system_del_iprule(&rule);
}

static int set_ip_lo_policy(bool add, bool v6, struct interface *iface)
static int insert_lo_policy(bool add, bool v6, struct interface *iface,
unsigned int table, unsigned int priority_base)
{
struct iprule rule = {
.flags = IPRULE_IN | IPRULE_LOOKUP | IPRULE_PRIORITY,
.priority = IPRULE_PRIORITY_NW + iface->l3_dev.dev->ifindex,
.lookup = (v6) ? iface->ip6table : iface->ip4table,
.priority = priority_base + iface->l3_dev.dev->ifindex,
.lookup = table,
.in_dev = "lo"
};

if (!rule.lookup)
return 0;

rule.flags |= (v6) ? IPRULE_INET6 : IPRULE_INET4;

return (add) ? system_add_iprule(&rule) : system_del_iprule(&rule);
}

static int set_ip_lo_policy(bool add, bool v6, struct interface *iface)
{
int local_res = 0, non_local_res = 0;

unsigned int local_table = v6 ? iface->ip6table_local : iface->ip4table_local;
if (local_table != 0)
local_res = insert_lo_policy(add, v6, iface, local_table, IPRULE_PRIORITY_NW_LOCAL);

/* best effort - still try this even the previous call failed */
unsigned int table = v6 ? iface->ip6table : iface->ip4table;
if (table != 0 && local_table != table)
non_local_res = insert_lo_policy(add, v6, iface, table, IPRULE_PRIORITY_NW);

return local_res < 0 ? local_res : non_local_res;
}

static bool
__find_ip_addr_target(struct interface_ip_settings *ip, union if_addr *a, bool v6)
{
Expand Down Expand Up @@ -322,8 +336,16 @@ interface_ip_add_target_route(union if_addr *addr, bool v6, struct interface *if
return iface;
}

static bool
route_no_nexthop(struct device_route *route)
{
static const union if_addr zero;

return !memcmp(&route->nexthop, &zero, sizeof(zero));
}

static void
interface_set_route_info(struct interface *iface, struct device_route *route)
interface_set_route_info(struct interface *iface, struct device_route *route, bool local)
{
bool v6 = ((route->flags & DEVADDR_FAMILY) == DEVADDR_INET6);

Expand All @@ -334,7 +356,11 @@ interface_set_route_info(struct interface *iface, struct device_route *route)
route->metric = iface->metric;

if (!(route->flags & DEVROUTE_TABLE)) {
route->table = (v6) ? iface->ip6table : iface->ip4table;
if (v6)
route->table = local ? iface->ip6table_local : iface->ip6table;
else
route->table = local ? iface->ip4table_local : iface->ip4table;

if (route->table)
route->flags |= DEVROUTE_SRCTABLE;
}
Expand Down Expand Up @@ -527,7 +553,7 @@ interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6)
if (no_device)
route->flags |= DEVROUTE_NODEV;
else
interface_set_route_info(iface, route);
interface_set_route_info(iface, route, route_no_nexthop(route));

vlist_add(&ip->route, &route->node, route);
return;
Expand Down Expand Up @@ -622,19 +648,27 @@ interface_handle_subnet_route(struct interface *iface, struct device_addr *addr,
system_del_route(dev, r);

r->flags &= ~DEVROUTE_PROTO;
interface_set_route_info(iface, r);
interface_set_route_info(iface, r, true);

system_add_route(dev, r);
}

static void
interface_add_addr_rules(struct device_addr *addr, bool enabled)
interface_add_addr_rules(struct interface *iface, struct device_addr *addr,
bool enabled)
{
bool v6 = (addr->flags & DEVADDR_FAMILY) == DEVADDR_INET6;
unsigned int nonlocal_table = v6 ? iface->ip6table : iface->ip4table;

set_ip_source_policy(enabled, v6, IPRULE_PRIORITY_ADDR, &addr->addr,
(v6) ? 128 : 32, addr->policy_table, NULL, NULL,
true);

if (nonlocal_table && nonlocal_table != addr->policy_table)
set_ip_source_policy(enabled, v6, IPRULE_PRIORITY_ADDR_NL, &addr->addr,
(v6) ? 128 : 32, nonlocal_table, NULL, NULL,
true);

set_ip_source_policy(enabled, v6, IPRULE_PRIORITY_ADDR_MASK,
&addr->addr, addr->mask, addr->policy_table, NULL,
NULL, false);
Expand Down Expand Up @@ -707,8 +741,8 @@ interface_update_proto_addr(struct vlist_tree *tree,
* only the network-rule will cause packets to be routed through the
* first matching network (source IP matches both masks)
*/
if (a_old->policy_table)
interface_add_addr_rules(a_old, false);
if (a_old->policy_table && !iface->disable_addr_rules)
interface_add_addr_rules(iface, a_old, false);

if (!(a_old->flags & DEVADDR_EXTERNAL)) {
interface_handle_subnet_route(iface, a_old, false);
Expand Down Expand Up @@ -741,7 +775,7 @@ interface_update_proto_addr(struct vlist_tree *tree,
if ((a_new->flags & DEVADDR_FAMILY) == DEVADDR_INET6)
v6 = true;

a_new->policy_table = (v6) ? iface->ip6table : iface->ip4table;
a_new->policy_table = v6 ? iface->ip6table_local : iface->ip4table_local;

if (!keep || replace) {
if (!(a_new->flags & DEVADDR_EXTERNAL)) {
Expand Down Expand Up @@ -773,8 +807,8 @@ interface_update_proto_addr(struct vlist_tree *tree,
system_add_route(NULL, &route);
}

if (a_new->policy_table)
interface_add_addr_rules(a_new, true);
if (a_new->policy_table && !iface->disable_addr_rules)
interface_add_addr_rules(iface, a_new, true);
}
}
}
Expand Down Expand Up @@ -995,12 +1029,16 @@ interface_set_prefix_address(struct device_prefix_assignment *assignment,
addr.valid_until = now + 7200;
}

if (iface->ip6table)
if (iface->ip6table_local && !iface->disable_addr_rules)
set_ip_source_policy(false, true, IPRULE_PRIORITY_ADDR_MASK, &addr.addr,
addr.mask < 64 ? 64 : addr.mask, iface->ip6table, NULL, NULL, false);
addr.mask < 64 ? 64 : addr.mask, iface->ip6table_local, NULL, NULL, false);

if (prefix->iface) {
if (prefix->iface->ip6table)
if (prefix->iface && !iface->disable_addr_rules) {
if (prefix->iface->ip6table_local)
set_ip_source_policy(false, true, IPRULE_PRIORITY_NW_LOCAL, &addr.addr,
addr.mask, prefix->iface->ip6table_local, iface, NULL, true);

if (prefix->iface->ip6table && prefix->iface->ip6table != prefix->iface->ip6table_local)
set_ip_source_policy(false, true, IPRULE_PRIORITY_NW, &addr.addr,
addr.mask, prefix->iface->ip6table, iface, NULL, true);

Expand All @@ -1009,7 +1047,7 @@ interface_set_prefix_address(struct device_prefix_assignment *assignment,
}

clear_if_addr(&route.addr, route.mask);
interface_set_route_info(iface, &route);
interface_set_route_info(iface, &route, true);

system_del_route(l3_downlink, &route);
if (addr.valid_until)
Expand All @@ -1035,22 +1073,26 @@ interface_set_prefix_address(struct device_prefix_assignment *assignment,
return;

if (!assignment->enabled) {
if (iface->ip6table)
if (iface->ip6table_local && !iface->disable_addr_rules)
set_ip_source_policy(true, true, IPRULE_PRIORITY_ADDR_MASK, &addr.addr,
addr.mask < 64 ? 64 : addr.mask, iface->ip6table, NULL, NULL, false);
addr.mask < 64 ? 64 : addr.mask, iface->ip6table_local, NULL, NULL, false);

if (prefix->iface) {
if (prefix->iface && !iface->disable_addr_rules) {
set_ip_source_policy(true, true, IPRULE_PRIORITY_REJECT, &addr.addr,
addr.mask, 0, iface, "unreachable", true);

if (prefix->iface->ip6table)
if (prefix->iface->ip6table_local)
set_ip_source_policy(true, true, IPRULE_PRIORITY_NW_LOCAL, &addr.addr,
addr.mask, prefix->iface->ip6table_local, iface, NULL, true);

if (prefix->iface->ip6table && prefix->iface->ip6table != prefix->iface->ip6table_local)
set_ip_source_policy(true, true, IPRULE_PRIORITY_NW, &addr.addr,
addr.mask, prefix->iface->ip6table, iface, NULL, true);
}
}

clear_if_addr(&route.addr, route.mask);
interface_set_route_info(iface, &route);
interface_set_route_info(iface, &route, true);

system_add_route(l3_downlink, &route);

Expand Down Expand Up @@ -1642,7 +1684,7 @@ interface_ip_set_route_enabled(struct interface_ip_settings *ip,
return;

if (enabled) {
interface_set_route_info(ip->iface, route);
interface_set_route_info(ip->iface, route, route_no_nexthop(route));

if (system_add_route(dev, route))
route->failed = true;
Expand Down Expand Up @@ -1678,7 +1720,7 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
if (enabled) {
system_add_address(dev, addr);

addr->policy_table = (v6) ? iface->ip6table : iface->ip4table;
addr->policy_table = v6 ? iface->ip6table_local : iface->ip4table_local;
if (iface->metric || addr->policy_table)
interface_handle_subnet_route(iface, addr, true);

Expand All @@ -1700,8 +1742,8 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
system_add_route(NULL, &route);
}

if (addr->policy_table)
interface_add_addr_rules(addr, true);
if (addr->policy_table && !iface->disable_addr_rules)
interface_add_addr_rules(iface, addr, true);
} else {
interface_handle_subnet_route(iface, addr, false);
system_del_address(dev, addr);
Expand All @@ -1721,8 +1763,8 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
system_del_route(NULL, &route);
}

if (addr->policy_table)
interface_add_addr_rules(addr, false);
if (addr->policy_table && !iface->disable_addr_rules)
interface_add_addr_rules(iface, addr, false);
}
addr->enabled = enabled;
}
Expand Down
62 changes: 58 additions & 4 deletions interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,14 @@ enum {
IFACE_ATTR_IP6HINT,
IFACE_ATTR_IP4TABLE,
IFACE_ATTR_IP6TABLE,
IFACE_ATTR_IP4TABLE_LOCAL,
IFACE_ATTR_IP6TABLE_LOCAL,
IFACE_ATTR_IP6CLASS,
IFACE_ATTR_DELEGATE,
IFACE_ATTR_IP6IFACEID,
IFACE_ATTR_FORCE_LINK,
IFACE_ATTR_IP6WEIGHT,
IFACE_ATTR_DISABLE_ADDR_RULES,
IFACE_ATTR_TAGS,
IFACE_ATTR_MAX
};
Expand All @@ -83,11 +86,14 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
[IFACE_ATTR_IP6HINT] = { .name = "ip6hint", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_IP4TABLE] = { .name = "ip4table", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_IP6TABLE] = { .name = "ip6table", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_IP4TABLE_LOCAL] = { .name = "ip4table_local", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_IP6TABLE_LOCAL] = { .name = "ip6table_local", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_IP6CLASS] = { .name = "ip6class", .type = BLOBMSG_TYPE_ARRAY },
[IFACE_ATTR_DELEGATE] = { .name = "delegate", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_IP6IFACEID] = { .name = "ip6ifaceid", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_FORCE_LINK] = { .name = "force_link", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_IP6WEIGHT] = { .name = "ip6weight", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_DISABLE_ADDR_RULES] = { .name = "disable_addr_rules", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_TAGS] = { .name = "tags", .type = BLOBMSG_TYPE_ARRAY },
};

Expand Down Expand Up @@ -858,6 +864,7 @@ interface_alloc(const char *name, struct blob_attr *config, bool dynamic)
iface->autostart = blobmsg_get_bool_default(tb[IFACE_ATTR_AUTO], true);
iface->renew = blobmsg_get_bool_default(tb[IFACE_ATTR_RENEW], true);
iface->force_link = blobmsg_get_bool_default(tb[IFACE_ATTR_FORCE_LINK], force_link);
iface->disable_addr_rules = blobmsg_get_bool_default(tb[IFACE_ATTR_DISABLE_ADDR_RULES], false);
iface->dynamic = dynamic;
iface->proto_ip.no_defaultroute =
!blobmsg_get_bool_default(tb[IFACE_ATTR_DEFAULTROUTE], true);
Expand Down Expand Up @@ -927,6 +934,18 @@ interface_alloc(const char *name, struct blob_attr *config, bool dynamic)
D(INTERFACE, "Failed to resolve routing table: %s", (char *) blobmsg_data(cur));
}

iface->ip4table_local = iface->ip4table;
if ((cur = tb[IFACE_ATTR_IP4TABLE_LOCAL])) {
if (!system_resolve_rt_table(blobmsg_data(cur), &iface->ip4table_local))
D(INTERFACE, "Failed to resolve routing table: %s", (char *) blobmsg_data(cur));
}

iface->ip6table_local = iface->ip6table;
if ((cur = tb[IFACE_ATTR_IP6TABLE_LOCAL])) {
if (!system_resolve_rt_table(blobmsg_data(cur), &iface->ip6table_local))
D(INTERFACE, "Failed to resolve routing table: %s", (char *) blobmsg_data(cur));
}

iface->proto_ip.no_delegation = !blobmsg_get_bool_default(tb[IFACE_ATTR_DELEGATE], true);

iface->config_autostart = iface->autostart;
Expand Down Expand Up @@ -1320,6 +1339,14 @@ interface_change_config(struct interface *if_old, struct interface *if_new)
__var |= __changed; \
})

#define CHECK(field, __var) ({ \
__var |= (if_old->field != if_new->field); \
})

#define APPLY(field) ({ \
if_old->field = if_new->field; \
})

if_old->config = if_new->config;
if_old->tags = if_new->tags;
if (if_old->config_autostart != if_new->config_autostart) {
Expand Down Expand Up @@ -1364,13 +1391,17 @@ interface_change_config(struct interface *if_old, struct interface *if_new)
if_old->proto_ip.no_dns = if_new->proto_ip.no_dns;
interface_replace_dns(&if_old->config_ip, &if_new->config_ip);

UPDATE(metric, reload_ip);
UPDATE(proto_ip.no_defaultroute, reload_ip);
UPDATE(ip4table, reload_ip);
UPDATE(ip6table, reload_ip);
CHECK(metric, reload_ip);
CHECK(proto_ip.no_defaultroute, reload_ip);
CHECK(ip4table, reload_ip);
CHECK(ip6table, reload_ip);
CHECK(ip4table_local, reload_ip);
CHECK(ip6table_local, reload_ip);
CHECK(disable_addr_rules, reload_ip);
interface_merge_assignment_data(if_old, if_new);

#undef UPDATE
#undef CHECK

if (!reload) {
struct device *old_dev = if_old->main_dev.dev;
Expand All @@ -1383,6 +1414,18 @@ interface_change_config(struct interface *if_old, struct interface *if_new)
D(INTERFACE, "Reload interface '%s' because of config changes",
if_old->name);
interface_clear_errors(if_old);

interface_ip_set_enabled(&if_old->config_ip, false);
interface_ip_set_enabled(&if_old->proto_ip, false);

APPLY(metric);
APPLY(proto_ip.no_defaultroute);
APPLY(ip4table);
APPLY(ip6table);
APPLY(ip4table_local);
APPLY(ip6table_local);
APPLY(disable_addr_rules);

set_config_state(if_old, IFC_RELOAD);
goto out;
}
Expand All @@ -1393,10 +1436,21 @@ interface_change_config(struct interface *if_old, struct interface *if_new)

interface_ip_set_enabled(&if_old->config_ip, false);
interface_ip_set_enabled(&if_old->proto_ip, false);

APPLY(metric);
APPLY(proto_ip.no_defaultroute);
APPLY(ip4table);
APPLY(ip6table);
APPLY(ip4table_local);
APPLY(ip6table_local);
APPLY(disable_addr_rules);

interface_ip_set_enabled(&if_old->proto_ip, proto_ip_enabled);
interface_ip_set_enabled(&if_old->config_ip, config_ip_enabled);
}

#undef APPLY

if (update_prefix_delegation)
interface_update_prefix_delegation(&if_old->proto_ip);

Expand Down
3 changes: 3 additions & 0 deletions interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ struct interface {
bool policy_rules_set;
bool link_up_event;
bool renew;
bool disable_addr_rules;

time_t start_time;
enum interface_state state;
Expand Down Expand Up @@ -160,6 +161,8 @@ struct interface {
int dns_metric;
unsigned int ip4table;
unsigned int ip6table;
unsigned int ip4table_local;
unsigned int ip6table_local;

/* IPv6 assignment parameters */
enum interface_id_selection_type assignment_iface_id_selection;
Expand Down
Loading