Initial import
authorFelix Fietkau <nbd@openwrt.org>
Sat, 19 Feb 2011 14:21:52 +0000 (15:21 +0100)
committerFelix Fietkau <nbd@openwrt.org>
Sun, 27 Mar 2011 00:42:33 +0000 (01:42 +0100)
15 files changed:
CMakeLists.txt [new file with mode: 0644]
bridge.c [new file with mode: 0644]
config.c [new file with mode: 0644]
config/network [new file with mode: 0644]
device.c [new file with mode: 0644]
device.h [new file with mode: 0644]
interface.c [new file with mode: 0644]
interface.h [new file with mode: 0644]
main.c [new file with mode: 0644]
netifd.h [new file with mode: 0644]
system-dummy.c [new file with mode: 0644]
system.h [new file with mode: 0644]
ubus.c [new file with mode: 0644]
ubus.h [new file with mode: 0644]
vlan.c [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..262ee3b
--- /dev/null
@@ -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)
diff --git a/bridge.c b/bridge.c
new file mode 100644 (file)
index 0000000..2aa0e68
--- /dev/null
+++ b/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;
+}
diff --git a/config.c b/config.c
new file mode 100644 (file)
index 0000000..a106db2
--- /dev/null
+++ b/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);
+       }
+}
diff --git a/config/network b/config/network
new file mode 100644 (file)
index 0000000..35276ab
--- /dev/null
@@ -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
diff --git a/device.c b/device.c
new file mode 100644 (file)
index 0000000..e1ffba1
--- /dev/null
+++ b/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;
+}
diff --git a/device.h b/device.h
new file mode 100644 (file)
index 0000000..9e7d309
--- /dev/null
+++ b/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
diff --git a/interface.c b/interface.c
new file mode 100644 (file)
index 0000000..d45f2d5
--- /dev/null
@@ -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;
+}
diff --git a/interface.h b/interface.h
new file mode 100644 (file)
index 0000000..0fd70d7
--- /dev/null
@@ -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
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..69aa5cd
--- /dev/null
+++ b/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;
+}
diff --git a/netifd.h b/netifd.h
new file mode 100644 (file)
index 0000000..fabf4d9
--- /dev/null
+++ b/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
diff --git a/system-dummy.c b/system-dummy.c
new file mode 100644 (file)
index 0000000..e143d8b
--- /dev/null
@@ -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;
+}
diff --git a/system.h b/system.h
new file mode 100644 (file)
index 0000000..f8428cc
--- /dev/null
+++ b/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
diff --git a/ubus.c b/ubus.c
new file mode 100644 (file)
index 0000000..30db080
--- /dev/null
+++ b/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);
+}
diff --git a/ubus.h b/ubus.h
new file mode 100644 (file)
index 0000000..668a34b
--- /dev/null
+++ b/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
diff --git a/vlan.c b/vlan.c
new file mode 100644 (file)
index 0000000..3de1121
--- /dev/null
+++ b/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;
+}