ROUTE_GATEWAY,
ROUTE_METRIC,
ROUTE_MTU,
+ ROUTE_VALID,
__ROUTE_MAX
};
[ROUTE_GATEWAY] = { .name = "gateway", .type = BLOBMSG_TYPE_STRING },
[ROUTE_METRIC] = { .name = "metric", .type = BLOBMSG_TYPE_INT32 },
[ROUTE_MTU] = { .name = "mtu", .type = BLOBMSG_TYPE_INT32 },
+ [ROUTE_VALID] = { .name = "valid", .type = BLOBMSG_TYPE_INT32 },
};
const struct config_param_list route_attr_list = {
struct list_head prefixes = LIST_HEAD_INIT(prefixes);
static struct device_prefix *ula_prefix = NULL;
+static struct uloop_timeout valid_until_timeout;
static void
route->flags |= DEVROUTE_MTU;
}
+ if ((cur = tb[ROUTE_VALID]) != NULL)
+ route->valid_until = system_get_rtime() + blobmsg_get_u32(cur);
+
vlist_add(&ip->route, &route->node, &route->flags);
return;
route.flags |= DEVADDR_KERNEL;
system_del_route(dev, &route);
- route.flags &= ~DEVADDR_KERNEL;
- route.metric = iface->metric;
- system_add_route(dev, &route);
+ if (!(addr->flags & DEVADDR_OFFLINK)) {
+ route.flags &= ~DEVADDR_KERNEL;
+ route.metric = iface->metric;
+ system_add_route(dev, &route);
+ }
} else {
- system_del_route(dev, &route);
+ if (!(addr->flags & DEVADDR_OFFLINK))
+ system_del_route(dev, &route);
}
}
if (a_new && a_old) {
keep = true;
- if (a_old->flags != a_new->flags)
+ if (a_old->flags != a_new->flags ||
+ a_old->valid_until != a_new->valid_until ||
+ a_old->preferred_until != a_new->preferred_until)
keep = false;
if ((a_new->flags & DEVADDR_FAMILY) == DEVADDR_INET4 &&
a_new->enabled = true;
if (!(a_new->flags & DEVADDR_EXTERNAL) && !keep) {
system_add_address(dev, a_new);
- if (iface->metric)
+ if ((a_new->flags & DEVADDR_OFFLINK) || iface->metric)
interface_handle_subnet_route(iface, a_new, true);
}
}
addr.valid_until = assignment->prefix->valid_until;
if (!add) {
- if (assignment->enabled)
- system_del_address(l3_downlink, &addr);
+ if (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;
+ system_add_address(l3_downlink, &addr);
+ }
} else {
system_add_address(l3_downlink, &addr);
struct device_prefix_assignment *assignment;
if (!length || length > 64) {
- assignment = vlist_find(prefix->assignments, &iface, assignment, node);
+ assignment = vlist_find(prefix->assignments, iface->name, assignment, node);
if (assignment)
interface_set_prefix_address(iface, false, assignment);
} else {
- uint8_t length = iface->proto_ip.assignment_length;
uint64_t want = 1ULL << (64 - length);
char *name;
// Update all assignments
struct device_prefix_assignment *assignment;
struct vlist_tree *assignments = prefix_new->assignments;
- vlist_for_each_element(assignments, assignment, node)
+ vlist_for_each_element(assignments, assignment, node) {
+ assignment->prefix = prefix_new;
assignments->update(assignments,
&assignment->node, &assignment->node);
+ }
} else if (node_new) {
prefix_new->avail = 1ULL << (64 - prefix_new->length);
prefix_new->assignments = calloc(1, sizeof(*prefix_new->assignments));
interface_ip_set_prefix_assignment(prefix_new, iface,
iface->proto_ip.assignment_length);
- list_add(&prefix_new->head, &prefixes);
-
// Set null-route to avoid routing loops
system_add_route(NULL, &route);
}
}
free(prefix_old);
}
+
+ if (node_new)
+ list_add(&prefix_new->head, &prefixes);
}
-void
+struct device_prefix*
interface_ip_add_device_prefix(struct interface *iface, struct in6_addr *addr,
uint8_t length, time_t valid_until, time_t preferred_until)
{
vlist_add(&iface->proto_ip.prefix, &prefix->node, &prefix->addr);
else
interface_update_prefix(NULL, &prefix->node, NULL);
+
+ return prefix;
}
void
interface_ip_set_ula_prefix(const char *prefix)
{
char buf[INET6_ADDRSTRLEN + 4] = {0}, *saveptr;
- strncpy(buf, prefix, sizeof(buf) - 1);
+ if (prefix)
+ strncpy(buf, prefix, sizeof(buf) - 1);
char *prefixaddr = strtok_r(buf, "/", &saveptr);
struct in6_addr addr;
- if (!prefixaddr || inet_pton(AF_INET6, prefixaddr, &addr) < 1)
+ if (!prefixaddr || inet_pton(AF_INET6, prefixaddr, &addr) < 1) {
+ if (ula_prefix) {
+ interface_update_prefix(NULL, NULL, &ula_prefix->node);
+ ula_prefix = NULL;
+ }
return;
+ }
int length;
char *prefixlen = strtok_r(NULL, ",", &saveptr);
if (!prefixlen || (length = atoi(prefixlen)) < 1 || length > 64)
return;
- if (ula_prefix && (!IN6_ARE_ADDR_EQUAL(&addr, &ula_prefix->addr) ||
- ula_prefix->length != length)) {
- interface_update_prefix(NULL, NULL, &ula_prefix->node);
- ula_prefix = NULL;
- }
+ if (!ula_prefix || !IN6_ARE_ADDR_EQUAL(&addr, &ula_prefix->addr) ||
+ ula_prefix->length != length) {
+ if (ula_prefix)
+ interface_update_prefix(NULL, NULL, &ula_prefix->node);
- interface_ip_add_device_prefix(NULL, &addr, length, 0, 0);
+ ula_prefix = interface_ip_add_device_prefix(NULL, &addr, length, 0, 0);
+ }
}
void
__interface_ip_init(&iface->proto_ip, iface);
__interface_ip_init(&iface->config_ip, iface);
vlist_init(&iface->host_routes, route_cmp, interface_update_host_route);
+
+}
+
+static void
+interface_ip_valid_until_handler(struct uloop_timeout *t)
+{
+ time_t now = system_get_rtime();
+ struct interface *iface;
+ vlist_for_each_element(&interfaces, iface, node) {
+ if (iface->state != IFS_UP)
+ continue;
+
+ struct device_addr *addr, *addrp;
+ struct device_route *route, *routep;
+ struct device_prefix *pref, *prefp;
+
+ vlist_for_each_element_safe(&iface->proto_ip.addr, addr, node, addrp)
+ if (addr->valid_until && addr->valid_until < now)
+ vlist_delete(&iface->proto_ip.addr, &addr->node);
+
+ vlist_for_each_element_safe(&iface->proto_ip.route, route, node, routep)
+ if (route->valid_until && route->valid_until < now)
+ vlist_delete(&iface->proto_ip.route, &route->node);
+
+ vlist_for_each_element_safe(&iface->proto_ip.prefix, pref, node, prefp)
+ if (pref->valid_until && pref->valid_until < now)
+ vlist_delete(&iface->proto_ip.prefix, &pref->node);
+
+ }
+
+ uloop_timeout_set(t, 1000);
+}
+
+static void __init
+interface_ip_init_worker(void)
+{
+ valid_until_timeout.cb = interface_ip_valid_until_handler;
+ uloop_timeout_set(&valid_until_timeout, 1000);
}