json_add_object ""
json_add_string target "$target"
- json_add_string mask "$mask"
+ json_add_string netmask "$mask"
json_add_string gateway "$gw"
json_close_object
}
#include "ubus.h"
#include "system.h"
+enum {
+ ROUTE_INTERFACE,
+ ROUTE_TARGET,
+ ROUTE_MASK,
+ ROUTE_GATEWAY,
+ ROUTE_DEVICE,
+ ROUTE_METRIC,
+ ROUTE_MTU,
+ __ROUTE_LAST
+};
+
+static const struct blobmsg_policy route_attr[__ROUTE_LAST] = {
+ [ROUTE_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
+ [ROUTE_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
+ [ROUTE_MASK] = { .name = "netmask", .type = BLOBMSG_TYPE_STRING },
+ [ROUTE_GATEWAY] = { .name = "gateway", .type = BLOBMSG_TYPE_STRING },
+ [ROUTE_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
+ [ROUTE_METRIC] = { .name = "metric", .type = BLOBMSG_TYPE_INT32 },
+ [ROUTE_MTU] = { .name = "mtu", .type = BLOBMSG_TYPE_INT32 },
+};
+
+void
+interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6)
+{
+ struct interface_ip_settings *ip;
+ struct blob_attr *tb[__ROUTE_LAST], *cur;
+ struct device_route *route;
+ int af = v6 ? AF_INET6 : AF_INET;
+ bool config = false;
+
+ blobmsg_parse(route_attr, __ROUTE_LAST, tb, blobmsg_data(attr), blobmsg_data_len(attr));
+
+ if (!tb[ROUTE_GATEWAY] && !tb[ROUTE_DEVICE])
+ return;
+
+ if (!iface) {
+ if ((cur = tb[ROUTE_INTERFACE]) == NULL)
+ return;
+
+ iface = vlist_find(&interfaces, blobmsg_data(cur), iface, node);
+ if (!iface)
+ return;
+
+ ip = &iface->config_ip;
+ config = true;
+ } else {
+ ip = &iface->proto_ip;
+ }
+
+ route = calloc(1, sizeof(*route));
+ if (!route)
+ return;
+
+ route->mask = v6 ? 128 : 32;
+ if ((cur = tb[ROUTE_MASK]) != NULL) {
+ route->mask = parse_netmask_string(blobmsg_data(cur), v6);
+ if (route->mask > (v6 ? 128 : 32))
+ goto error;
+ }
+
+ if ((cur = tb[ROUTE_TARGET]) != NULL) {
+ if (!inet_pton(af, blobmsg_data(cur), &route->addr)) {
+ DPRINTF("Failed to parse route target: %s\n", (char *) blobmsg_data(cur));
+ goto error;
+ }
+ }
+
+ if ((cur = tb[ROUTE_GATEWAY]) != NULL) {
+ if (!inet_pton(af, blobmsg_data(cur), &route->nexthop)) {
+ DPRINTF("Failed to parse route gateway: %s\n", (char *) blobmsg_data(cur));
+ goto error;
+ }
+ }
+
+ if ((cur = tb[ROUTE_METRIC]) != NULL)
+ route->metric = blobmsg_get_u32(cur);
+
+ if ((cur = tb[ROUTE_MTU]) != NULL)
+ route->mtu = blobmsg_get_u32(cur);
+
+ if (!config && (cur = tb[ROUTE_DEVICE]) != NULL)
+ route->device = device_get(blobmsg_data(cur), true);
+
+ vlist_add(&ip->route, &route->node);
+ return;
+
+error:
+ free(route);
+}
+
static int
addr_cmp(const void *k1, const void *k2, void *ptr)
{
if (node_old) {
addr = container_of(node_old, struct device_addr, node);
- if (!(addr->flags & DEVADDR_EXTERNAL))
+ if (!(addr->flags & DEVADDR_EXTERNAL) && addr->enabled)
system_del_address(dev, addr);
free(addr);
}
addr = container_of(node_new, struct device_addr, node);
if (!(addr->flags & DEVADDR_EXTERNAL))
system_add_address(dev, addr);
+ addr->enabled = true;
}
}
if (node_old) {
route = container_of(node_old, struct device_route, node);
- if (!(route->flags & DEVADDR_EXTERNAL))
+ if (!(route->flags & DEVADDR_EXTERNAL) && route->enabled)
system_del_route(dev, route);
free(route);
}
route = container_of(node_new, struct device_route, node);
if (!(route->flags & DEVADDR_EXTERNAL))
system_add_route(dev, route);
+ route->enabled = true;
}
}
}
}
+void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
+{
+ struct device_addr *addr;
+ struct device_route *route;
+ struct device *dev;
+
+ ip->enabled = enabled;
+ dev = ip->iface->l3_dev->dev;
+ if (!dev)
+ return;
+
+ vlist_for_each_element(&ip->addr, addr, node) {
+ if (addr->enabled == enabled)
+ continue;
+
+ if (enabled)
+ system_add_address(dev, addr);
+ else
+ system_del_address(dev, addr);
+ addr->enabled = enabled;
+ }
+
+ vlist_for_each_element(&ip->route, route, node) {
+ if (route->enabled == enabled)
+ continue;
+
+ if (enabled)
+ system_add_route(dev, route);
+ else
+ system_del_route(dev, route);
+ route->enabled = enabled;
+ }
+}
+
void
interface_ip_update_start(struct interface_ip_settings *ip)
{
interface_ip_init(struct interface_ip_settings *ip, struct interface *iface)
{
ip->iface = iface;
+ ip->enabled = true;
INIT_LIST_HEAD(&ip->dns_search);
INIT_LIST_HEAD(&ip->dns_servers);
vlist_init(&ip->route, route_cmp, interface_update_proto_route,
struct vlist_node node;
enum device_addr_flags flags;
+ bool enabled;
+
+ struct device *device;
/* must be last */
unsigned int mask;
struct vlist_node node;
enum device_addr_flags flags;
+ bool enabled;
bool keep;
union if_addr nexthop;
struct device *device;
+ int metric;
+ int mtu;
/* must be last */
unsigned int mask;
void interface_add_dns_search_list(struct interface_ip_settings *ip, struct blob_attr *list);
void interface_write_resolv_conf(void);
+void interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6);
+
void interface_ip_update_start(struct interface_ip_settings *ip);
void interface_ip_update_complete(struct interface_ip_settings *ip);
void interface_ip_flush(struct interface_ip_settings *ip);
+void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled);
#endif
list_for_each_entry_safe(dep, tmp, &iface->users, list)
interface_remove_user(dep);
+ interface_ip_flush(&iface->config_ip);
interface_flush_state(iface);
interface_clear_errors(iface);
if (iface->main_dev.dev)
if (iface->state != IFS_SETUP)
return;
+ interface_ip_set_enabled(&iface->config_ip, true);
system_flush_routes();
iface->state = IFS_UP;
iface->start_time = system_get_rtime();
return;
netifd_log_message(L_NOTICE, "Interface '%s' is now down\n", iface->name);
+ interface_ip_set_enabled(&iface->config_ip, false);
system_flush_routes();
mark_interface_down(iface);
interface_handle_config_change(iface);
INIT_LIST_HEAD(&iface->users);
INIT_LIST_HEAD(&iface->hotplug_list);
interface_ip_init(&iface->proto_ip, iface);
+ interface_ip_init(&iface->config_ip, iface);
+ iface->config_ip.enabled = false;
iface->main_dev.cb = interface_cb;
iface->l3_dev = &iface->main_dev;
__interface_set_down(iface, false);
}
+void
+interface_update_start(struct interface *iface)
+{
+ interface_ip_update_start(&iface->proto_ip);
+}
+
+void
+interface_update_complete(struct interface *iface)
+{
+ struct device_route *route;
+
+ interface_ip_update_complete(&iface->proto_ip);
+ vlist_for_each_element(&iface->config_ip.route, route, node) {
+ if (iface->l3_dev->dev)
+ system_add_route(iface->l3_dev->dev, route);
+ }
+}
+
static void
interface_change_config(struct interface *if_old, struct interface *if_new)
{
struct interface_ip_settings {
struct interface *iface;
+ bool enabled;
struct vlist_tree addr;
struct vlist_tree route;
struct interface_proto_state *proto;
struct interface_ip_settings proto_ip;
+ struct interface_ip_settings config_ip;
/* errors/warnings while trying to bring up the interface */
struct list_head errors;
void interface_add_error(struct interface *iface, const char *subsystem,
const char *code, const char **data, int n_data);
+void interface_update_start(struct interface *iface);
+void interface_update_complete(struct interface *iface);
+
void interface_queue_event(struct interface *iface, enum interface_event ev);
void interface_dequeue_event(struct interface *iface);
}
}
-enum {
- ROUTE_TARGET,
- ROUTE_MASK,
- ROUTE_GATEWAY,
- ROUTE_DEVICE,
- __ROUTE_LAST
-};
-
-static const struct blobmsg_policy route_attr[__ROUTE_LAST] = {
- [ROUTE_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
- [ROUTE_MASK] = { .name = "mask", .type = BLOBMSG_TYPE_STRING },
- [ROUTE_GATEWAY] = { .name = "gateway", .type = BLOBMSG_TYPE_STRING },
- [ROUTE_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
-};
-
-static void
-parse_route(struct interface_ip_settings *ip, struct blob_attr *attr, bool v6)
-{
- struct blob_attr *tb[__ROUTE_LAST], *cur;
- struct device_route *route;
- int af = v6 ? AF_INET6 : AF_INET;
-
- blobmsg_parse(route_attr, __ROUTE_LAST, tb, blobmsg_data(attr), blobmsg_data_len(attr));
-
- if (!tb[ROUTE_GATEWAY] && !tb[ROUTE_DEVICE])
- return;
-
- route = calloc(1, sizeof(*route));
- if (!route)
- return;
-
- route->mask = v6 ? 128 : 32;
- if ((cur = tb[ROUTE_MASK]) != NULL) {
- route->mask = parse_netmask_string(blobmsg_data(cur), v6);
- if (route->mask > (v6 ? 128 : 32))
- goto error;
- }
-
- if ((cur = tb[ROUTE_TARGET]) != NULL) {
- if (!inet_pton(af, blobmsg_data(cur), &route->addr)) {
- DPRINTF("Failed to parse route target: %s\n", (char *) blobmsg_data(cur));
- goto error;
- }
- }
-
- if ((cur = tb[ROUTE_GATEWAY]) != NULL) {
- if (!inet_pton(af, blobmsg_data(cur), &route->nexthop)) {
- DPRINTF("Failed to parse route gateway: %s\n", (char *) blobmsg_data(cur));
- goto error;
- }
- }
-
- if ((cur = tb[ROUTE_DEVICE]) != NULL)
- route->device = device_get(blobmsg_data(cur), true);
-
- vlist_add(&ip->route, &route->node);
- return;
-
-error:
- free(route);
-}
-
static void
-proto_shell_parse_route_list(struct interface_ip_settings *ip, struct blob_attr *attr,
+proto_shell_parse_route_list(struct interface *iface, struct blob_attr *attr,
bool v6)
{
struct blob_attr *cur;
continue;
}
- parse_route(ip, cur, v6);
+ interface_ip_add_route(iface, cur, v6);
}
}
}
ip = &state->proto.iface->proto_ip;
- interface_ip_update_start(ip);
+ interface_update_start(state->proto.iface);
if ((cur = tb[NOTIFY_ADDR_EXT]) != NULL)
addr_ext = blobmsg_get_bool(cur);
proto_shell_parse_addr_list(ip, cur, true, addr_ext);
if ((cur = tb[NOTIFY_ROUTES]) != NULL)
- proto_shell_parse_route_list(ip, cur, false);
+ proto_shell_parse_route_list(state->proto.iface, cur, false);
if ((cur = tb[NOTIFY_ROUTES6]) != NULL)
- proto_shell_parse_route_list(ip, cur, true);
+ proto_shell_parse_route_list(state->proto.iface, cur, true);
if ((cur = tb[NOTIFY_DNS]) != NULL)
interface_add_dns_server_list(ip, cur);
if ((cur = tb[NOTIFY_DNS_SEARCH]) != NULL)
interface_add_dns_search_list(ip, cur);
- interface_ip_update_complete(ip);
+ interface_update_complete(state->proto.iface);
state->proto.proto_event(&state->proto, IFPEV_UP);
.ifa_index = dev->ifindex,
};
- struct nl_msg *msg = nlmsg_alloc_simple(cmd, 0);
+ struct nl_msg *msg;
+
+ dev = addr->device;
+ if (dev) {
+ if (!dev->ifindex)
+ return -1;
+
+ ifa.ifa_index = dev->ifindex;
+ }
+
+ msg = nlmsg_alloc_simple(cmd, 0);
if (!msg)
return -1;
int alen = ((route->flags & DEVADDR_FAMILY) == DEVADDR_INET4) ? 4 : 16;
bool have_gw;
unsigned int flags = 0;
+ int ifindex = dev->ifindex;
if (alen == 4)
have_gw = !!route->nexthop.in.s_addr;
.rtm_scope = scope,
.rtm_type = (cmd == RTM_DELROUTE) ? 0: RTN_UNICAST,
};
+ struct nl_msg *msg;
if (cmd == RTM_NEWROUTE)
flags |= NLM_F_CREATE | NLM_F_REPLACE;
- struct nl_msg *msg = nlmsg_alloc_simple(cmd, flags);
+ dev = route->device;
+ if (dev) {
+ if (!dev->ifindex)
+ return -1;
+
+ ifindex = dev->ifindex;
+ }
+
+ msg = nlmsg_alloc_simple(cmd, flags);
if (!msg)
return -1;
nla_put(msg, RTA_GATEWAY, alen, &route->nexthop);
if (route->flags & DEVADDR_DEVICE)
- nla_put_u32(msg, RTA_OIF, dev->ifindex);
+ nla_put_u32(msg, RTA_OIF, ifindex);
return system_rtnl_call(msg);
}
#define vlist_init(tree, cmp, update, type, node, key) \
__vlist_init(tree, cmp, update, offsetof(type, key) - offsetof(type, node))
+#define vlist_find(tree, name, element, node_member) \
+ avl_find_element(&(tree)->avl, name, element, node_member.avl)
+
static inline void vlist_update(struct vlist_tree *tree)
{
tree->version++;