Browse Source

Initial import

Felix Fietkau 13 years ago
commit
5d1fff7af6
15 changed files with 1423 additions and 0 deletions
  1. 13 0
      CMakeLists.txt
  2. 343 0
      bridge.c
  3. 51 0
      config.c
  4. 14 0
      config/network
  5. 188 0
      device.c
  6. 95 0
      device.h
  7. 169 0
      interface.c
  8. 63 0
      interface.h
  9. 46 0
      main.c
  10. 29 0
      netifd.h
  11. 62 0
      system-dummy.c
  12. 18 0
      system.h
  13. 167 0
      ubus.c
  14. 9 0
      ubus.h
  15. 156 0
      vlan.c

+ 13 - 0
CMakeLists.txt

@@ -0,0 +1,13 @@
+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)

+ 343 - 0
bridge.c

@@ -0,0 +1,343 @@
+#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;
+}

+ 51 - 0
config.c

@@ -0,0 +1,51 @@
+#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);
+	}
+}

+ 14 - 0
config/network

@@ -0,0 +1,14 @@
+# 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

+ 188 - 0
device.c

@@ -0,0 +1,188 @@
+#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;
+}

+ 95 - 0
device.h

@@ -0,0 +1,95 @@
+#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

+ 169 - 0
interface.c

@@ -0,0 +1,169 @@
+#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;
+}

+ 63 - 0
interface.h

@@ -0,0 +1,63 @@
+#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

+ 46 - 0
main.c

@@ -0,0 +1,46 @@
+#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;
+}

+ 29 - 0
netifd.h

@@ -0,0 +1,29 @@
+#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

+ 62 - 0
system-dummy.c

@@ -0,0 +1,62 @@
+#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;
+}

+ 18 - 0
system.h

@@ -0,0 +1,18 @@
+#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

+ 167 - 0
ubus.c

@@ -0,0 +1,167 @@
+#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);
+}

+ 9 - 0
ubus.h

@@ -0,0 +1,9 @@
+#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

+ 156 - 0
vlan.c

@@ -0,0 +1,156 @@
+#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;
+}