#include <arpa/inet.h>
#include <netinet/in.h>
-#ifdef linux
-#include <netinet/ether.h>
-#else
-#include <net/ethernet.h>
-#endif
-
#include "netifd.h"
#include "device.h"
#include "interface.h"
ROUTE_ONLINK,
ROUTE_TYPE,
ROUTE_PROTO,
+ ROUTE_DISABLED,
__ROUTE_MAX
};
[ROUTE_ONLINK] = { .name = "onlink", .type = BLOBMSG_TYPE_BOOL },
[ROUTE_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
[ROUTE_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING },
+ [ROUTE_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL },
};
const struct uci_blob_param_list route_attr_list = {
if (route->flags & DEVROUTE_TABLE)
continue;
- if (!*res || route->mask > (*res)->mask)
+ if (!*res || route->mask > (*res)->mask ||
+ ((route->mask == (*res)->mask) && (route->flags & DEVROUTE_METRIC)
+ && (route->metric < (*res)->metric)))
*res = route;
}
}
blobmsg_parse(route_attr, __ROUTE_MAX, tb, blobmsg_data(attr), blobmsg_data_len(attr));
+ if ((cur = tb[ROUTE_DISABLED]) != NULL && blobmsg_get_bool(cur))
+ return;
+
if (!iface) {
if ((cur = tb[ROUTE_INTERFACE]) == NULL)
return;
static int
addr_cmp(const void *k1, const void *k2, void *ptr)
{
- return memcmp(k1, k2, sizeof(struct device_addr) -
- offsetof(struct device_addr, flags));
+ const struct device_addr *a1 = k1;
+ const struct device_addr *a2 = k2;
+ const int cmp_offset = offsetof(struct device_addr, flags);
+ const int cmp_size = sizeof(struct device_addr) - cmp_offset;
+
+ if (a1->index != a2->index)
+ return a1->index - a2->index;
+ return memcmp(k1+cmp_offset, k2+cmp_offset, cmp_size);
}
static int
if (!(a_old->flags & DEVADDR_EXTERNAL)) {
interface_handle_subnet_route(iface, a_old, false);
system_del_address(dev, a_old);
+
+ if ((a_old->flags & DEVADDR_OFFLINK) && (a_old->mask < (v6 ? 128 : 32))) {
+ struct device_route route;
+
+ memset(&route, 0, sizeof(route));
+ route.flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
+ route.metric = INT32_MAX;
+ route.mask = a_old->mask;
+ route.addr = a_old->addr;
+
+ clear_if_addr(&route.addr, route.mask);
+
+ /* Delete null-route */
+ system_del_route(NULL, &route);
+ }
+
}
}
free(a_old->pclass);
a_new->enabled = true;
if ((a_new->flags & DEVADDR_FAMILY) == DEVADDR_INET6)
- v6 = true;
+ v6 = true;
a_new->policy_table = (v6) ? iface->ip6table : iface->ip4table;
}
if (!keep) {
+ if (!(a_new->flags & DEVADDR_EXTERNAL) &&
+ (a_new->flags & DEVADDR_OFFLINK) &&
+ (a_new->mask < (v6 ? 128 : 32))) {
+ struct device_route route;
+
+ memset(&route, 0, sizeof(route));
+ route.flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
+ route.metric = INT32_MAX;
+ route.mask = a_new->mask;
+ route.addr = a_new->addr;
+
+ clear_if_addr(&route.addr, route.mask);
+
+ /*
+ * In case off link is specifed as address property
+ * add null-route to avoid routing loops
+ */
+ system_add_route(NULL, &route);
+ }
+
if (a_new->policy_table)
interface_add_addr_rules(a_new, true);
}
if (!add && assignment->enabled) {
time_t now = system_get_rtime();
- addr.preferred_until = now;
- if (!addr.valid_until || addr.valid_until - now > 7200)
- addr.valid_until = now + 7200;
+ if (addr.valid_until && addr.valid_until - 1 <= now) {
+ addr.valid_until = 0;
+ addr.preferred_until = 0;
+ } else {
+ /* Address is still valid; pass its ownership to kernel (see L-14 RFC 7084). */
+ addr.preferred_until = now;
+
+ if (!addr.valid_until || addr.valid_until > now + 7200)
+ addr.valid_until = now + 7200;
+ }
if (iface->ip6table)
set_ip_source_policy(false, true, IPRULE_PRIORITY_ADDR_MASK, &addr.addr,
interface_set_route_info(iface, &route);
system_del_route(l3_downlink, &route);
- system_add_address(l3_downlink, &addr);
+ if (addr.valid_until)
+ system_add_address(l3_downlink, &addr);
+ else
+ system_del_address(l3_downlink, &addr);
assignment->addr = in6addr_any;
assignment->enabled = false;
static bool refresh = false;
if (!hint && refresh) {
struct device_prefix *p;
- list_for_each_entry(p, &prefixes, head)
- interface_update_prefix_assignments(p, true);
+ time_t now = system_get_rtime();
+
+ list_for_each_entry(p, &prefixes, head) {
+ bool valid = !(p->valid_until && p->valid_until - 1 <= now);
+
+ interface_update_prefix_assignments(p, valid);
+ }
}
refresh = hint;
}
void interface_update_prefix_delegation(struct interface_ip_settings *ip)
{
struct device_prefix *prefix;
+ time_t now = system_get_rtime();
vlist_for_each_element(&ip->prefix, prefix, node) {
- interface_update_prefix_assignments(prefix, !ip->no_delegation);
+ bool valid = !(prefix->valid_until && prefix->valid_until - 1 <= now);
+
+ interface_update_prefix_assignments(prefix, !ip->no_delegation && valid);
if (ip->no_delegation) {
if (prefix->head.next)
route.mask = (node_new) ? prefix_new->length : prefix_old->length;
route.addr.in6 = (node_new) ? prefix_new->addr : prefix_old->addr;
-
struct device_prefix_assignment *c;
struct interface *iface;
+ bool new_valid = node_new && !(prefix_new->valid_until && prefix_new->valid_until - 1 <= system_get_rtime());
if (node_old && node_new) {
/* Move assignments and refresh addresses to update valid times */
list_for_each_entry(c, &prefix_new->assignments, head)
if ((iface = vlist_find(&interfaces, c->name, iface, node)))
- interface_set_prefix_address(c, prefix_new, iface, true);
+ interface_set_prefix_address(c, prefix_new, iface, new_valid);
if (prefix_new->preferred_until != prefix_old->preferred_until ||
prefix_new->valid_until != prefix_old->valid_until)
system_add_route(NULL, &route);
if (!prefix_new->iface || !prefix_new->iface->proto_ip.no_delegation)
- interface_update_prefix_assignments(prefix_new, true);
+ interface_update_prefix_assignments(prefix_new, new_valid);
} else if (node_old) {
/* Remove null-route */
interface_update_prefix_assignments(prefix_old, false);
uint8_t length, time_t valid_until, time_t preferred_until,
struct in6_addr *excl_addr, uint8_t excl_length, const char *pclass)
{
+ union if_addr a = { .in6 = *addr };
+
if (!pclass)
pclass = (iface) ? iface->name : "local";
if (!prefix)
return NULL;
+ clear_if_addr(&a, length);
+
prefix->length = length;
- prefix->addr = *addr;
+ prefix->addr = a.in6;
prefix->preferred_until = preferred_until;
prefix->valid_until = valid_until;
prefix->iface = iface;
if (iface->metric || addr->policy_table)
interface_handle_subnet_route(iface, addr, true);
+ if ((addr->flags & DEVADDR_OFFLINK) && (addr->mask < (v6 ? 128 : 32))) {
+ struct device_route route;
+
+ memset(&route, 0, sizeof(route));
+ route.flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
+ route.metric = INT32_MAX;
+ route.mask = addr->mask;
+ route.addr = addr->addr;
+
+ clear_if_addr(&route.addr, route.mask);
+
+ /*
+ * In case off link is specifed as address property
+ * add null-route to avoid routing loops
+ */
+ system_add_route(NULL, &route);
+ }
+
if (addr->policy_table)
interface_add_addr_rules(addr, true);
} else {
interface_handle_subnet_route(iface, addr, false);
system_del_address(dev, addr);
+ if ((addr->flags & DEVADDR_OFFLINK) && (addr->mask < (v6 ? 128 : 32))) {
+ struct device_route route;
+
+ memset(&route, 0, sizeof(route));
+ route.flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
+ route.metric = INT32_MAX;
+ route.mask = addr->mask;
+ route.addr = addr->addr;
+
+ clear_if_addr(&route.addr, route.mask);
+
+ /* Delete null-route */
+ system_del_route(NULL, &route);
+ }
+
if (addr->policy_table)
interface_add_addr_rules(addr, false);
}
route->failed = true;
} else
system_del_route(dev, route);
+
route->enabled = _enabled;
}