--- /dev/null
+cmake_minimum_required(VERSION 2.6)
+
+PROJECT(netifd C)
+ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3)
+
+SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+
+IF(DEBUG)
+ ADD_DEFINITIONS(-DDEBUG -O0)
+ENDIF()
+
+ADD_EXECUTABLE(netifd main.c interface.c config.c device.c bridge.c vlan.c ubus.c system-dummy.c)
+TARGET_LINK_LIBRARIES(netifd ubox ubus uci)
--- /dev/null
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "netifd.h"
+#include "system.h"
+
+struct bridge_state {
+ struct device dev;
+ device_state_cb set_state;
+
+ bool active;
+
+ struct list_head members;
+ int n_present;
+};
+
+struct bridge_member {
+ struct list_head list;
+ struct bridge_state *bst;
+ struct device_user dev;
+ bool present;
+};
+
+static int
+bridge_disable_member(struct bridge_member *bm)
+{
+ struct bridge_state *bst = bm->bst;
+
+ if (!bm->present)
+ return 0;
+
+ system_bridge_delif(&bst->dev, bm->dev.dev);
+ release_device(bm->dev.dev);
+
+ return 0;
+}
+
+static int
+bridge_enable_member(struct bridge_member *bm)
+{
+ struct bridge_state *bst = bm->bst;
+ int ret;
+
+ if (!bm->present)
+ return 0;
+
+ ret = claim_device(bm->dev.dev);
+ if (ret < 0)
+ goto error;
+
+ ret = system_bridge_addif(&bst->dev, bm->dev.dev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ bm->present = false;
+ bst->n_present--;
+ return ret;
+}
+
+static void
+bridge_member_cb(struct device_user *dev, enum device_event ev)
+{
+ struct bridge_member *bm = container_of(dev, struct bridge_member, dev);
+ struct bridge_state *bst = bm->bst;
+
+ switch (ev) {
+ case DEV_EVENT_ADD:
+ assert(!bm->present);
+
+ bm->present = true;
+ bst->n_present++;
+
+ if (bst->dev.active)
+ bridge_enable_member(bm);
+ else if (bst->n_present == 1)
+ set_device_present(&bst->dev, true);
+
+ break;
+ case DEV_EVENT_REMOVE:
+ if (!bm->present)
+ return;
+
+ if (bst->dev.active)
+ bridge_disable_member(bm);
+
+ bm->present = false;
+ bm->bst->n_present--;
+ if (bst->n_present == 0)
+ set_device_present(&bst->dev, false);
+
+ break;
+ default:
+ return;
+ }
+}
+
+static int
+bridge_set_down(struct bridge_state *bst)
+{
+ struct bridge_member *bm;
+
+ bst->set_state(&bst->dev, false);
+
+ list_for_each_entry(bm, &bst->members, list)
+ bridge_disable_member(bm);
+
+ system_bridge_delbr(&bst->dev);
+
+ return 0;
+}
+
+static int
+bridge_set_up(struct bridge_state *bst)
+{
+ struct bridge_member *bm;
+ int ret;
+
+ if (!bst->n_present)
+ return -ENOENT;
+
+ ret = system_bridge_addbr(&bst->dev);
+ if (ret < 0)
+ goto out;
+
+ list_for_each_entry(bm, &bst->members, list)
+ bridge_enable_member(bm);
+
+ if (!bst->n_present) {
+ /* initialization of all member interfaces failed */
+ system_bridge_delbr(&bst->dev);
+ set_device_present(&bst->dev, false);
+ return -ENOENT;
+ }
+
+ ret = bst->set_state(&bst->dev, true);
+ if (ret < 0)
+ bridge_set_down(bst);
+
+out:
+ return ret;
+}
+
+static int
+bridge_set_state(struct device *dev, bool up)
+{
+ struct bridge_state *bst;
+
+ bst = container_of(dev, struct bridge_state, dev);
+
+ if (up)
+ return bridge_set_up(bst);
+ else
+ return bridge_set_down(bst);
+}
+
+static struct bridge_member *
+bridge_create_member(struct bridge_state *bst, struct device *dev)
+{
+ struct bridge_member *bm;
+
+ bm = calloc(1, sizeof(*bm));
+ bm->bst = bst;
+ bm->dev.cb = bridge_member_cb;
+ add_device_user(&bm->dev, dev);
+
+ list_add(&bm->list, &bst->members);
+
+ if (bst->dev.active)
+ bridge_enable_member(bm);
+
+ return bm;
+}
+
+static void
+bridge_free_member(struct bridge_member *bm)
+{
+ if (bm->present) {
+ bridge_member_cb(&bm->dev, DEV_EVENT_REMOVE);
+ bm->bst->n_present--;
+ if (bm->bst->dev.active)
+ bridge_disable_member(bm);
+ }
+
+ list_del(&bm->list);
+ remove_device_user(&bm->dev);
+ free(bm);
+}
+
+static void
+bridge_add_member(struct bridge_state *bst, const char *name)
+{
+ struct device *dev;
+
+ dev = get_device(name, true);
+ if (!dev)
+ return;
+
+ bridge_create_member(bst, dev);
+}
+
+static int
+bridge_hotplug_add(struct device *dev, struct device *member)
+{
+ struct bridge_state *bst = container_of(dev, struct bridge_state, dev);
+
+ bridge_create_member(bst, member);
+
+ return 0;
+}
+
+static int
+bridge_hotplug_del(struct device *dev, struct device *member)
+{
+ struct bridge_state *bst = container_of(dev, struct bridge_state, dev);
+ struct bridge_member *bm;
+
+ list_for_each_entry(bm, &bst->members, list) {
+ if (bm->dev.dev != member)
+ continue;
+
+ bridge_free_member(bm);
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static const struct device_hotplug_ops bridge_ops = {
+ .add = bridge_hotplug_add,
+ .del = bridge_hotplug_del
+};
+
+static void
+bridge_parse_config(struct bridge_state *bst, struct uci_section *s)
+{
+ struct uci_element *e;
+ struct uci_option *o;
+ char buf[IFNAMSIZ + 1];
+ char *p, *end;
+ int len;
+
+ o = uci_lookup_option(uci_ctx, s, "ifname");
+ if (!o)
+ return;
+
+ if (o->type == UCI_TYPE_LIST) {
+ uci_foreach_element(&o->v.list, e)
+ bridge_add_member(bst, e->name);
+ } else {
+ p = o->v.string;
+ do {
+ if (!*p)
+ break;
+
+ if (*p == ' ')
+ continue;
+
+ end = strchr(p, ' ');
+ if (!end) {
+ bridge_add_member(bst, p);
+ break;
+ }
+
+ len = end - p;
+ if (len <= IFNAMSIZ) {
+ memcpy(buf, p, len);
+ buf[len] = 0;
+ bridge_add_member(bst, buf);
+ }
+ p = end;
+ } while (p++);
+ }
+}
+
+static void
+bridge_free(struct device *dev)
+{
+ struct bridge_state *bst;
+ struct bridge_member *bm;
+
+ bst = container_of(dev, struct bridge_state, dev);
+ while (!list_empty(&bst->members)) {
+ bm = list_first_entry(&bst->members, struct bridge_member, list);
+ bridge_free_member(bm);
+ }
+ free(bst);
+}
+
+struct device *
+bridge_create(const char *name, struct uci_section *s)
+{
+ static const struct device_type bridge_type = {
+ .name = "Bridge",
+ .free = bridge_free,
+ };
+ struct bridge_state *bst;
+ struct device *dev;
+
+ dev = get_device(name, false);
+ if (dev)
+ return NULL;
+
+ bst = calloc(1, sizeof(*bst));
+ if (!bst)
+ return NULL;
+
+ init_device(&bst->dev, &bridge_type, name);
+
+ bst->set_state = bst->dev.set_state;
+ bst->dev.set_state = bridge_set_state;
+
+ bst->dev.hotplug_ops = &bridge_ops;
+
+ INIT_LIST_HEAD(&bst->members);
+
+ if (s)
+ bridge_parse_config(bst, s);
+
+ return &bst->dev;
+}
+
+int
+interface_attach_bridge(struct interface *iface, struct uci_section *s)
+{
+ struct device *dev;
+ char brname[IFNAMSIZ];
+
+ snprintf(brname, IFNAMSIZ - 1, "br-%s", iface->name);
+ brname[IFNAMSIZ - 1] = 0;
+
+ dev = bridge_create(brname, s);
+ if (!dev)
+ return -1;
+
+ add_device_user(&iface->main_dev, dev);
+ return 0;
+}
--- /dev/null
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "netifd.h"
+
+struct uci_context *uci_ctx;
+
+static void config_parse_interface(struct uci_section *s)
+{
+ struct interface *iface;
+ const char *type;
+
+ DPRINTF("Create interface '%s'\n", s->e.name);
+
+ iface = alloc_interface(s->e.name);
+ type = uci_lookup_option_string(uci_ctx, s, "type");
+
+ if (!type)
+ type = "";
+
+ if (!strcmp(type, "bridge"))
+ interface_attach_bridge(iface, s);
+}
+
+void config_init_interfaces(const char *name)
+{
+ struct uci_context *ctx;
+ struct uci_package *p = NULL;
+ struct uci_element *e;
+
+ ctx = uci_alloc_context();
+ uci_ctx = ctx;
+
+ uci_set_confdir(ctx, "./config");
+
+ if (uci_load(ctx, "network", &p)) {
+ fprintf(stderr, "Failed to load network config\n");
+ return;
+ }
+
+ uci_foreach_element(&p->sections, e) {
+ struct uci_section *s = uci_to_section(e);
+
+ if (name && strcmp(s->e.name, name) != 0)
+ continue;
+
+ if (!strcmp(s->type, "interface"))
+ config_parse_interface(s);
+ }
+}
--- /dev/null
+# Copyright (C) 2006 OpenWrt.org
+
+config interface loopback
+ option ifname lo
+ option proto static
+ option ipaddr 127.0.0.1
+ option netmask 255.0.0.0
+
+config interface lan
+ option ifname 'eth0.1 eth0.2'
+ option type bridge
+ option proto static
+ option ipaddr 192.168.1.1
+ option netmask 255.255.255.0
--- /dev/null
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <libubox/uapi.h>
+
+#include "netifd.h"
+#include "system.h"
+
+static struct avl_tree devices;
+
+static int avl_strcmp(const void *k1, const void *k2, void *ptr)
+{
+ return strcmp(k1, k2);
+}
+
+static void API_CTOR dev_init(void)
+{
+ avl_init(&devices, avl_strcmp, false, NULL);
+}
+
+static void free_device(struct device *dev)
+{
+ cleanup_device(dev);
+ free(dev);
+}
+
+static void broadcast_device_event(struct device *dev, enum device_event ev)
+{
+ struct device_user *dep, *tmp;
+
+ list_for_each_entry_safe(dep, tmp, &dev->users, list) {
+ if (!dep->cb)
+ continue;
+
+ dep->cb(dep, ev);
+ }
+}
+
+static int set_device_state(struct device *dev, bool state)
+{
+ if (state) {
+ broadcast_device_event(dev, DEV_EVENT_SETUP);
+ system_if_up(dev);
+ broadcast_device_event(dev, DEV_EVENT_UP);
+ } else {
+ broadcast_device_event(dev, DEV_EVENT_TEARDOWN);
+ system_if_down(dev);
+ broadcast_device_event(dev, DEV_EVENT_DOWN);
+ }
+ return 0;
+}
+
+int claim_device(struct device *dev)
+{
+ int ret;
+
+ DPRINTF("claim device %s, new refcount: %d\n", dev->ifname, dev->active + 1);
+ if (++dev->active != 1)
+ return 0;
+
+ ret = dev->set_state(dev, true);
+ if (ret != 0)
+ dev->active = 0;
+
+ return ret;
+}
+
+void release_device(struct device *dev)
+{
+ dev->active--;
+ DPRINTF("release device %s, new refcount: %d\n", dev->ifname, dev->active);
+ assert(dev->active >= 0);
+
+ if (!dev->active)
+ dev->set_state(dev, false);
+}
+
+int check_device_state(struct device *dev)
+{
+ if (!dev->type->check_state)
+ return 0;
+
+ return dev->type->check_state(dev);
+}
+
+int init_device(struct device *dev, const struct device_type *type, const char *ifname)
+{
+ int ret;
+
+ assert(dev);
+ assert(type);
+
+ if (ifname)
+ strncpy(dev->ifname, ifname, IFNAMSIZ);
+
+ if (!dev->set_state)
+ dev->set_state = set_device_state;
+
+ fprintf(stderr, "Initialize interface '%s'\n", dev->ifname);
+ INIT_LIST_HEAD(&dev->users);
+ dev->avl.key = dev->ifname;
+ dev->type = type;
+
+ ret = avl_insert(&devices, &dev->avl);
+ if (ret < 0)
+ return ret;
+
+ check_device_state(dev);
+ return 0;
+}
+
+struct device *get_device(const char *name, bool create)
+{
+ static const struct device_type simple_type = {
+ .name = "Device",
+ .check_state = system_if_check,
+ .free = free_device,
+ };
+ struct device *dev;
+
+
+ if (strchr(name, '.'))
+ return get_vlan_device_chain(name, create);
+
+ dev = avl_find_element(&devices, name, dev, avl);
+ if (dev)
+ return dev;
+
+ if (!create)
+ return NULL;
+
+ dev = calloc(1, sizeof(*dev));
+ init_device(dev, &simple_type, name);
+
+ return dev;
+}
+
+void cleanup_device(struct device *dev)
+{
+ struct device_user *dep, *tmp;
+
+ fprintf(stderr, "Clean up interface '%s'\n", dev->ifname);
+ list_for_each_entry_safe(dep, tmp, &dev->users, list) {
+ if (!dep->cb)
+ continue;
+
+ dep->cb(dep, DEV_EVENT_REMOVE);
+ }
+
+ avl_delete(&devices, &dev->avl);
+}
+
+void set_device_present(struct device *dev, bool state)
+{
+ if (dev->present == state)
+ return;
+
+ DPRINTF("Device '%s' %s present\n", dev->ifname, state ? "is now" : "is no longer" );
+ dev->present = state;
+ broadcast_device_event(dev, state ? DEV_EVENT_ADD : DEV_EVENT_REMOVE);
+}
+
+void add_device_user(struct device_user *dep, struct device *dev)
+{
+ dep->dev = dev;
+ list_add(&dep->list, &dev->users);
+ if (dep->cb && dev->present) {
+ dep->cb(dep, DEV_EVENT_ADD);
+ if (dev->active)
+ dep->cb(dep, DEV_EVENT_UP);
+ }
+}
+
+void remove_device_user(struct device_user *dep)
+{
+ struct device *dev = dep->dev;
+
+ list_del(&dep->list);
+
+ if (list_empty(&dev->users)) {
+ /* all references have gone away, remove this interface */
+ dev->type->free(dev);
+ }
+
+ dep->dev = NULL;
+}
--- /dev/null
+#ifndef __LL_H
+#define __LL_H
+
+#include <libubox/avl.h>
+
+struct device;
+struct device_hotplug_ops;
+
+typedef int (*device_state_cb)(struct device *, bool up);
+
+struct device_type {
+ const char *name;
+
+ int (*check_state)(struct device *);
+ void (*free)(struct device *);
+};
+
+/*
+ * link layer device. typically represents a linux network device.
+ * can be used to support VLANs as well
+ */
+struct device {
+ const struct device_type *type;
+
+ struct avl_node avl;
+ struct list_head users;
+
+ char ifname[IFNAMSIZ + 1];
+ int ifindex;
+
+ bool present;
+ int active;
+
+ /* set interface up or down */
+ device_state_cb set_state;
+
+ const struct device_hotplug_ops *hotplug_ops;
+};
+
+/* events broadcasted to all users of a device */
+enum device_event {
+ /* device has been added to the system and can be brought up */
+ DEV_EVENT_ADD,
+
+ /* device has been removed */
+ DEV_EVENT_REMOVE,
+
+ /* device is being brought up */
+ DEV_EVENT_SETUP,
+
+ /* device is being brought down */
+ DEV_EVENT_TEARDOWN,
+
+ /* device has been brought up */
+ DEV_EVENT_UP,
+
+ /* device has been brought down */
+ DEV_EVENT_DOWN,
+
+ /* device has changed its link state to up */
+ DEV_EVENT_LINK_UP,
+
+ /* device has changed its link state to down */
+ DEV_EVENT_LINK_DOWN,
+};
+
+/*
+ * device dependency with callbacks
+ */
+struct device_user {
+ struct list_head list;
+
+ struct device *dev;
+ void (*cb)(struct device_user *, enum device_event);
+};
+
+struct device_hotplug_ops {
+ int (*add)(struct device *main, struct device *member);
+ int (*del)(struct device *main, struct device *member);
+};
+
+int init_device(struct device *iface, const struct device_type *type, const char *ifname);
+void cleanup_device(struct device *iface);
+struct device *get_device(const char *name, bool create);
+void add_device_user(struct device_user *dep, struct device *iface);
+void remove_device_user(struct device_user *dep);
+
+void set_device_present(struct device *dev, bool state);
+int claim_device(struct device *dev);
+void release_device(struct device *dev);
+int check_device_state(struct device *dev);
+
+struct device *get_vlan_device_chain(const char *ifname, bool create);
+
+#endif
--- /dev/null
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "netifd.h"
+#include "ubus.h"
+
+LIST_HEAD(interfaces);
+
+static int interface_event(struct interface *iface, enum interface_event ev)
+{
+ if (!iface->state || !iface->state->event)
+ return 0;
+
+ return iface->state->event(iface, iface->state, ev);
+}
+
+static void
+__set_interface_up(struct interface *iface)
+{
+ if (iface->up)
+ return;
+
+ if (claim_device(iface->main_dev.dev) < 0)
+ return;
+
+ if (interface_event(iface, IFEV_UP) < 0) {
+ release_device(iface->main_dev.dev);
+ return;
+ }
+
+ iface->up = true;
+}
+
+static void
+__set_interface_down(struct interface *iface)
+{
+ if (!iface->up)
+ return;
+
+ iface->up = false;
+ interface_event(iface, IFEV_DOWN);
+ release_device(iface->main_dev.dev);
+}
+
+static void
+interface_cb(struct device_user *dep, enum device_event ev)
+{
+ struct interface *iface;
+ bool new_state;
+
+ iface = container_of(dep, struct interface, main_dev);
+ switch (ev) {
+ case DEV_EVENT_ADD:
+ new_state = true;
+ break;
+ case DEV_EVENT_REMOVE:
+ new_state = false;
+ break;
+ default:
+ return;
+ }
+
+ if (iface->active == new_state)
+ return;
+
+ iface->active = new_state;
+
+ if (new_state) {
+ if (iface->autostart)
+ __set_interface_up(iface);
+ } else
+ __set_interface_down(iface);
+}
+
+struct interface *
+alloc_interface(const char *name)
+{
+ struct interface *iface;
+
+ iface = get_interface(name);
+ if (iface)
+ return iface;
+
+ iface = calloc(1, sizeof(*iface));
+ iface->main_dev.cb = interface_cb;
+ iface->l3_iface = &iface->main_dev;
+ strncpy(iface->name, name, sizeof(iface->name) - 1);
+ list_add(&iface->list, &interfaces);
+ netifd_ubus_add_interface(iface);
+
+ return iface;
+}
+
+void
+free_interface(struct interface *iface)
+{
+ netifd_ubus_remove_interface(iface);
+ list_del(&iface->list);
+ if (iface->state && iface->state->free)
+ iface->state->free(iface, iface->state);
+ free(iface);
+}
+
+struct interface *
+get_interface(const char *name)
+{
+ struct interface *iface;
+
+ list_for_each_entry(iface, &interfaces, list) {
+ if (!strcmp(iface->name, name))
+ return iface;
+ }
+ return NULL;
+}
+
+void
+interface_remove_link(struct interface *iface, struct device *llif)
+{
+ struct device *dev = iface->main_dev.dev;
+
+ if (dev && dev->hotplug_ops) {
+ dev->hotplug_ops->del(dev, llif);
+ return;
+ }
+
+ remove_device_user(&iface->main_dev);
+}
+
+int
+interface_add_link(struct interface *iface, struct device *llif)
+{
+ struct device *dev = iface->main_dev.dev;
+
+ if (dev && dev->hotplug_ops)
+ return dev->hotplug_ops->add(dev, llif);
+
+ if (iface->main_dev.dev)
+ interface_remove_link(iface, NULL);
+
+ add_device_user(&iface->main_dev, llif);
+
+ return 0;
+}
+
+int
+set_interface_up(struct interface *iface)
+{
+ iface->autostart = true;
+
+ if (iface->up || !iface->active)
+ return -1;
+
+ __set_interface_up(iface);
+ return 0;
+}
+
+int
+set_interface_down(struct interface *iface)
+{
+ iface->autostart = false;
+
+ if (!iface->up)
+ return -1;
+
+ __set_interface_down(iface);
+
+ return 0;
+}
--- /dev/null
+#ifndef __NETIFD_INTERFACE_H
+#define __NETIFD_INTERFACE_H
+
+struct interface;
+struct interface_proto;
+struct interface_proto_state;
+
+extern struct list_head interfaces;
+
+enum interface_event {
+ IFEV_UP,
+ IFEV_DOWN,
+};
+
+struct interface_proto_state {
+ const struct interface_proto *proto;
+
+ int (*event)(struct interface *, struct interface_proto_state *, enum interface_event ev);
+ void (*free)(struct interface *, struct interface_proto_state *);
+};
+
+/*
+ * interface configuration
+ */
+struct interface {
+ struct list_head list;
+
+ char name[IFNAMSIZ - 2];
+
+ /* interface is up and running */
+ bool up;
+
+ /* interface can be brought up */
+ bool active;
+
+ /* interface will be brought up when available */
+ bool autostart;
+
+ /* main interface that the interface is bound to */
+ struct device_user main_dev;
+
+ /* interface that layer 3 communication will go through */
+ struct device_user *l3_iface;
+
+ /* primary protocol state */
+ struct interface_proto_state *state;
+
+ struct ubus_object ubus;
+};
+
+struct interface *get_interface(const char *name);
+struct interface *alloc_interface(const char *name);
+void free_interface(struct interface *iface);
+
+int set_interface_up(struct interface *iface);
+int set_interface_down(struct interface *iface);
+
+int interface_add_link(struct interface *iface, struct device *llif);
+void interface_remove_link(struct interface *iface, struct device *llif);
+
+int interface_attach_bridge(struct interface *iface, struct uci_section *s);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#include "netifd.h"
+#include "ubus.h"
+
+static int usage(const char *progname)
+{
+ fprintf(stderr, "Usage: %s [options]\n"
+ "Options:\n"
+ " -s <path>: Path to the ubus socket\n"
+ "\n", progname);
+
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ const char *socket = NULL;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "s:")) != -1) {
+ switch(ch) {
+ case 's':
+ socket = optarg;
+ break;
+ default:
+ return usage(argv[0]);
+ }
+ }
+
+ if (netifd_ubus_init(NULL) < 0) {
+ fprintf(stderr, "Failed to connect to ubus\n");
+ return 1;
+ }
+
+ config_init_interfaces(NULL);
+
+ uloop_run();
+
+ netifd_ubus_done();
+
+ return 0;
+}
--- /dev/null
+#ifndef __NETIFD_H
+#define __NETIFD_H
+
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <libubox/list.h>
+#include <libubox/uloop.h>
+
+#include <libubus.h>
+#include <uci.h>
+
+#include "device.h"
+#include "interface.h"
+
+#ifdef DEBUG
+#define DPRINTF(format, ...) fprintf(stderr, "%s(%d): " format, __func__, __LINE__, ## __VA_ARGS__)
+#else
+#define DPRINTF(...) do {} while(0)
+#endif
+
+extern struct uci_context *uci_ctx;
+
+void config_init_interfaces(const char *name);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+
+#include "netifd.h"
+
+int system_bridge_addbr(struct device *bridge)
+{
+ DPRINTF("brctl addbr %s\n", bridge->ifname);
+ return 0;
+}
+
+int system_bridge_delbr(struct device *bridge)
+{
+ DPRINTF("brctl delbr %s\n", bridge->ifname);
+ return 0;
+}
+
+int system_bridge_addif(struct device *bridge, struct device *dev)
+{
+ DPRINTF("brctl addif %s %s\n", bridge->ifname, dev->ifname);
+ return 0;
+}
+
+int system_bridge_delif(struct device *bridge, struct device *dev)
+{
+ DPRINTF("brctl delif %s %s\n", bridge->ifname, dev->ifname);
+ return 0;
+}
+
+int system_vlan_add(struct device *dev, int id)
+{
+ DPRINTF("vconfig add %s %d\n", dev->ifname, id);
+ return 0;
+}
+
+int system_vlan_del(struct device *dev)
+{
+ DPRINTF("vconfig rem %s\n", dev->ifname);
+ return 0;
+}
+
+int system_if_up(struct device *dev)
+{
+ DPRINTF("ifconfig %s up\n", dev->ifname);
+ return 0;
+}
+
+int system_if_down(struct device *dev)
+{
+ DPRINTF("ifconfig %s down\n", dev->ifname);
+ return 0;
+}
+
+int system_if_check(struct device *dev)
+{
+ dev->ifindex = 0;
+
+ if (!strcmp(dev->ifname, "eth0"))
+ set_device_present(dev, true);
+
+ return 0;
+}
--- /dev/null
+#ifndef __NETIFD_SYSTEM_H
+#define __NETIFD_SYSTEM_H
+
+#include "device.h"
+
+int system_bridge_addbr(struct device *bridge);
+int system_bridge_delbr(struct device *bridge);
+int system_bridge_addif(struct device *bridge, struct device *dev);
+int system_bridge_delif(struct device *bridge, struct device *dev);
+
+int system_vlan_add(struct device *dev, int id);
+int system_vlan_del(struct device *dev);
+
+int system_if_up(struct device *dev);
+int system_if_down(struct device *dev);
+int system_if_check(struct device *dev);
+
+#endif
--- /dev/null
+#include <string.h>
+
+#include "netifd.h"
+#include "ubus.h"
+
+static struct ubus_context *ctx = NULL;
+
+/* global object */
+
+static const struct ubus_signature main_object_sig[] = {
+ UBUS_METHOD_START("add_device"),
+ UBUS_FIELD(STRING, "name"),
+ UBUS_METHOD_END(),
+
+ UBUS_METHOD_START("del_device"),
+ UBUS_FIELD(STRING, "name"),
+ UBUS_METHOD_END(),
+};
+
+static struct ubus_object_type main_object_type =
+ UBUS_OBJECT_TYPE("netifd", main_object_sig);
+
+enum {
+ DEV_NAME,
+ DEV_FORCE,
+ DEV_LAST,
+};
+
+static const struct blobmsg_policy dev_policy[] = {
+ [DEV_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
+ [DEV_FORCE] = { .name = "force", .type = BLOBMSG_TYPE_INT8 },
+};
+
+static int netifd_handle_device(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct device *dev;
+ struct blob_attr *tb[DEV_LAST];
+ bool add = !strncmp(method, "add", 3);
+
+ blobmsg_parse(dev_policy, ARRAY_SIZE(dev_policy), tb, blob_data(msg), blob_len(msg));
+
+ if (!tb[DEV_NAME])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ dev = get_device(blobmsg_data(tb[DEV_NAME]), false);
+ if (!dev)
+ return UBUS_STATUS_NOT_FOUND;
+
+ if (!add || (tb[DEV_FORCE] && blobmsg_get_u8(tb[DEV_FORCE])))
+ set_device_present(dev, add);
+ else
+ check_device_state(dev);
+
+ return 0;
+}
+
+static struct ubus_method main_object_methods[] = {
+ { .name = "add_device", .handler = netifd_handle_device },
+ { .name = "del_device", .handler = netifd_handle_device },
+};
+
+static struct ubus_object main_object = {
+ .name = "network.interface",
+ .type = &main_object_type,
+ .methods = main_object_methods,
+ .n_methods = ARRAY_SIZE(main_object_methods),
+};
+
+int netifd_ubus_init(const char *path)
+{
+ int ret;
+
+ ctx = ubus_connect(path);
+ if (!ctx)
+ return -EIO;
+
+ DPRINTF("connected as %08x\n", ctx->local_id);
+ uloop_init();
+ ubus_add_uloop(ctx);
+
+ ret = ubus_add_object(ctx, &main_object);
+ if (ret != 0)
+ fprintf(stderr, "Failed to publish object: %s\n", ubus_strerror(ret));
+
+ return 0;
+}
+
+void netifd_ubus_done(void)
+{
+ ubus_free(ctx);
+}
+
+
+/* per-interface object */
+static const struct ubus_signature iface_object_sig[] = {
+ UBUS_METHOD_START("up"),
+ UBUS_METHOD_END(),
+
+ UBUS_METHOD_START("down"),
+ UBUS_METHOD_END(),
+};
+
+static struct ubus_object_type iface_object_type =
+ UBUS_OBJECT_TYPE("netifd", iface_object_sig);
+
+
+static int netifd_handle_up(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct interface *iface;
+
+ iface = container_of(obj, struct interface, ubus);
+ set_interface_up(iface);
+
+ return 0;
+}
+
+static int netifd_handle_down(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct interface *iface;
+
+ iface = container_of(obj, struct interface, ubus);
+ set_interface_down(iface);
+
+ return 0;
+}
+
+static struct ubus_method iface_object_methods[] = {
+ { .name = "up", .handler = netifd_handle_up },
+ { .name = "down", .handler = netifd_handle_down },
+};
+
+
+void netifd_ubus_add_interface(struct interface *iface)
+{
+ struct ubus_object *obj = &iface->ubus;
+ char *name;
+
+ name = malloc(strlen(main_object.name) + strlen(iface->name) + 2);
+ if (!name)
+ return;
+
+ sprintf(name, "%s.%s", main_object.name, iface->name);
+ obj->name = name;
+ obj->type = &iface_object_type;
+ obj->methods = iface_object_methods;
+ obj->n_methods = ARRAY_SIZE(iface_object_methods);
+ if (ubus_add_object(ctx, &iface->ubus)) {
+ DPRINTF("failed to publish ubus object for interface '%s'\n", iface->name);
+ free(name);
+ obj->name = NULL;
+ }
+}
+
+void netifd_ubus_remove_interface(struct interface *iface)
+{
+ if (!iface->ubus.name)
+ return;
+
+ ubus_remove_object(ctx, &iface->ubus);
+ free((void *) iface->ubus.name);
+}
--- /dev/null
+#ifndef __NETIFD_UBUS_H
+#define __NETIFD_UBUS_H
+
+int netifd_ubus_init(const char *path);
+void netifd_ubus_done(void);
+void netifd_ubus_add_interface(struct interface *iface);
+void netifd_ubus_remove_interface(struct interface *iface);
+
+#endif
--- /dev/null
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "netifd.h"
+#include "system.h"
+
+struct vlan_device {
+ struct device dev;
+ struct device_user dep;
+
+ device_state_cb set_state;
+ int id;
+};
+
+static void free_vlan_if(struct device *iface)
+{
+ struct vlan_device *vldev;
+
+ vldev = container_of(iface, struct vlan_device, dev);
+ remove_device_user(&vldev->dep);
+ cleanup_device(&vldev->dev);
+ free(vldev);
+}
+
+static int vlan_set_device_state(struct device *dev, bool up)
+{
+ struct vlan_device *vldev;
+ int ret = 0;
+
+ vldev = container_of(dev, struct vlan_device, dev);
+ if (!up) {
+ vldev->set_state(dev, false);
+ system_vlan_del(dev);
+ release_device(vldev->dep.dev);
+ return 0;
+ }
+
+ ret = claim_device(vldev->dep.dev);
+ if (ret)
+ return ret;
+
+ system_vlan_add(vldev->dep.dev, vldev->id);
+ ret = vldev->set_state(dev, true);
+ if (ret)
+ release_device(vldev->dep.dev);
+
+ return ret;
+}
+
+static void vlan_dev_cb(struct device_user *dep, enum device_event ev)
+{
+ struct vlan_device *vldev;
+
+ vldev = container_of(dep, struct vlan_device, dep);
+ switch(ev) {
+ case DEV_EVENT_ADD:
+ set_device_present(&vldev->dev, true);
+ break;
+ case DEV_EVENT_REMOVE:
+ set_device_present(&vldev->dev, false);
+ break;
+ default:
+ break;
+ }
+}
+
+static struct device *get_vlan_device(struct device *dev, int id, bool create)
+{
+ static const struct device_type vlan_type = {
+ .name = "VLAN",
+ .free = free_vlan_if,
+ };
+ struct vlan_device *vldev;
+ struct device_user *dep;
+
+ /* look for an existing interface before creating a new one */
+ list_for_each_entry(dep, &dev->users, list) {
+ if (dep->cb != vlan_dev_cb)
+ continue;
+
+ vldev = container_of(dep, struct vlan_device, dep);
+ if (vldev->id != id)
+ continue;
+
+ return &vldev->dev;
+ }
+
+ if (!create)
+ return NULL;
+
+ vldev = calloc(1, sizeof(*vldev));
+ snprintf(vldev->dev.ifname, IFNAMSIZ, "%s.%d", dev->ifname, id);
+
+ init_device(&vldev->dev, &vlan_type, NULL);
+
+ vldev->set_state = vldev->dev.set_state;
+ vldev->dev.set_state = vlan_set_device_state;
+
+ vldev->id = id;
+
+ vldev->dep.cb = vlan_dev_cb;
+ add_device_user(&vldev->dep, dev);
+
+ return &vldev->dev;
+}
+
+static inline char *split_vlan(char *s)
+{
+ s = strchr(s, '.');
+ if (!s)
+ goto out;
+
+ *s = 0;
+ s++;
+
+out:
+ return s;
+}
+
+struct device *get_vlan_device_chain(const char *ifname, bool create)
+{
+ struct device *iface = NULL;
+ char *buf, *s, *next, *err = NULL;
+ int id;
+
+ buf = strdup(ifname);
+ if (!buf)
+ return NULL;
+
+ s = split_vlan(buf);
+ iface = get_device(buf, create);
+ if (!iface && !create)
+ goto error;
+
+ do {
+ next = split_vlan(s);
+ id = strtoul(s, &err, 10);
+ if (err && *err)
+ goto error;
+
+ iface = get_vlan_device(iface, id, create);
+ if (!iface)
+ goto error;
+
+ s = next;
+ if (!s)
+ goto out;
+ } while (1);
+
+error:
+ iface = NULL;
+out:
+ free(buf);
+ return iface;
+}