proto_add_ipv6_address() {
local address="$1"
local mask="$2"
+ local preferred="$3"
+ local valid="$4"
+ local offlink="$5"
- append PROTO_IP6ADDR "$address/$mask"
+ append PROTO_IP6ADDR "$address/$mask/$preferred/$valid/$offlink"
}
proto_add_ipv4_route() {
local mask="$2"
local gw="$3"
- append PROTO_ROUTE "$target/$mask/$gw"
+ append PROTO_ROUTE "$target/$mask/$gw//"
}
proto_add_ipv6_route() {
local target="$1"
local mask="$2"
local gw="$3"
+ local metric="$4"
+ local valid="$5"
- append PROTO_ROUTE6 "$target/$mask/$gw"
+ append PROTO_ROUTE6 "$target/$mask/$gw/$metric/$valid"
}
proto_add_ipv6_prefix() {
_proto_push_ipv6_addr() {
local str="$1"
- local address mask
+ local address mask preferred valid offlink
address="${str%%/*}"
str="${str#*/}"
- mask="$str"
+ mask="${str%%/*}"
+ str="${str#*/}"
+ preferred="${str%%/*}"
+ str="${str#*/}"
+ valid="${str%%/*}"
+ str="${str#*/}"
+ offlink="${str%%/*}"
json_add_object ""
json_add_string ipaddr "$address"
[ -n "$mask" ] && json_add_string mask "$mask"
+ [ -n "$preferred" ] && json_add_int preferred "$preferred"
+ [ -n "$valid" ] && json_add_int valid "$valid"
+ [ -n "$offlink" ] && json_add_boolean offlink "$offlink"
json_close_object
}
local target="${str%%/*}"
str="${str#*/}"
local mask="${str%%/*}"
- local gw="${str#*/}"
+ str="${str#*/}"
+ local gw="${str%%/*}"
+ str="${str#*/}"
+ local metric="${str%%/*}"
+ str="${str#*/}"
+ local valid="${str%%/*}"
json_add_object ""
json_add_string target "$target"
json_add_string netmask "$mask"
[ -n "$gw" ] && json_add_string gateway "$gw"
+ [ -n "$metric" ] && json_add_int metric "$metric"
+ [ -n "$valid" ] && json_add_int valid "$valid"
json_close_object
}
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);
}
}
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
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);
}
/* route automatically added by kernel */
DEVADDR_KERNEL = (1 << 5),
+
+ /* address is off-link (no subnet-route) */
+ DEVADDR_OFFLINK = (1 << 6),
};
union if_addr {
bool keep;
union if_addr nexthop;
- int metric;
int mtu;
+ time_t valid_until;
/* must be last */
enum device_addr_flags flags;
+ int metric; // there can be multiple routes to the same target
unsigned int mask;
union if_addr addr;
};
void interface_ip_set_prefix_assignment(struct device_prefix *prefix,
struct interface *iface, uint8_t length);
-void interface_ip_add_device_prefix(struct interface *iface, struct in6_addr *addr,
- uint8_t length, time_t valid_until, time_t preferred_until);
+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);
void interface_ip_set_ula_prefix(const char *prefix);
#endif
ADDR_MASK,
ADDR_BROADCAST,
ADDR_PTP,
+ ADDR_PREFERRED,
+ ADDR_VALID,
+ ADDR_OFFLINK,
__ADDR_MAX
};
[ADDR_MASK] = { .name = "mask", .type = BLOBMSG_TYPE_STRING },
[ADDR_BROADCAST] = { .name = "broadcast", .type = BLOBMSG_TYPE_STRING },
[ADDR_PTP] = { .name = "ptp", .type = BLOBMSG_TYPE_STRING },
+ [ADDR_PREFERRED] = { .name = "preferred", .type = BLOBMSG_TYPE_INT32 },
+ [ADDR_VALID] = { .name = "valid", .type = BLOBMSG_TYPE_INT32 },
+ [ADDR_OFFLINK] = { .name = "offlink", .type = BLOBMSG_TYPE_BOOL },
};
static struct device_addr *
if (!inet_pton(v6 ? AF_INET6 : AF_INET, blobmsg_data(cur), &addr->addr))
goto error;
+ if ((cur = tb[ADDR_OFFLINK]) && blobmsg_get_bool(cur))
+ addr->flags |= DEVADDR_OFFLINK;
+
if (!v6) {
if ((cur = tb[ADDR_BROADCAST]) &&
!inet_pton(AF_INET, blobmsg_data(cur), &addr->broadcast))
if ((cur = tb[ADDR_PTP]) &&
!inet_pton(AF_INET, blobmsg_data(cur), &addr->point_to_point))
goto error;
+ } else {
+ time_t now = system_get_rtime();
+ if ((cur = tb[ADDR_PREFERRED])) {
+ uint32_t preferred = blobmsg_get_u32(cur);
+ if (preferred < UINT32_MAX)
+ addr->preferred_until = now + preferred;
+ }
+
+ if ((cur = tb[ADDR_VALID])) {
+ uint32_t valid = blobmsg_get_u32(cur);
+ if (valid < UINT32_MAX)
+ addr->valid_until = now + valid;
+
+ }
+
+ if (addr->valid_until) {
+ if (!addr->preferred_until)
+ addr->preferred_until = addr->valid_until;
+ else if (addr->preferred_until > addr->valid_until)
+ goto error;
+ }
}
return addr;
int buflen = 128;
int af;
+ time_t now = system_get_rtime();
vlist_for_each_element(&ip->addr, addr, node) {
if (addr->enabled != enabled)
continue;
blobmsg_add_u32(&b, "mask", addr->mask);
+ if (addr->preferred_until) {
+ int preferred = addr->preferred_until - now;
+ if (preferred < 0)
+ preferred = 0;
+ blobmsg_add_u32(&b, "preferred", preferred);
+ }
+
+ if (addr->valid_until)
+ blobmsg_add_u32(&b, "valid", addr->valid_until - now);
+
blobmsg_close_table(&b, a);
}
}
void *r;
int af;
+ time_t now = system_get_rtime();
vlist_for_each_element(&ip->route, route, node) {
if (route->enabled != enabled)
continue;
if (route->flags & DEVROUTE_METRIC)
blobmsg_add_u32(&b, "metric", route->metric);
+ if (route->valid_until)
+ blobmsg_add_u32(&b, "valid", route->valid_until - now);
+
blobmsg_close_table(&b, r);
}
}
void *a, *c;
const int buflen = INET6_ADDRSTRLEN;
+ time_t now = system_get_rtime();
vlist_for_each_element(&ip->prefix, prefix, node) {
a = blobmsg_open_table(&b, NULL);
blobmsg_add_u32(&b, "mask", prefix->length);
- time_t now = system_get_rtime();
if (prefix->preferred_until) {
int preferred = prefix->preferred_until - now;
if (preferred < 0)
blobmsg_add_u32(&b, "preferred", preferred);
}
- if (prefix->valid_until) {
- int valid = prefix->valid_until - now;
- if (valid < 0)
- valid = 0;
- blobmsg_add_u32(&b, "valid", valid);
- }
+ if (prefix->valid_until)
+ blobmsg_add_u32(&b, "valid", prefix->valid_until - now);
c = blobmsg_open_table(&b, "assigned");
struct device_prefix_assignment *assign;
}
+static void
+interface_ip_dump_prefix_assignment_list(struct interface *iface)
+{
+ void *a;
+ char *buf;
+ const int buflen = INET6_ADDRSTRLEN;
+ time_t now = system_get_rtime();
+
+ struct device_prefix *prefix;
+ list_for_each_entry(prefix, &prefixes, head) {
+ struct device_prefix_assignment *assign;
+ vlist_for_each_element(prefix->assignments, assign, node) {
+ if (strcmp(assign->name, iface->name))
+ continue;
+
+ a = blobmsg_open_table(&b, NULL);
+
+ buf = blobmsg_alloc_string_buffer(&b, "address", buflen);
+ inet_ntop(AF_INET6, &assign->addr, buf, buflen);
+ blobmsg_add_string_buffer(&b);
+
+ blobmsg_add_u32(&b, "mask", assign->length);
+
+ if (prefix->preferred_until) {
+ int preferred = prefix->preferred_until - now;
+ if (preferred < 0)
+ preferred = 0;
+ blobmsg_add_u32(&b, "preferred", preferred);
+ }
+
+ if (prefix->valid_until)
+ blobmsg_add_u32(&b, "valid", prefix->valid_until - now);
+
+ blobmsg_close_table(&b, a);
+ }
+ }
+}
+
+
static void
interface_ip_dump_dns_server_list(struct interface_ip_settings *ip,
bool enabled)
interface_ip_dump_prefix_list(&iface->config_ip);
interface_ip_dump_prefix_list(&iface->proto_ip);
blobmsg_close_array(&b, a);
+ a = blobmsg_open_array(&b, "ipv6-prefix-assignment");
+ interface_ip_dump_prefix_assignment_list(iface);
+ blobmsg_close_array(&b, a);
a = blobmsg_open_array(&b, "route");
interface_ip_dump_route_list(&iface->config_ip, true);
interface_ip_dump_route_list(&iface->proto_ip, true);
int version;
};
+#define vlist_for_each_element_safe(tree, element, node_member, ptr) \
+ avl_for_each_element_safe(&(tree)->avl, element, node_member.avl, ptr)
+
#define vlist_simple_init(tree, node, member) \
__vlist_simple_init(tree, offsetof(node, member))