From ab4a8a357bae08a7bb3d835299fe53098dbdfbf7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 18 Oct 2011 20:41:23 +0200 Subject: [PATCH] add support for alias devices, which are activated based on hotplug events containing the real device name --- config/network | 6 +++ device.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++-- device.h | 1 + ubus.c | 51 ++++++++++++++++++++++- 4 files changed, 162 insertions(+), 4 deletions(-) diff --git a/config/network b/config/network index e39164a..2361aa0 100644 --- a/config/network +++ b/config/network @@ -40,3 +40,9 @@ config interface wan option ifname br-lan2 option username foo option password bar + +config interface test + option ifname @test + option proto static + option ipaddr 192.168.5.1 + option netmask 255.255.255.0 diff --git a/device.c b/device.c index 4d0de67..ccba9d3 100644 --- a/device.c +++ b/device.c @@ -16,6 +16,17 @@ #include "config.h" static struct avl_tree devices; +static struct avl_tree aliases; + +struct alias_device { + struct avl_node avl; + struct device dev; + struct device_user dep; + bool cleanup; + char name[]; +}; + +static const struct device_type alias_device_type; static const struct blobmsg_policy dev_attrs[__DEV_ATTR_MAX] = { [DEV_ATTR_TYPE] = { "type", BLOBMSG_TYPE_STRING }, @@ -61,6 +72,56 @@ const struct device_type simple_device_type = { .free = simple_device_free, }; +static int +alias_device_set_state(struct device *dev, bool state) +{ + struct alias_device *alias; + + alias = container_of(dev, struct alias_device, dev); + if (!alias->dep.dev) + return -1; + + if (state) + return device_claim(&alias->dep); + + device_release(&alias->dep); + if (alias->cleanup) + device_remove_user(&alias->dep); + return 0; +} + +static struct device * +alias_device_create(const char *name, struct blob_attr *attr) +{ + struct alias_device *alias; + + alias = calloc(1, sizeof(*alias) + strlen(name) + 1); + strcpy(alias->name, name); + alias->dev.set_state = alias_device_set_state; + device_init_virtual(&alias->dev, &alias_device_type, NULL); + alias->avl.key = alias->name; + avl_insert(&aliases, &alias->avl); + + return &alias->dev; +} + +static void alias_device_free(struct device *dev) +{ + struct alias_device *alias; + + device_cleanup(dev); + + alias = container_of(dev, struct alias_device, dev); + avl_delete(&aliases, &alias->avl); + free(alias); +} + +static const struct device_type alias_device_type = { + .name = "Network alias", + .create = alias_device_create, + .free = alias_device_free, +}; + void device_init_settings(struct device *dev, struct blob_attr **tb) { @@ -91,6 +152,7 @@ device_init_settings(struct device *dev, struct blob_attr **tb) static void __init dev_init(void) { avl_init(&devices, avl_strcmp, true, NULL); + avl_init(&aliases, avl_strcmp, false, NULL); } static void device_broadcast_event(struct device *dev, enum device_event ev) @@ -105,6 +167,31 @@ static void device_broadcast_event(struct device *dev, enum device_event ev) } } +void +alias_notify_device(const char *name, struct device *dev) +{ + struct alias_device *alias; + + alias = avl_find_element(&aliases, name, alias, avl); + if (!alias) + return; + + alias->cleanup = !dev; + if (dev) { + if (dev != alias->dep.dev) { + if (alias->dep.dev) + device_remove_user(&alias->dep); + strcpy(alias->dev.ifname, dev->ifname); + device_add_user(&alias->dep, dev); + } + } + + device_set_present(&alias->dev, !!dev); + + if (!dev && alias->dep.dev && !alias->dep.dev->active) + device_remove_user(&alias->dep); +} + static int set_device_state(struct device *dev, bool state) { if (state) @@ -124,7 +211,7 @@ int device_claim(struct device_user *dep) return 0; dep->claimed = true; - D(DEVICE, "claim device %s, new refcount: %d\n", dev->ifname, dev->active + 1); + D(DEVICE, "Claim %s %s, new refcount: %d\n", dev->type->name, dev->ifname, dev->active + 1); if (++dev->active != 1) return 0; @@ -150,7 +237,7 @@ void device_release(struct device_user *dep) dep->claimed = false; dev->active--; - D(DEVICE, "release device %s, new refcount: %d\n", dev->ifname, dev->active); + D(DEVICE, "Release %s %s, new refcount: %d\n", dev->type->name, dev->ifname, dev->active); assert(dev->active >= 0); if (dev->active) @@ -215,6 +302,18 @@ device_create_default(const char *name) return dev; } +static struct device * +device_alias_get(const char *name) +{ + struct alias_device *alias; + + alias = avl_find_element(&aliases, name, alias, avl); + if (alias) + return &alias->dev; + + return alias_device_create(name, NULL); +} + struct device * device_get(const char *name, bool create) { @@ -223,6 +322,9 @@ device_get(const char *name, bool create) if (strchr(name, '.')) return get_vlan_device_chain(name, create); + if (name[0] == '@') + return device_alias_get(name + 1); + dev = avl_find_element(&devices, name, dev, avl); if (dev) return dev; @@ -265,7 +367,7 @@ void device_set_present(struct device *dev, bool state) if (dev->present == state) return; - D(DEVICE, "Device '%s' %s present\n", dev->ifname, state ? "is now" : "is no longer" ); + D(DEVICE, "%s '%s' %s present\n", dev->type->name, dev->ifname, state ? "is now" : "is no longer" ); dev->present = state; device_broadcast_event(dev, state ? DEV_EVENT_ADD : DEV_EVENT_REMOVE); } diff --git a/device.h b/device.h index 8d610c9..686b1b1 100644 --- a/device.h +++ b/device.h @@ -143,5 +143,6 @@ device_free(struct device *dev) void device_free_unused(struct device *dev); struct device *get_vlan_device_chain(const char *ifname, bool create); +void alias_notify_device(const char *name, struct device *dev); #endif diff --git a/ubus.c b/ubus.c index 9b0f515..5529e59 100644 --- a/ubus.c +++ b/ubus.c @@ -80,8 +80,57 @@ netifd_dev_status(struct ubus_context *ctx, struct ubus_object *obj, return 0; } +enum { + ALIAS_ATTR_ALIAS, + ALIAS_ATTR_DEV, + __ALIAS_ATTR_MAX, +}; + +static const struct blobmsg_policy alias_attrs[__ALIAS_ATTR_MAX] = { + [ALIAS_ATTR_ALIAS] = { "alias", BLOBMSG_TYPE_ARRAY }, + [ALIAS_ATTR_DEV] = { "device", BLOBMSG_TYPE_STRING }, +}; + +static int +netifd_handle_alias(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct device *dev = NULL; + struct blob_attr *tb[__ALIAS_ATTR_MAX]; + struct blob_attr *cur; + int rem; + + blobmsg_parse(alias_attrs, __ALIAS_ATTR_MAX, tb, blob_data(msg), blob_len(msg)); + + if (!tb[ALIAS_ATTR_ALIAS]) + return UBUS_STATUS_INVALID_ARGUMENT; + + if ((cur = tb[ALIAS_ATTR_DEV]) != NULL) { + dev = device_get(blobmsg_data(cur), true); + if (!dev) + return UBUS_STATUS_NOT_FOUND; + } + + blobmsg_for_each_attr(cur, tb[ALIAS_ATTR_ALIAS], rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) + goto error; + + if (!blobmsg_check_attr(cur, NULL)) + goto error; + + alias_notify_device(blobmsg_data(cur), dev); + } + return 0; + +error: + device_free_unused(dev); + return UBUS_STATUS_INVALID_ARGUMENT; +} + static struct ubus_method dev_object_methods[] = { - UBUS_METHOD("status", netifd_dev_status, dev_policy) + UBUS_METHOD("status", netifd_dev_status, dev_policy), + UBUS_METHOD("set_alias", netifd_handle_alias, alias_attrs), }; static struct ubus_object_type dev_object_type = -- 2.30.2