Bump netifd-2016-06-06-99e6dc68bbac5a57a0ebca810a9dc36e38667821

This commit is contained in:
jbnadal
2016-12-06 17:42:55 +01:00
parent 9f6ea62b0a
commit 2b5e82bd8c
46 changed files with 16693 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
cmake_minimum_required(VERSION 2.6)
PROJECT(netifd C)
ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -Wmissing-declarations)
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
IF(APPLE)
INCLUDE_DIRECTORIES(/opt/local/include)
LINK_DIRECTORIES(/opt/local/lib)
ENDIF()
SET(SOURCES
main.c utils.c system.c tunnel.c handler.c
interface.c interface-ip.c interface-event.c
iprule.c proto.c proto-static.c proto-shell.c
config.c device.c bridge.c vlan.c alias.c
macvlan.c ubus.c vlandev.c wireless.c)
SET(LIBS
ubox ubus uci json-c blobmsg_json)
IF (NOT DEFINED LIBNL_LIBS)
FIND_LIBRARY(libnl NAMES libnl-3 libnl nl-3 nl)
SET(LIBNL_LIBS ${libnl})
ENDIF()
IF("${CMAKE_SYSTEM_NAME}" MATCHES "Linux" AND NOT DUMMY_MODE)
SET(SOURCES ${SOURCES} system-linux.c)
SET(LIBS ${LIBS} ${LIBNL_LIBS})
ELSE()
ADD_DEFINITIONS(-DDUMMY_MODE=1)
SET(SOURCES ${SOURCES} system-dummy.c)
ENDIF()
IF(DEBUG)
ADD_DEFINITIONS(-DDEBUG -g3)
IF(NO_OPTIMIZE)
ADD_DEFINITIONS(-O0)
ENDIF()
ENDIF()
ADD_EXECUTABLE(netifd ${SOURCES})
TARGET_LINK_LIBRARIES(netifd ${LIBS})
INSTALL(TARGETS netifd
RUNTIME DESTINATION sbin
)

140
src/3P/netifd/DESIGN Normal file
View File

@@ -0,0 +1,140 @@
Design of the the network interface daemon (netifd)
----------------------------------------------------
The primary elements of netifd's state are devices and interfaces.
Devices
-------
A device represents either a physical Linux interface (e.g. eth0), or a
virtual link, bound to a static route, a socket or some other external trigger
(e.g. for VPN links or tunnels).
Anything that needs to be aware of device state registers a device_user, which
is bound to the device and contains a callback for receiving events on device
changes. As soon as the last device_user is removed, the device itself is freed
immediately.
Devices can also internally reference other devices, this is used to manage
specific kinds of devices, such as bridges or vlans, which do not require
special treatment from interfaces or other high level data structures, with
the exception of adding more member interfaces via hotplug, which is useful
for bridges or bonding interfaces.
The device up/down state is managed through refcounting. A device can be
brought up using claim_device(), and its reference released again with
release_device(). As soon as the refcount goes to zero, the device is
immediately brought down.
If the device cannot be brought up, claim_device() will return a non-zero
status and the caller will not have increased the refcount.
A registered device may not be available immediately, an interface or another
device could also be attached to it, waiting for it to appear in the system,
to support triggering interfaces via hotplug.
All device state transitions are announced via events to all the device_user
callbacks. The following event types are supported:
DEV_EVENT_ADD:
The device is now present in the system. When a device_user is being added
to a device and the device was already present, this event is generated
immediately.
DEV_EVENT_REMOVE:
The device is no longer available. Either it is currently being removed,
or it has already disappeared. All users should drop their references
immediately and clean up their state for this device.
DEV_EVENT_SETUP:
The device is about to be brought up. This allows device users to apply
some low level configuration parameters if necessary, however this event
may not be emitted in all cases. Externally managed devices added via
hotplug may be already up, and in that case this notification is skipped.
DEV_EVENT_UP:
The device has been successfully brought up.
DEV_EVENT_TEARDOWN:
The device is about to be brought down
DEV_EVENT_DOWN:
The device has been brought down
The following optional events are supported on some devices:
DEV_EVENT_LINK_UP: a link has been established on this device
DEV_EVENT_LINK_DOWN: the link has been lost
Interfaces
----------
An interface represents a high level configuration applied to one or more
devices. An active interface must always be bound to one main device and
to a layer 3 device. By default, the layer 3 device points at the reference
to the main device, based on how simple protocols like static, dhcp, etc.
are set up. More complex protcol handlers such as ppp/pptp or VPN software
can remap the layer 3 interface to something else, and external modules
such as the firewall can take care of both interfaces if necessary.
An interface always has the following state information:
active:
The interface can be brought up (its main device is available)
autostart:
If the interface switches from inactive to active, netifd will attempt
to bring it up immediately. Manually setting an interface to up (regardless
of whether that was successful or not) will set this flag.
state:
IFS_SETUP:
The interface is currently being configured by the protocol handler
IFS_UP:
The interface is fully configured
IFS_TEARDOWN:
The interface is being deconfigured
IFS_DOWN:
The interface is down
An interface references only one protocol handler state, modular protocol
handlers such as PPP are expected to be written in a way that allows them
to be set up as slave to another protocol handler if necessary (useful for
PPPoE or PPTP).
Protocol handlers
-----------------
A protocol handler can be attached to anything that provides a callback
for state changes. For the simple case it is usually attached to an interface
directly.
The state of a protocol handler is tracked in a struct interface_proto_state,
and it depends on the state of the entity that's controlling it.
It responds to PROTO_CMD_SETUP and PROTO_SETUP_TEARDOWN commands, which
should not block at any point in time. Completion is signalled back to the
master by sending IFPEV_UP and IFPEV_DOWN events.
If the setup can be done fast without blocking for a noticeable amount of
time, the callback can do it and send back those events immediately.
If the setup can take longer, it should use uloop to schedule its actions
asynchronously and (if necessary) fork.
The protocol handler must be able to abort a setup request that's being
processed when it encounters a teardown command.
When a PROTO_SETUP_TEARDOWN call is issued and the 'force' parameter is
set, the protocol handler needs to clean up immediately as good as possible,
without waiting for its pending actions to complete. If it has spawned
any child processes, it needs to kill them and clean up their mess.
Simple protocol handlers can set the PROTO_FLAG_IMMEDIATE flag if they
can perform all required actions immediately without waiting and thus
do not need to schedule IFPEV_UP and IFPEV_DOWN transitions. This will
cause those events to be generated by core code instead.
## TODO: Configuration management, ubus callbacks

199
src/3P/netifd/alias.c Normal file
View File

@@ -0,0 +1,199 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "netifd.h"
#include "device.h"
#include "interface.h"
static struct avl_tree aliases;
struct alias_device {
struct avl_node avl;
struct device dev;
struct device_user dep;
struct device_user new_dep;
bool update;
char name[];
};
static const struct device_type alias_device_type;
static void alias_set_device(struct alias_device *alias, struct device *dev)
{
if (dev == alias->dep.dev) {
if (alias->update) {
device_remove_user(&alias->new_dep);
alias->update = false;
if (dev)
device_set_present(&alias->dev, true);
}
return;
}
device_set_present(&alias->dev, false);
device_remove_user(&alias->new_dep);
if (alias->dev.active) {
if (dev)
device_add_user(&alias->new_dep, dev);
alias->update = true;
return;
}
alias->update = false;
device_remove_user(&alias->dep);
alias->dev.hidden = !dev;
if (dev) {
device_set_ifindex(&alias->dev, dev->ifindex);
device_set_ifname(&alias->dev, dev->ifname);
device_add_user(&alias->dep, dev);
} else
device_set_ifname(&alias->dev, "");
}
static int
alias_device_set_state(struct device *dev, bool state)
{
struct alias_device *alias;
alias = container_of(dev, struct alias_device, dev);
if (!alias->dep.dev)
return -1;
if (state)
return device_claim(&alias->dep);
device_release(&alias->dep);
if (alias->update)
alias_set_device(alias, alias->new_dep.dev);
return 0;
}
static void alias_device_cb(struct device_user *dep, enum device_event ev)
{
struct alias_device *alias;
bool new_state = false;
alias = container_of(dep, struct alias_device, dep);
switch (ev) {
case DEV_EVENT_ADD:
new_state = true;
case DEV_EVENT_REMOVE:
device_set_present(&alias->dev, new_state);
break;
case DEV_EVENT_LINK_UP:
new_state = true;
case DEV_EVENT_LINK_DOWN:
device_set_link(&alias->dev, new_state);
break;
case DEV_EVENT_UPDATE_IFINDEX:
device_set_ifindex(&alias->dev, dep->dev->ifindex);
break;
default:
device_broadcast_event(&alias->dev, ev);
break;
}
}
static struct device *
alias_device_create(const char *name, struct blob_attr *attr)
{
struct alias_device *alias;
alias = calloc(1, sizeof(*alias) + strlen(name) + 1);
if (!alias)
return NULL;
strcpy(alias->name, name);
alias->dev.set_state = alias_device_set_state;
alias->dev.hidden = true;
device_init_virtual(&alias->dev, &alias_device_type, NULL);
alias->avl.key = alias->name;
avl_insert(&aliases, &alias->avl);
alias->dep.alias = true;
alias->dep.cb = alias_device_cb;
device_check_state(&alias->dev);
return &alias->dev;
}
static void alias_device_free(struct device *dev)
{
struct alias_device *alias;
alias = container_of(dev, struct alias_device, dev);
device_remove_user(&alias->new_dep);
device_remove_user(&alias->dep);
avl_delete(&aliases, &alias->avl);
free(alias);
}
static int alias_check_state(struct device *dev)
{
struct alias_device *alias;
struct interface *iface;
struct device *ndev = NULL;
alias = container_of(dev, struct alias_device, dev);
iface = vlist_find(&interfaces, alias->name, iface, node);
if (iface && iface->state == IFS_UP)
ndev = iface->l3_dev.dev;
alias_set_device(alias, ndev);
return 0;
}
static const struct device_type alias_device_type = {
.name = "Network alias",
.create = alias_device_create,
.free = alias_device_free,
.check_state = alias_check_state,
};
void
alias_notify_device(const char *name, struct device *dev)
{
struct alias_device *alias;
device_lock();
alias = avl_find_element(&aliases, name, alias, avl);
if (alias)
alias_set_device(alias, dev);
device_unlock();
}
struct device *
device_alias_get(const char *name)
{
struct alias_device *alias;
alias = avl_find_element(&aliases, name, alias, avl);
if (alias)
return &alias->dev;
return alias_device_create(name, NULL);
}
static void __init alias_init(void)
{
avl_init(&aliases, avl_strcmp, false, NULL);
}

726
src/3P/netifd/bridge.c Normal file
View File

@@ -0,0 +1,726 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include "netifd.h"
#include "device.h"
#include "interface.h"
#include "system.h"
enum {
BRIDGE_ATTR_IFNAME,
BRIDGE_ATTR_STP,
BRIDGE_ATTR_FORWARD_DELAY,
BRIDGE_ATTR_PRIORITY,
BRIDGE_ATTR_IGMP_SNOOP,
BRIDGE_ATTR_AGEING_TIME,
BRIDGE_ATTR_HELLO_TIME,
BRIDGE_ATTR_MAX_AGE,
BRIDGE_ATTR_BRIDGE_EMPTY,
BRIDGE_ATTR_MULTICAST_QUERIER,
BRIDGE_ATTR_HASH_MAX,
BRIDGE_ATTR_ROBUSTNESS,
BRIDGE_ATTR_QUERY_INTERVAL,
BRIDGE_ATTR_QUERY_RESPONSE_INTERVAL,
BRIDGE_ATTR_LAST_MEMBER_INTERVAL,
__BRIDGE_ATTR_MAX
};
static const struct blobmsg_policy bridge_attrs[__BRIDGE_ATTR_MAX] = {
[BRIDGE_ATTR_IFNAME] = { "ifname", BLOBMSG_TYPE_ARRAY },
[BRIDGE_ATTR_STP] = { "stp", BLOBMSG_TYPE_BOOL },
[BRIDGE_ATTR_FORWARD_DELAY] = { "forward_delay", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_PRIORITY] = { "priority", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_AGEING_TIME] = { "ageing_time", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_HELLO_TIME] = { "hello_time", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_MAX_AGE] = { "max_age", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_IGMP_SNOOP] = { "igmp_snooping", BLOBMSG_TYPE_BOOL },
[BRIDGE_ATTR_BRIDGE_EMPTY] = { "bridge_empty", BLOBMSG_TYPE_BOOL },
[BRIDGE_ATTR_MULTICAST_QUERIER] = { "multicast_querier", BLOBMSG_TYPE_BOOL },
[BRIDGE_ATTR_HASH_MAX] = { "hash_max", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_ROBUSTNESS] = { "robustness", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_QUERY_INTERVAL] = { "query_interval", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_QUERY_RESPONSE_INTERVAL] = { "query_response_interval", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_LAST_MEMBER_INTERVAL] = { "last_member_interval", BLOBMSG_TYPE_INT32 },
};
static const struct uci_blob_param_info bridge_attr_info[__BRIDGE_ATTR_MAX] = {
[BRIDGE_ATTR_IFNAME] = { .type = BLOBMSG_TYPE_STRING },
};
static const struct uci_blob_param_list bridge_attr_list = {
.n_params = __BRIDGE_ATTR_MAX,
.params = bridge_attrs,
.info = bridge_attr_info,
.n_next = 1,
.next = { &device_attr_list },
};
static struct device *bridge_create(const char *name, struct blob_attr *attr);
static void bridge_config_init(struct device *dev);
static void bridge_free(struct device *dev);
static void bridge_dump_info(struct device *dev, struct blob_buf *b);
enum dev_change_type
bridge_reload(struct device *dev, struct blob_attr *attr);
const struct device_type bridge_device_type = {
.name = "Bridge",
.config_params = &bridge_attr_list,
.create = bridge_create,
.config_init = bridge_config_init,
.reload = bridge_reload,
.free = bridge_free,
.dump_info = bridge_dump_info,
};
struct bridge_state {
struct device dev;
device_state_cb set_state;
struct blob_attr *config_data;
struct bridge_config config;
struct blob_attr *ifnames;
bool active;
bool force_active;
struct uloop_timeout retry;
struct bridge_member *primary_port;
struct vlist_tree members;
int n_present;
int n_failed;
};
struct bridge_member {
struct vlist_node node;
struct bridge_state *bst;
struct device_user dev;
bool present;
char name[];
};
static void
bridge_reset_primary(struct bridge_state *bst)
{
struct bridge_member *bm;
if (!bst->primary_port &&
(bst->dev.settings.flags & DEV_OPT_MACADDR))
return;
bst->primary_port = NULL;
bst->dev.settings.flags &= ~DEV_OPT_MACADDR;
vlist_for_each_element(&bst->members, bm, node) {
uint8_t *macaddr;
if (!bm->present)
continue;
bst->primary_port = bm;
if (bm->dev.dev->settings.flags & DEV_OPT_MACADDR)
macaddr = bm->dev.dev->settings.macaddr;
else
macaddr = bm->dev.dev->orig_settings.macaddr;
memcpy(bst->dev.settings.macaddr, macaddr, 6);
bst->dev.settings.flags |= DEV_OPT_MACADDR;
return;
}
}
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);
device_release(&bm->dev);
device_broadcast_event(&bst->dev, DEV_EVENT_TOPO_CHANGE);
return 0;
}
static int
bridge_enable_interface(struct bridge_state *bst)
{
int ret;
if (bst->active)
return 0;
ret = system_bridge_addbr(&bst->dev, &bst->config);
if (ret < 0)
return ret;
bst->active = true;
return 0;
}
static void
bridge_disable_interface(struct bridge_state *bst)
{
if (!bst->active)
return;
system_bridge_delbr(&bst->dev);
bst->active = false;
}
static int
bridge_enable_member(struct bridge_member *bm)
{
struct bridge_state *bst = bm->bst;
int ret;
if (!bm->present)
return 0;
ret = bridge_enable_interface(bst);
if (ret)
goto error;
/* Disable IPv6 for bridge members */
if (!(bm->dev.dev->settings.flags & DEV_OPT_IPV6)) {
bm->dev.dev->settings.ipv6 = 0;
bm->dev.dev->settings.flags |= DEV_OPT_IPV6;
}
ret = device_claim(&bm->dev);
if (ret < 0)
goto error;
ret = system_bridge_addif(&bst->dev, bm->dev.dev);
if (ret < 0) {
D(DEVICE, "Bridge device %s could not be added\n", bm->dev.dev->ifname);
goto error;
}
device_set_present(&bst->dev, true);
device_broadcast_event(&bst->dev, DEV_EVENT_TOPO_CHANGE);
return 0;
error:
bst->n_failed++;
bm->present = false;
bst->n_present--;
device_release(&bm->dev);
return ret;
}
static void
bridge_remove_member(struct bridge_member *bm)
{
struct bridge_state *bst = bm->bst;
if (!bm->present)
return;
if (bm == bst->primary_port)
bridge_reset_primary(bst);
if (bst->dev.active)
bridge_disable_member(bm);
bm->present = false;
bm->bst->n_present--;
if (bst->config.bridge_empty)
return;
bst->force_active = false;
if (bst->n_present == 0)
device_set_present(&bst->dev, false);
}
static void
bridge_free_member(struct bridge_member *bm)
{
struct device *dev = bm->dev.dev;
bridge_remove_member(bm);
device_remove_user(&bm->dev);
/*
* When reloading the config and moving a device from one bridge to
* another, the other bridge may have tried to claim this device
* before it was removed here.
* Ensure that claiming the device is retried by toggling its present
* state
*/
if (dev->present) {
device_set_present(dev, false);
device_set_present(dev, true);
}
free(bm);
}
static void
bridge_check_retry(struct bridge_state *bst)
{
if (!bst->n_failed)
return;
uloop_timeout_set(&bst->retry, 100);
}
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->n_present == 1)
device_set_present(&bst->dev, true);
if (bst->dev.active && !bridge_enable_member(bm)) {
/*
* Adding a bridge member can overwrite the bridge mtu
* in the kernel, apply the bridge settings in case the
* bridge mtu is set
*/
system_if_apply_settings(&bst->dev, &bst->dev.settings,
DEV_OPT_MTU | DEV_OPT_MTU6);
}
break;
case DEV_EVENT_REMOVE:
if (dev->hotplug) {
vlist_delete(&bst->members, &bm->node);
return;
}
if (bm->present)
bridge_remove_member(bm);
break;
default:
return;
}
}
static int
bridge_set_down(struct bridge_state *bst)
{
struct bridge_member *bm;
bst->set_state(&bst->dev, false);
vlist_for_each_element(&bst->members, bm, node)
bridge_disable_member(bm);
bridge_disable_interface(bst);
return 0;
}
static int
bridge_set_up(struct bridge_state *bst)
{
struct bridge_member *bm;
int ret;
if (!bst->n_present) {
if (!bst->force_active)
return -ENOENT;
ret = bridge_enable_interface(bst);
if (ret)
return ret;
}
bst->n_failed = 0;
vlist_for_each_element(&bst->members, bm, node)
bridge_enable_member(bm);
bridge_check_retry(bst);
if (!bst->force_active && !bst->n_present) {
/* initialization of all member interfaces failed */
bridge_disable_interface(bst);
device_set_present(&bst->dev, false);
return -ENOENT;
}
bridge_reset_primary(bst);
ret = bst->set_state(&bst->dev, true);
if (ret < 0)
bridge_set_down(bst);
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, bool hotplug)
{
struct bridge_member *bm;
bm = calloc(1, sizeof(*bm) + strlen(dev->ifname) + 1);
if (!bm)
return NULL;
bm->bst = bst;
bm->dev.cb = bridge_member_cb;
bm->dev.hotplug = hotplug;
strcpy(bm->name, dev->ifname);
bm->dev.dev = dev;
vlist_add(&bst->members, &bm->node, bm->name);
// Need to look up the bridge member again as the above
// created pointer will be freed in case the bridge member
// already existed
bm = vlist_find(&bst->members, dev->ifname, bm, node);
if (hotplug && bm)
bm->node.version = -1;
return bm;
}
static void
bridge_member_update(struct vlist_tree *tree, struct vlist_node *node_new,
struct vlist_node *node_old)
{
struct bridge_member *bm;
struct device *dev;
if (node_new) {
bm = container_of(node_new, struct bridge_member, node);
if (node_old) {
free(bm);
return;
}
dev = bm->dev.dev;
bm->dev.dev = NULL;
device_add_user(&bm->dev, dev);
}
if (node_old) {
bm = container_of(node_old, struct bridge_member, node);
bridge_free_member(bm);
}
}
static void
bridge_add_member(struct bridge_state *bst, const char *name)
{
struct device *dev;
dev = device_get(name, true);
if (!dev)
return;
bridge_create_member(bst, dev, false);
}
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, true);
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;
bm = vlist_find(&bst->members, member->ifname, bm, node);
if (!bm)
return UBUS_STATUS_NOT_FOUND;
vlist_delete(&bst->members, &bm->node);
return 0;
}
static int
bridge_hotplug_prepare(struct device *dev)
{
struct bridge_state *bst;
bst = container_of(dev, struct bridge_state, dev);
bst->force_active = true;
device_set_present(&bst->dev, true);
return 0;
}
static const struct device_hotplug_ops bridge_ops = {
.prepare = bridge_hotplug_prepare,
.add = bridge_hotplug_add,
.del = bridge_hotplug_del
};
static void
bridge_free(struct device *dev)
{
struct bridge_state *bst;
bst = container_of(dev, struct bridge_state, dev);
vlist_flush_all(&bst->members);
free(bst->config_data);
free(bst);
}
static void
bridge_dump_info(struct device *dev, struct blob_buf *b)
{
struct bridge_state *bst;
struct bridge_member *bm;
void *list;
bst = container_of(dev, struct bridge_state, dev);
system_if_dump_info(dev, b);
list = blobmsg_open_array(b, "bridge-members");
vlist_for_each_element(&bst->members, bm, node)
blobmsg_add_string(b, NULL, bm->dev.dev->ifname);
blobmsg_close_array(b, list);
}
static void
bridge_config_init(struct device *dev)
{
struct bridge_state *bst;
struct blob_attr *cur;
int rem;
bst = container_of(dev, struct bridge_state, dev);
if (bst->config.bridge_empty) {
bst->force_active = true;
device_set_present(&bst->dev, true);
}
bst->n_failed = 0;
vlist_update(&bst->members);
if (bst->ifnames) {
blobmsg_for_each_attr(cur, bst->ifnames, rem) {
bridge_add_member(bst, blobmsg_data(cur));
}
}
vlist_flush(&bst->members);
bridge_check_retry(bst);
}
static void
bridge_apply_settings(struct bridge_state *bst, struct blob_attr **tb)
{
struct bridge_config *cfg = &bst->config;
struct blob_attr *cur;
/* defaults */
cfg->stp = false;
cfg->forward_delay = 2;
cfg->igmp_snoop = true;
cfg->multicast_querier = true;
cfg->robustness = 2;
cfg->query_interval = 12500;
cfg->query_response_interval = 1000;
cfg->last_member_interval = 100;
cfg->hash_max = 512;
cfg->bridge_empty = false;
cfg->priority = 0x7FFF;
if ((cur = tb[BRIDGE_ATTR_STP]))
cfg->stp = blobmsg_get_bool(cur);
if ((cur = tb[BRIDGE_ATTR_FORWARD_DELAY]))
cfg->forward_delay = blobmsg_get_u32(cur);
if ((cur = tb[BRIDGE_ATTR_PRIORITY]))
cfg->priority = blobmsg_get_u32(cur);
if ((cur = tb[BRIDGE_ATTR_IGMP_SNOOP]))
cfg->multicast_querier = cfg->igmp_snoop = blobmsg_get_bool(cur);
if ((cur = tb[BRIDGE_ATTR_MULTICAST_QUERIER]))
cfg->multicast_querier = blobmsg_get_bool(cur);
if ((cur = tb[BRIDGE_ATTR_HASH_MAX]))
cfg->hash_max = blobmsg_get_u32(cur);
if ((cur = tb[BRIDGE_ATTR_ROBUSTNESS])) {
cfg->robustness = blobmsg_get_u32(cur);
cfg->flags |= BRIDGE_OPT_ROBUSTNESS;
}
if ((cur = tb[BRIDGE_ATTR_QUERY_INTERVAL])) {
cfg->query_interval = blobmsg_get_u32(cur);
cfg->flags |= BRIDGE_OPT_QUERY_INTERVAL;
}
if ((cur = tb[BRIDGE_ATTR_QUERY_RESPONSE_INTERVAL])) {
cfg->query_response_interval = blobmsg_get_u32(cur);
cfg->flags |= BRIDGE_OPT_QUERY_RESPONSE_INTERVAL;
}
if ((cur = tb[BRIDGE_ATTR_LAST_MEMBER_INTERVAL])) {
cfg->last_member_interval = blobmsg_get_u32(cur);
cfg->flags |= BRIDGE_OPT_LAST_MEMBER_INTERVAL;
}
if ((cur = tb[BRIDGE_ATTR_AGEING_TIME])) {
cfg->ageing_time = blobmsg_get_u32(cur);
cfg->flags |= BRIDGE_OPT_AGEING_TIME;
}
if ((cur = tb[BRIDGE_ATTR_HELLO_TIME])) {
cfg->hello_time = blobmsg_get_u32(cur);
cfg->flags |= BRIDGE_OPT_HELLO_TIME;
}
if ((cur = tb[BRIDGE_ATTR_MAX_AGE])) {
cfg->max_age = blobmsg_get_u32(cur);
cfg->flags |= BRIDGE_OPT_MAX_AGE;
}
if ((cur = tb[BRIDGE_ATTR_BRIDGE_EMPTY]))
cfg->bridge_empty = blobmsg_get_bool(cur);
}
enum dev_change_type
bridge_reload(struct device *dev, struct blob_attr *attr)
{
struct blob_attr *tb_dev[__DEV_ATTR_MAX];
struct blob_attr *tb_br[__BRIDGE_ATTR_MAX];
enum dev_change_type ret = DEV_CONFIG_APPLIED;
unsigned long diff;
struct bridge_state *bst;
BUILD_BUG_ON(sizeof(diff) < __BRIDGE_ATTR_MAX / 8);
BUILD_BUG_ON(sizeof(diff) < __DEV_ATTR_MAX / 8);
bst = container_of(dev, struct bridge_state, dev);
attr = blob_memdup(attr);
blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, tb_dev,
blob_data(attr), blob_len(attr));
blobmsg_parse(bridge_attrs, __BRIDGE_ATTR_MAX, tb_br,
blob_data(attr), blob_len(attr));
bst->ifnames = tb_br[BRIDGE_ATTR_IFNAME];
device_init_settings(dev, tb_dev);
bridge_apply_settings(bst, tb_br);
if (bst->config_data) {
struct blob_attr *otb_dev[__DEV_ATTR_MAX];
struct blob_attr *otb_br[__BRIDGE_ATTR_MAX];
blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, otb_dev,
blob_data(bst->config_data), blob_len(bst->config_data));
diff = 0;
uci_blob_diff(tb_dev, otb_dev, &device_attr_list, &diff);
if (diff)
ret = DEV_CONFIG_RESTART;
blobmsg_parse(bridge_attrs, __BRIDGE_ATTR_MAX, otb_br,
blob_data(bst->config_data), blob_len(bst->config_data));
diff = 0;
uci_blob_diff(tb_br, otb_br, &bridge_attr_list, &diff);
if (diff & ~(1 << BRIDGE_ATTR_IFNAME))
ret = DEV_CONFIG_RESTART;
bridge_config_init(dev);
}
free(bst->config_data);
bst->config_data = attr;
return ret;
}
static void
bridge_retry_members(struct uloop_timeout *timeout)
{
struct bridge_state *bst = container_of(timeout, struct bridge_state, retry);
struct bridge_member *bm;
bst->n_failed = 0;
vlist_for_each_element(&bst->members, bm, node) {
if (bm->present)
continue;
if (!bm->dev.dev->present)
continue;
bm->present = true;
bst->n_present++;
bridge_enable_member(bm);
}
}
static struct device *
bridge_create(const char *name, struct blob_attr *attr)
{
struct bridge_state *bst;
struct device *dev = NULL;
bst = calloc(1, sizeof(*bst));
if (!bst)
return NULL;
dev = &bst->dev;
device_init(dev, &bridge_device_type, name);
dev->config_pending = true;
bst->retry.cb = bridge_retry_members;
bst->set_state = dev->set_state;
dev->set_state = bridge_set_state;
dev->hotplug_ops = &bridge_ops;
vlist_init(&bst->members, avl_strcmp, bridge_member_update);
bst->members.keep_old = true;
bridge_reload(dev, attr);
return dev;
}

434
src/3P/netifd/config.c Normal file
View File

@@ -0,0 +1,434 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <uci.h>
#include "netifd.h"
#include "interface.h"
#include "interface-ip.h"
#include "iprule.h"
#include "proto.h"
#include "wireless.h"
#include "config.h"
bool config_init = false;
static struct uci_context *uci_ctx;
static struct uci_package *uci_network;
static struct uci_package *uci_wireless;
static struct blob_buf b;
static int
config_section_idx(struct uci_section *s)
{
struct uci_element *e;
int idx = 0;
uci_foreach_element(&uci_wireless->sections, e) {
struct uci_section *cur = uci_to_section(e);
if (s == cur)
return idx;
if (!strcmp(cur->type, s->type))
idx++;
}
return -1;
}
static int
config_parse_bridge_interface(struct uci_section *s)
{
char *name;
name = alloca(strlen(s->e.name) + 4);
sprintf(name, "br-%s", s->e.name);
blobmsg_add_string(&b, "name", name);
uci_to_blob(&b, s, bridge_device_type.config_params);
if (!device_create(name, &bridge_device_type, b.head)) {
D(INTERFACE, "Failed to create bridge for interface '%s'\n", s->e.name);
return -EINVAL;
}
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "ifname", name);
return 0;
}
static void
config_parse_interface(struct uci_section *s, bool alias)
{
struct interface *iface;
const char *type = NULL, *disabled;
struct blob_attr *config;
bool bridge = false;
disabled = uci_lookup_option_string(uci_ctx, s, "disabled");
if (disabled && !strcmp(disabled, "1"))
return;
blob_buf_init(&b, 0);
if (!alias)
type = uci_lookup_option_string(uci_ctx, s, "type");
if (type && !strcmp(type, "bridge")) {
if (config_parse_bridge_interface(s))
return;
bridge = true;
}
uci_to_blob(&b, s, &interface_attr_list);
iface = interface_alloc(s->e.name, b.head);
if (!iface)
return;
if (iface->proto_handler && iface->proto_handler->config_params)
uci_to_blob(&b, s, iface->proto_handler->config_params);
if (!bridge && uci_to_blob(&b, s, simple_device_type.config_params))
iface->device_config = true;
config = blob_memdup(b.head);
if (!config)
goto error;
if (alias) {
if (!interface_add_alias(iface, config))
goto error_free_config;
} else {
interface_add(iface, config);
}
return;
error_free_config:
free(config);
error:
free(iface);
}
static void
config_parse_route(struct uci_section *s, bool v6)
{
void *route;
blob_buf_init(&b, 0);
route = blobmsg_open_array(&b, "route");
uci_to_blob(&b, s, &route_attr_list);
blobmsg_close_array(&b, route);
interface_ip_add_route(NULL, blob_data(b.head), v6);
}
static void
config_parse_rule(struct uci_section *s, bool v6)
{
void *rule;
blob_buf_init(&b, 0);
rule = blobmsg_open_array(&b, "rule");
uci_to_blob(&b, s, &rule_attr_list);
blobmsg_close_array(&b, rule);
iprule_add(blob_data(b.head), v6);
}
static void
config_init_devices(void)
{
struct uci_element *e;
uci_foreach_element(&uci_network->sections, e) {
const struct uci_blob_param_list *params = NULL;
struct uci_section *s = uci_to_section(e);
const struct device_type *devtype = NULL;
struct device *dev;
const char *type, *name;
if (strcmp(s->type, "device") != 0)
continue;
name = uci_lookup_option_string(uci_ctx, s, "name");
if (!name)
continue;
type = uci_lookup_option_string(uci_ctx, s, "type");
if (type) {
if (!strcmp(type, "8021ad"))
devtype = &vlandev_device_type;
else if (!strcmp(type, "8021q"))
devtype = &vlandev_device_type;
else if (!strcmp(type, "bridge"))
devtype = &bridge_device_type;
else if (!strcmp(type, "macvlan"))
devtype = &macvlan_device_type;
else if (!strcmp(type, "tunnel"))
devtype = &tunnel_device_type;
}
if (devtype)
params = devtype->config_params;
if (!params)
params = simple_device_type.config_params;
blob_buf_init(&b, 0);
uci_to_blob(&b, s, params);
if (devtype) {
dev = device_create(name, devtype, b.head);
if (!dev)
continue;
} else {
dev = device_get(name, 1);
if (!dev)
continue;
dev->current_config = true;
device_apply_config(dev, dev->type, b.head);
}
dev->default_config = false;
}
}
static struct uci_package *
config_init_package(const char *config)
{
struct uci_context *ctx = uci_ctx;
struct uci_package *p = NULL;
if (!ctx) {
ctx = uci_alloc_context();
uci_ctx = ctx;
ctx->flags &= ~UCI_FLAG_STRICT;
if (config_path)
uci_set_confdir(ctx, config_path);
#ifdef DUMMY_MODE
uci_set_savedir(ctx, "./tmp");
#endif
} else {
p = uci_lookup_package(ctx, config);
if (p)
uci_unload(ctx, p);
}
if (uci_load(ctx, config, &p))
return NULL;
return p;
}
static void
config_init_interfaces(void)
{
struct uci_element *e;
uci_foreach_element(&uci_network->sections, e) {
struct uci_section *s = uci_to_section(e);
if (!strcmp(s->type, "interface"))
config_parse_interface(s, false);
}
uci_foreach_element(&uci_network->sections, e) {
struct uci_section *s = uci_to_section(e);
if (!strcmp(s->type, "alias"))
config_parse_interface(s, true);
}
}
static void
config_init_routes(void)
{
struct interface *iface;
struct uci_element *e;
vlist_for_each_element(&interfaces, iface, node)
interface_ip_update_start(&iface->config_ip);
uci_foreach_element(&uci_network->sections, e) {
struct uci_section *s = uci_to_section(e);
if (!strcmp(s->type, "route"))
config_parse_route(s, false);
else if (!strcmp(s->type, "route6"))
config_parse_route(s, true);
}
vlist_for_each_element(&interfaces, iface, node)
interface_ip_update_complete(&iface->config_ip);
}
static void
config_init_rules(void)
{
struct uci_element *e;
iprule_update_start();
uci_foreach_element(&uci_network->sections, e) {
struct uci_section *s = uci_to_section(e);
if (!strcmp(s->type, "rule"))
config_parse_rule(s, false);
else if (!strcmp(s->type, "rule6"))
config_parse_rule(s, true);
}
iprule_update_complete();
}
static void
config_init_globals(void)
{
struct uci_section *globals = uci_lookup_section(
uci_ctx, uci_network, "globals");
if (!globals)
return;
const char *ula_prefix = uci_lookup_option_string(
uci_ctx, globals, "ula_prefix");
interface_ip_set_ula_prefix(ula_prefix);
const char *default_ps = uci_lookup_option_string(
uci_ctx, globals, "default_ps");
if (default_ps)
device_set_default_ps(strcmp(default_ps, "1") ? false : true);
}
static void
config_parse_wireless_device(struct uci_section *s)
{
struct wireless_driver *drv;
const char *driver_name;
driver_name = uci_lookup_option_string(uci_ctx, s, "type");
if (!driver_name)
return;
drv = avl_find_element(&wireless_drivers, driver_name, drv, node);
if (!drv)
return;
blob_buf_init(&b, 0);
uci_to_blob(&b, s, drv->device.config);
wireless_device_create(drv, s->e.name, b.head);
}
static void
config_parse_wireless_interface(struct wireless_device *wdev, struct uci_section *s)
{
char *name;
name = alloca(strlen(s->type) + 16);
sprintf(name, "@%s[%d]", s->type, config_section_idx(s));
blob_buf_init(&b, 0);
uci_to_blob(&b, s, wdev->drv->interface.config);
wireless_interface_create(wdev, b.head, s->anonymous ? name : s->e.name);
}
static void
config_init_wireless(void)
{
struct wireless_device *wdev;
struct uci_element *e;
const char *dev_name;
if (!uci_wireless) {
DPRINTF("No wireless configuration found\n");
return;
}
vlist_update(&wireless_devices);
uci_foreach_element(&uci_wireless->sections, e) {
struct uci_section *s = uci_to_section(e);
if (strcmp(s->type, "wifi-device") != 0)
continue;
config_parse_wireless_device(s);
}
vlist_flush(&wireless_devices);
vlist_for_each_element(&wireless_devices, wdev, node) {
wdev->vif_idx = 0;
vlist_update(&wdev->interfaces);
}
uci_foreach_element(&uci_wireless->sections, e) {
struct uci_section *s = uci_to_section(e);
if (strcmp(s->type, "wifi-iface") != 0)
continue;
dev_name = uci_lookup_option_string(uci_ctx, s, "device");
if (!dev_name)
continue;
wdev = vlist_find(&wireless_devices, dev_name, wdev, node);
if (!wdev) {
DPRINTF("device %s not found!\n", dev_name);
continue;
}
config_parse_wireless_interface(wdev, s);
}
vlist_for_each_element(&wireless_devices, wdev, node)
vlist_flush(&wdev->interfaces);
}
void
config_init_all(void)
{
uci_network = config_init_package("network");
if (!uci_network) {
fprintf(stderr, "Failed to load network config\n");
return;
}
uci_wireless = config_init_package("wireless");
vlist_update(&interfaces);
config_init = true;
device_lock();
device_reset_config();
config_init_devices();
config_init_interfaces();
config_init_routes();
config_init_rules();
config_init_globals();
config_init_wireless();
config_init = false;
device_unlock();
device_reset_old();
device_init_pending();
vlist_flush(&interfaces);
device_free_unused(NULL);
interface_refresh_assignments(false);
interface_start_pending();
wireless_start_pending();
}

24
src/3P/netifd/config.h Normal file
View File

@@ -0,0 +1,24 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __NETIFD_CONFIG_H
#define __NETIFD_CONFIG_H
#include <libubox/blobmsg.h>
#include <uci_blob.h>
extern bool config_init;
void config_init_all(void);
#endif

View File

@@ -0,0 +1,74 @@
# 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 device
option name br-lan
option type bridge
list ifname eth0.1
list ifname eth0.2
option mtu 1500
config device
option name dummy
config interface lan
option ifname 'br-lan'
option proto static
option ipaddr 192.168.1.1
option netmask 255.255.255.0
config interface dummy
option ifname eth0.4
option mtu 1500
option proto none
config interface lan2
option ifname "eth0.3 eth0.5"
option type bridge
option proto static
option ipaddr 192.168.1.1
option netmask 255.255.255.0
option gateway 192.168.1.2
option dns '192.168.1.5 192.168.1.6'
config interface wan
option proto pppoe
option ifname br-lan2
option username foo
option password bar
config alias wan1
option proto static
option interface wan
option ipaddr 192.168.99.1
option ip6addr 2a01:4f8:131:30e2::2/59
option netmask 255.255.255.0
option gateway 192.168.99.3
config route6
option interface wan1
option target ::0/0
option gateway 2a01:4f8:131:30e2::1
config interface wlan
option proto static
option ipaddr 192.168.2.1
option netmask 255.255.255.0
config interface pptp
option proto pptp
option server 1.1.1.1
option peerdns 0
option dns 192.168.10.1
config route
option target 192.168.0.1
option netmask 24
option gateway 192.168.5.2
option interface wan

View File

@@ -0,0 +1,20 @@
config wifi-device radio0
option type mac80211
option channel 11
option hwmode 11ng
option path 'platform/ar933x_wmac'
option htmode HT20
list ht_capab SHORT-GI-20
list ht_capab SHORT-GI-40
list ht_capab RX-STBC1
list ht_capab DSSS_CCK-40
# REMOVE THIS LINE TO ENABLE WIFI:
# option disabled 1
config wifi-iface
option device radio0
option network lan
option mode ap
option ssid OpenWrt
option encryption none

1011
src/3P/netifd/device.c Normal file

File diff suppressed because it is too large Load Diff

282
src/3P/netifd/device.h Normal file
View File

@@ -0,0 +1,282 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __NETIFD_DEVICE_H
#define __NETIFD_DEVICE_H
#include <libubox/avl.h>
#include <libubox/safe_list.h>
#include <netinet/in.h>
struct device;
struct device_user;
struct device_hotplug_ops;
struct interface;
typedef int (*device_state_cb)(struct device *, bool up);
enum {
DEV_ATTR_TYPE,
DEV_ATTR_MTU,
DEV_ATTR_MTU6,
DEV_ATTR_MACADDR,
DEV_ATTR_TXQUEUELEN,
DEV_ATTR_ENABLED,
DEV_ATTR_IPV6,
DEV_ATTR_PROMISC,
DEV_ATTR_RPFILTER,
DEV_ATTR_ACCEPTLOCAL,
DEV_ATTR_IGMPVERSION,
DEV_ATTR_MLDVERSION,
DEV_ATTR_NEIGHREACHABLETIME,
DEV_ATTR_RPS,
DEV_ATTR_XPS,
DEV_ATTR_DADTRANSMITS,
DEV_ATTR_MULTICAST_TO_UNICAST,
DEV_ATTR_MULTICAST_ROUTER,
DEV_ATTR_MULTICAST,
DEV_ATTR_LEARNING,
DEV_ATTR_UNICAST_FLOOD,
DEV_ATTR_NEIGHGCSTALETIME,
__DEV_ATTR_MAX,
};
enum dev_change_type {
DEV_CONFIG_NO_CHANGE,
DEV_CONFIG_APPLIED,
DEV_CONFIG_RESTART,
DEV_CONFIG_RECREATE,
};
struct device_type {
struct list_head list;
const char *name;
const struct uci_blob_param_list *config_params;
struct device *(*create)(const char *name, struct blob_attr *attr);
void (*config_init)(struct device *);
enum dev_change_type (*reload)(struct device *, struct blob_attr *);
void (*dump_info)(struct device *, struct blob_buf *buf);
void (*dump_stats)(struct device *, struct blob_buf *buf);
int (*check_state)(struct device *);
void (*free)(struct device *);
};
enum {
DEV_OPT_MTU = (1 << 0),
DEV_OPT_MACADDR = (1 << 1),
DEV_OPT_TXQUEUELEN = (1 << 2),
DEV_OPT_IPV6 = (1 << 3),
DEV_OPT_PROMISC = (1 << 4),
DEV_OPT_RPFILTER = (1 << 5),
DEV_OPT_ACCEPTLOCAL = (1 << 6),
DEV_OPT_IGMPVERSION = (1 << 7),
DEV_OPT_MLDVERSION = (1 << 8),
DEV_OPT_NEIGHREACHABLETIME = (1 << 9),
DEV_OPT_RPS = (1 << 10),
DEV_OPT_XPS = (1 << 11),
DEV_OPT_MTU6 = (1 << 12),
DEV_OPT_DADTRANSMITS = (1 << 13),
DEV_OPT_MULTICAST_TO_UNICAST = (1 << 14),
DEV_OPT_MULTICAST_ROUTER = (1 << 15),
DEV_OPT_MULTICAST = (1 << 16),
DEV_OPT_LEARNING = (1 << 17),
DEV_OPT_UNICAST_FLOOD = (1 << 18),
DEV_OPT_NEIGHGCSTALETIME = (1 << 19),
};
/* events broadcasted to all users of a device */
enum device_event {
DEV_EVENT_ADD,
DEV_EVENT_REMOVE,
DEV_EVENT_UPDATE_IFNAME,
DEV_EVENT_UPDATE_IFINDEX,
DEV_EVENT_SETUP,
DEV_EVENT_TEARDOWN,
DEV_EVENT_UP,
DEV_EVENT_DOWN,
DEV_EVENT_LINK_UP,
DEV_EVENT_LINK_DOWN,
/* Topology changed (i.e. bridge member added) */
DEV_EVENT_TOPO_CHANGE,
__DEV_EVENT_MAX
};
/*
* device dependency with callbacks
*/
struct device_user {
struct safe_list list;
bool claimed;
bool hotplug;
bool alias;
uint8_t ev_idx[__DEV_EVENT_MAX];
struct device *dev;
void (*cb)(struct device_user *, enum device_event);
};
struct device_settings {
unsigned int flags;
unsigned int valid_flags;
unsigned int mtu;
unsigned int mtu6;
unsigned int txqueuelen;
uint8_t macaddr[6];
bool ipv6;
bool promisc;
unsigned int rpfilter;
bool acceptlocal;
unsigned int igmpversion;
unsigned int mldversion;
unsigned int neigh4reachabletime;
unsigned int neigh6reachabletime;
unsigned int neigh4gcstaletime;
unsigned int neigh6gcstaletime;
bool rps;
bool xps;
unsigned int dadtransmits;
bool multicast_to_unicast;
unsigned int multicast_router;
bool multicast;
bool learning;
bool unicast_flood;
};
/*
* 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 safe_list users;
struct safe_list aliases;
char ifname[IFNAMSIZ + 1];
int ifindex;
struct blob_attr *config;
bool config_pending;
bool sys_present;
/* DEV_EVENT_ADD */
bool present;
/* DEV_EVENT_UP */
int active;
/* DEV_EVENT_LINK_UP */
bool link_active;
bool external;
bool disabled;
bool deferred;
bool hidden;
bool current_config;
bool iface_config;
bool default_config;
bool wireless;
bool wireless_ap;
bool wireless_isolate;
struct interface *config_iface;
/* set interface up or down */
device_state_cb set_state;
const struct device_hotplug_ops *hotplug_ops;
struct device_user parent;
struct device_settings orig_settings;
struct device_settings settings;
};
struct device_hotplug_ops {
int (*prepare)(struct device *dev);
int (*add)(struct device *main, struct device *member);
int (*del)(struct device *main, struct device *member);
};
extern const struct uci_blob_param_list device_attr_list;
extern const struct device_type simple_device_type;
extern const struct device_type bridge_device_type;
extern const struct device_type tunnel_device_type;
extern const struct device_type macvlan_device_type;
extern const struct device_type vlandev_device_type;
void device_lock(void);
void device_unlock(void);
struct device *device_create(const char *name, const struct device_type *type,
struct blob_attr *config);
void device_init_settings(struct device *dev, struct blob_attr **tb);
void device_init_pending(void);
enum dev_change_type
device_apply_config(struct device *dev, const struct device_type *type,
struct blob_attr *config);
void device_reset_config(void);
void device_reset_old(void);
void device_set_default_ps(bool state);
void device_init_virtual(struct device *dev, const struct device_type *type, const char *name);
int device_init(struct device *iface, const struct device_type *type, const char *ifname);
void device_cleanup(struct device *dev);
struct device *device_find(const char *name);
struct device *device_get(const char *name, int create);
void device_add_user(struct device_user *dep, struct device *iface);
void device_remove_user(struct device_user *dep);
void device_broadcast_event(struct device *dev, enum device_event ev);
void device_set_present(struct device *dev, bool state);
void device_set_link(struct device *dev, bool state);
void device_set_ifindex(struct device *dev, int ifindex);
int device_set_ifname(struct device *dev, const char *name);
void device_refresh_present(struct device *dev);
int device_claim(struct device_user *dep);
void device_release(struct device_user *dep);
int device_check_state(struct device *dev);
void device_dump_status(struct blob_buf *b, struct device *dev);
void device_free(struct device *dev);
void device_free_unused(struct device *dev);
struct device *get_vlan_device_chain(const char *ifname, bool create);
void alias_notify_device(const char *name, struct device *dev);
struct device *device_alias_get(const char *name);
static inline void
device_set_deferred(struct device *dev, bool value)
{
dev->deferred = value;
device_refresh_present(dev);
}
static inline void
device_set_disabled(struct device *dev, bool value)
{
dev->disabled = value;
device_refresh_present(dev);
}
#endif

View File

@@ -0,0 +1 @@
echo "Action: $ACTION, Interface: $INTERFACE"

View File

@@ -0,0 +1,71 @@
#!/bin/sh
NETIFD_MAIN_DIR=../../scripts
. $NETIFD_MAIN_DIR/netifd-proto.sh
init_proto "$@"
ppp_generic_init_config() {
proto_config_add_string "username"
proto_config_add_string "password"
proto_config_add_int "keepalive"
}
proto_ppp_init_config() {
no_device=1
available=1
ppp_generic_init_config
}
proto_ppp_setup() {
echo "ppp_setup($1): $2"
}
proto_ppp_teardown() {
return
}
add_protocol ppp
proto_pppoe_init_config() {
ppp_generic_init_config
}
proto_pppoe_setup() {
local interface="$1"
local device="$2"
json_get_var username username
json_get_var password password
echo "pppoe_setup($interface, $device), username=$username, password=$password"
proto_init_update pppoe-$interface 1
proto_set_keep 1
proto_add_ipv4_address "192.168.2.1" 32
proto_add_dns_server "192.168.2.2"
proto_add_ipv4_route "0.0.0.0" 0 192.168.2.2
proto_add_data
json_add_string "ppp-type" "pppoe"
proto_close_data
proto_send_update "$interface"
proto_init_update pppoe-$interface 1
proto_set_keep 1
proto_add_ipv6_address "fe80::2" 64
proto_add_ipv6_route "::0" 0 "fe80::1"
proto_add_data
json_add_string "ppp-type" "pppoe"
proto_close_data
proto_send_update "$interface"
proto_run_command "$interface" sleep 30
}
proto_pppoe_teardown() {
[ "$ERROR" = 9 ] && {
proto_notify_error "$interface" PROCESS_KILLED
proto_block_restart "$interface"
}
proto_kill_command "$interface"
return
}
add_protocol pppoe

View File

@@ -0,0 +1,44 @@
#!/bin/sh
NETIFD_MAIN_DIR=../../scripts
. $NETIFD_MAIN_DIR/netifd-proto.sh
init_proto "$@"
proto_pptp_init_config() {
no_device=1
available=1
proto_config_add_string "username"
proto_config_add_string "password"
proto_config_add_string server
}
proto_pptp_setup() {
local interface="$1"
local device="$2"
json_get_var server server
proto_add_host_dependency "$interface" "$server"
json_get_var username username
json_get_var password password
echo "pptp_setup($interface), username=$username, password=$password"
proto_init_update "pptp-$interface" 1
proto_set_keep 1
proto_add_ipv4_address "192.168.9.1" 32
proto_add_dns_server "192.168.9.2"
proto_add_ipv4_route "0.0.0.0" 0 192.168.9.2
proto_add_data
json_add_string "ppp-type" "pptp"
proto_close_data
proto_send_update "$interface"
proto_run_command "$interface" sleep 30
}
proto_pptp_teardown() {
return
}
add_protocol pptp

View File

@@ -0,0 +1,66 @@
#!/bin/sh
NETIFD_MAIN_DIR=../../scripts
. $NETIFD_MAIN_DIR/netifd-wireless.sh
init_wireless_driver "$@"
drv_mac80211_init_device_config() {
# identifiers
config_add_string macaddr
config_add_string path
config_add_string phy
# config
config_add_int channel
config_add_string hwmode
config_add_array ht_capab
config_add_int chanbw
}
drv_mac80211_init_iface_config() {
config_add_string macaddr
config_add_boolean wds
config_add_int maxassoc
config_add_int dtim_period
config_add_int max_listen_int
config_add_boolean hidden
config_add_boolean wmm
}
setup_vif() {
local name="$1"
json_select config
json_get_var ssid ssid
json_select ..
wireless_add_vif "$name" "dummy-$ssid"
/bin/sleep 10 &
wireless_add_process "$!" /bin/sleep 1
}
drv_mac80211_cleanup() {
echo "mac80211 cleanup"
}
drv_mac80211_setup() {
echo "mac80211 setup: $1"
json_dump
for_each_interface "sta ap adhoc" setup_vif
wireless_set_data phy=phy0
wireless_set_up
}
drv_mac80211_teardown() {
json_select data
json_get_var phy phy
json_select ..
echo "mac80211 teardown: $1 ($phy)"
json_dump
}
add_driver mac80211

212
src/3P/netifd/handler.c Normal file
View File

@@ -0,0 +1,212 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012-2013 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define _GNU_SOURCE
#include <glob.h>
#include <fcntl.h>
#include <stdio.h>
#include "netifd.h"
#include "system.h"
#include "handler.h"
static int
netifd_dir_push(int fd)
{
int prev_fd = open(".", O_RDONLY | O_DIRECTORY);
system_fd_set_cloexec(prev_fd);
if (fd >= 0)
if (fchdir(fd)) {}
return prev_fd;
}
static void
netifd_dir_pop(int prev_fd)
{
if (fchdir(prev_fd)) {}
close(prev_fd);
}
int netifd_open_subdir(const char *name)
{
int prev_dir;
int ret = -1;
prev_dir = netifd_dir_push(-1);
if (chdir(main_path)) {
perror("chdir(main path)");
goto out;
}
ret = open(name, O_RDONLY | O_DIRECTORY);
if (ret >= 0)
system_fd_set_cloexec(ret);
out:
netifd_dir_pop(prev_dir);
return ret;
}
static void
netifd_init_script_handler(const char *script, json_object *obj, script_dump_cb cb)
{
json_object *tmp;
const char *name;
if (!json_check_type(obj, json_type_object))
return;
tmp = json_get_field(obj, "name", json_type_string);
if (!tmp)
return;
name = json_object_get_string(tmp);
cb(script, name, obj);
}
static void
netifd_parse_script_handler(const char *name, script_dump_cb cb)
{
struct json_tokener *tok = NULL;
json_object *obj;
static char buf[512];
char *start, *cmd;
FILE *f;
int len;
#define DUMP_SUFFIX " '' dump"
cmd = alloca(strlen(name) + 1 + sizeof(DUMP_SUFFIX));
sprintf(cmd, "%s" DUMP_SUFFIX, name);
f = popen(cmd, "r");
if (!f)
return;
do {
start = fgets(buf, sizeof(buf), f);
if (!start)
continue;
len = strlen(start);
if (!tok)
tok = json_tokener_new();
obj = json_tokener_parse_ex(tok, start, len);
if (!is_error(obj)) {
netifd_init_script_handler(name, obj, cb);
json_object_put(obj);
json_tokener_free(tok);
tok = NULL;
} else if (start[len - 1] == '\n') {
json_tokener_free(tok);
tok = NULL;
}
} while (!feof(f) && !ferror(f));
if (tok)
json_tokener_free(tok);
pclose(f);
}
void netifd_init_script_handlers(int dir_fd, script_dump_cb cb)
{
glob_t g;
int i, prev_fd;
prev_fd = netifd_dir_push(dir_fd);
if (glob("./*.sh", 0, NULL, &g))
return;
for (i = 0; i < g.gl_pathc; i++)
netifd_parse_script_handler(g.gl_pathv[i], cb);
netifd_dir_pop(prev_fd);
globfree(&g);
}
char *
netifd_handler_parse_config(struct uci_blob_param_list *config, json_object *obj)
{
struct blobmsg_policy *attrs;
char *str_buf, *str_cur;
char const **validate;
int str_len = 0;
int i;
config->n_params = json_object_array_length(obj);
attrs = calloc(1, sizeof(*attrs) * config->n_params);
if (!attrs)
return NULL;
validate = calloc(1, sizeof(char*) * config->n_params);
if (!validate)
goto error;
config->params = attrs;
config->validate = validate;
for (i = 0; i < config->n_params; i++) {
json_object *cur, *name, *type;
cur = json_check_type(json_object_array_get_idx(obj, i), json_type_array);
if (!cur)
goto error;
name = json_check_type(json_object_array_get_idx(cur, 0), json_type_string);
if (!name)
goto error;
type = json_check_type(json_object_array_get_idx(cur, 1), json_type_int);
if (!type)
goto error;
attrs[i].name = json_object_get_string(name);
attrs[i].type = json_object_get_int(type);
if (attrs[i].type > BLOBMSG_TYPE_LAST)
goto error;
str_len += strlen(attrs[i].name) + 1;
}
str_buf = malloc(str_len);
if (!str_buf)
goto error;
str_cur = str_buf;
for (i = 0; i < config->n_params; i++) {
const char *name = attrs[i].name;
char *delim;
attrs[i].name = str_cur;
str_cur += sprintf(str_cur, "%s", name) + 1;
delim = strchr(attrs[i].name, ':');
if (delim) {
*delim = '\0';
validate[i] = ++delim;
} else {
validate[i] = NULL;
}
}
return str_buf;
error:
free(attrs);
if (validate)
free(validate);
config->n_params = 0;
return NULL;
}

46
src/3P/netifd/handler.h Normal file
View File

@@ -0,0 +1,46 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012-2013 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __NETIFD_HANDLER_H
#define __NETIFD_HANDLER_H
#include <libubox/blobmsg_json.h>
#include <json-c/json.h>
#include "config.h"
typedef void (*script_dump_cb)(const char *script, const char *name, json_object *obj);
static inline json_object *
json_check_type(json_object *obj, json_type type)
{
if (!obj)
return NULL;
if (json_object_get_type(obj) != type)
return NULL;
return obj;
}
static inline json_object *
json_get_field(json_object *obj, const char *name, json_type type)
{
return json_object_object_get_ex(obj, name, &obj) ?
json_check_type(obj, type) : NULL;
}
int netifd_open_subdir(const char *name);
void netifd_init_script_handlers(int dir_fd, script_dump_cb cb);
char *netifd_handler_parse_config(struct uci_blob_param_list *config, json_object *obj);
#endif

View File

@@ -0,0 +1,200 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libubox/uloop.h>
#include "netifd.h"
#include "interface.h"
#include "ubus.h"
char *hotplug_cmd_path = DEFAULT_HOTPLUG_PATH;
static struct interface *current;
static enum interface_event current_ev;
static struct list_head pending = LIST_HEAD_INIT(pending);
static void task_complete(struct uloop_process *proc, int ret);
static struct uloop_process task = {
.cb = task_complete,
};
static const char * const eventnames[] = {"ifdown", "ifup", "ifupdate", "free", "reload"};
static void
run_cmd(const char *ifname, const char *device, enum interface_event event,
enum interface_update_flags updated)
{
char *argv[3];
int pid;
pid = fork();
if (pid < 0)
return task_complete(NULL, -1);
if (pid > 0) {
task.pid = pid;
uloop_process_add(&task);
return;
}
setenv("ACTION", eventnames[event], 1);
setenv("INTERFACE", ifname, 1);
if (device)
setenv("DEVICE", device, 1);
if (event == IFEV_UPDATE) {
if (updated & IUF_ADDRESS)
setenv("IFUPDATE_ADDRESSES", "1", 1);
if (updated & IUF_ROUTE)
setenv("IFUPDATE_ROUTES", "1", 1);
if (updated & IUF_PREFIX)
setenv("IFUPDATE_PREFIXES", "1", 1);
if (updated & IUF_DATA)
setenv("IFUPDATE_DATA", "1", 1);
}
argv[0] = hotplug_cmd_path;
argv[1] = "iface";
argv[2] = NULL;
execvp(argv[0], argv);
exit(127);
}
static void
call_hotplug(void)
{
const char *device = NULL;
if (list_empty(&pending))
return;
current = list_first_entry(&pending, struct interface, hotplug_list);
current_ev = current->hotplug_ev;
list_del_init(&current->hotplug_list);
if ((current_ev == IFEV_UP || current_ev == IFEV_UPDATE) && current->l3_dev.dev)
device = current->l3_dev.dev->ifname;
D(SYSTEM, "Call hotplug handler for interface '%s', event '%s' (%s)\n",
current->name, eventnames[current_ev], device ? device : "none");
run_cmd(current->name, device, current_ev, current->updated);
}
static void
task_complete(struct uloop_process *proc, int ret)
{
if (current)
D(SYSTEM, "Complete hotplug handler for interface '%s'\n", current->name);
current = NULL;
call_hotplug();
}
/*
* Queue an interface for an up/down event.
* An interface can only have one event in the queue and one
* event waiting for completion.
* When queueing an event that is the same as the one waiting for
* completion, remove the interface from the queue
*/
static void
interface_queue_event(struct interface *iface, enum interface_event ev)
{
D(SYSTEM, "Queue hotplug handler for interface '%s', event '%s'\n",
iface->name, eventnames[ev]);
if (ev == IFEV_UP || ev == IFEV_DOWN)
netifd_ubus_interface_event(iface, ev == IFEV_UP);
netifd_ubus_interface_notify(iface, ev != IFEV_DOWN);
if (current == iface) {
/* an event for iface is being processed */
if (!list_empty(&iface->hotplug_list)) {
/* an additional event for iface is pending */
/* overwrite pending event if it differs from */
/* an update */
if (ev != IFEV_UPDATE)
iface->hotplug_ev = ev;
}
else {
/* no additional event for iface is pending */
if (ev != current_ev || ev == IFEV_UPDATE) {
/* only add the interface to the pending list if
* the event is different from the one being
* handled or if it is an update */
iface->hotplug_ev = ev;
/* Handle hotplug calls FIFO */
list_add_tail(&iface->hotplug_list, &pending);
}
}
}
else {
/* currently not handling an event or handling an event
* for another interface */
if (!list_empty(&iface->hotplug_list)) {
/* an event for iface is pending */
if (!(iface->hotplug_ev == IFEV_UP &&
ev == IFEV_UPDATE)) {
/* overwrite pending event, unless the incoming
* event is an ifupdate while the pending one
* is an ifup */
iface->hotplug_ev = ev;
}
}
else {
/* an event for the interface is not yet pending,
* queue it */
iface->hotplug_ev = ev;
/* Handle hotplug calls FIFO */
list_add_tail(&iface->hotplug_list, &pending);
}
}
if (!task.pending && !current)
call_hotplug();
}
static void
interface_dequeue_event(struct interface *iface)
{
if (iface == current)
current = NULL;
if (!list_empty(&iface->hotplug_list))
list_del_init(&iface->hotplug_list);
}
static void interface_event_cb(struct interface_user *dep, struct interface *iface,
enum interface_event ev)
{
switch (ev) {
case IFEV_UP:
case IFEV_UPDATE:
case IFEV_DOWN:
interface_queue_event(iface, ev);
break;
case IFEV_FREE:
case IFEV_RELOAD:
interface_dequeue_event(iface);
break;
}
}
static struct interface_user event_user = {
.cb = interface_event_cb
};
static void __init interface_event_init(void)
{
interface_add_user(&event_user, NULL);
}

1422
src/3P/netifd/interface-ip.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,175 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __INTERFACE_IP_H
#define __INTERFACE_IP_H
#include "interface.h"
enum device_addr_flags {
/* address family for routes and addresses */
DEVADDR_INET4 = (0 << 0),
DEVADDR_INET6 = (1 << 0),
DEVADDR_FAMILY = DEVADDR_INET4 | DEVADDR_INET6,
/* externally added address */
DEVADDR_EXTERNAL = (1 << 2),
/* route overrides the default interface metric */
DEVROUTE_METRIC = (1 << 3),
/* route overrides the default interface mtu */
DEVROUTE_MTU = (1 << 4),
/* route automatically added by kernel */
DEVADDR_KERNEL = (1 << 5),
/* address is off-link (no subnet-route) */
DEVADDR_OFFLINK = (1 << 6),
/* route resides in different table */
DEVROUTE_TABLE = (1 << 7),
/* route resides in default source-route table */
DEVROUTE_SRCTABLE = (1 << 8),
/* route is on-link */
DEVROUTE_ONLINK = (1 << 9),
/* route overrides the default route type */
DEVROUTE_TYPE = (1 << 10),
};
union if_addr {
struct in_addr in;
struct in6_addr in6;
};
struct device_prefix_assignment {
struct list_head head;
int32_t assigned;
uint8_t length;
struct in6_addr addr;
bool enabled;
char name[];
};
struct device_prefix {
struct vlist_node node;
struct list_head head;
struct list_head assignments;
struct interface *iface;
time_t valid_until;
time_t preferred_until;
struct in6_addr excl_addr;
uint8_t excl_length;
struct in6_addr addr;
uint8_t length;
char pclass[];
};
struct device_route {
struct vlist_node node;
struct interface *iface;
bool enabled;
bool keep;
bool failed;
union if_addr nexthop;
int mtu;
unsigned int type;
time_t valid_until;
/* must be last */
enum device_addr_flags flags;
int metric; // there can be multiple routes to the same target
unsigned int table;
unsigned int mask;
unsigned int sourcemask;
union if_addr addr;
union if_addr source;
};
struct device_addr {
struct vlist_node node;
bool enabled;
bool failed;
unsigned int policy_table;
struct device_route subnet;
/* ipv4 only */
uint32_t broadcast;
uint32_t point_to_point;
/* ipv6 only */
time_t valid_until;
time_t preferred_until;
char *pclass;
/* must be last */
enum device_addr_flags flags;
unsigned int mask;
union if_addr addr;
};
struct device_source_table {
struct list_head head;
uint32_t table;
uint16_t refcount;
uint8_t v6;
uint8_t mask;
union if_addr addr;
};
struct dns_server {
struct vlist_simple_node node;
int af;
union if_addr addr;
};
struct dns_search_domain {
struct vlist_simple_node node;
char name[];
};
extern const struct uci_blob_param_list route_attr_list;
extern struct list_head prefixes;
void interface_ip_init(struct interface *iface);
void interface_add_dns_server(struct interface_ip_settings *ip, const char *str);
void interface_add_dns_server_list(struct interface_ip_settings *ip, struct blob_attr *list);
void interface_add_dns_search_list(struct interface_ip_settings *ip, struct blob_attr *list);
void interface_write_resolv_conf(void);
void interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6);
void interface_ip_update_start(struct interface_ip_settings *ip);
void interface_ip_update_complete(struct interface_ip_settings *ip);
void interface_ip_flush(struct interface_ip_settings *ip);
void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled);
void interface_ip_update_metric(struct interface_ip_settings *ip, int metric);
struct interface *interface_ip_add_target_route(union if_addr *addr, bool v6, struct interface *iface);
struct device_prefix* interface_ip_add_device_prefix(struct interface *iface,
struct in6_addr *addr, uint8_t length, time_t valid_until, time_t preferred_until,
struct in6_addr *excl_addr, uint8_t excl_length, const char *pclass);
void interface_ip_set_ula_prefix(const char *prefix);
void interface_refresh_assignments(bool hint);
#endif

1247
src/3P/netifd/interface.c Normal file

File diff suppressed because it is too large Load Diff

204
src/3P/netifd/interface.h Normal file
View File

@@ -0,0 +1,204 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __NETIFD_INTERFACE_H
#define __NETIFD_INTERFACE_H
#include "device.h"
#include "config.h"
struct interface;
struct interface_proto_state;
enum interface_event {
IFEV_DOWN,
IFEV_UP,
IFEV_UPDATE,
IFEV_FREE,
IFEV_RELOAD,
};
enum interface_state {
IFS_SETUP,
IFS_UP,
IFS_TEARDOWN,
IFS_DOWN,
};
enum interface_config_state {
IFC_NORMAL,
IFC_RELOAD,
IFC_REMOVE
};
enum interface_id_selection_type {
IFID_FIXED,
IFID_RANDOM,
IFID_EUI64
};
enum interface_update_flags {
IUF_ADDRESS = (1 << 0),
IUF_ROUTE = (1 << 1),
IUF_PREFIX = (1 << 2),
IUF_DATA = (1 << 3),
};
struct interface_error {
struct list_head list;
const char *subsystem;
const char *code;
const char *data[];
};
struct interface_user {
struct list_head list;
struct interface *iface;
void (*cb)(struct interface_user *dep, struct interface *iface, enum interface_event ev);
};
struct interface_ip_settings {
struct interface *iface;
bool enabled;
bool no_defaultroute;
bool no_dns;
bool no_delegation;
struct vlist_tree addr;
struct vlist_tree route;
struct vlist_tree prefix;
struct vlist_simple_tree dns_servers;
struct vlist_simple_tree dns_search;
};
struct interface_data {
struct avl_node node;
struct blob_attr data[];
};
struct interface_assignment_class {
struct list_head head;
char name[];
};
/*
* interface configuration
*/
struct interface {
struct vlist_node node;
struct list_head hotplug_list;
enum interface_event hotplug_ev;
const char *name;
const char *ifname;
bool available;
bool autostart;
bool config_autostart;
bool device_config;
bool enabled;
bool link_state;
bool force_link;
bool dynamic;
bool policy_rules_set;
time_t start_time;
enum interface_state state;
enum interface_config_state config_state;
enum interface_update_flags updated;
struct list_head users;
/* for alias interface */
const char *parent_ifname;
struct interface_user parent_iface;
/* main interface that the interface is bound to */
struct device_user main_dev;
struct device_user ext_dev;
/* interface that layer 3 communication will go through */
struct device_user l3_dev;
struct blob_attr *config;
/* primary protocol state */
const struct proto_handler *proto_handler;
struct interface_proto_state *proto;
struct interface_ip_settings proto_ip;
struct interface_ip_settings config_ip;
struct vlist_tree host_routes;
int metric;
unsigned int ip4table;
unsigned int ip6table;
/* IPv6 assignment parameters */
enum interface_id_selection_type assignment_iface_id_selection;
struct in6_addr assignment_fixed_iface_id;
uint8_t assignment_length;
int32_t assignment_hint;
struct list_head assignment_classes;
/* errors/warnings while trying to bring up the interface */
struct list_head errors;
/* extra data provided by protocol handlers or modules */
struct avl_tree data;
struct uloop_timeout remove_timer;
struct ubus_object ubus;
};
extern struct vlist_tree interfaces;
extern const struct uci_blob_param_list interface_attr_list;
struct interface *interface_alloc(const char *name, struct blob_attr *config);
void interface_set_dynamic(struct interface *iface);
void interface_add(struct interface *iface, struct blob_attr *config);
bool interface_add_alias(struct interface *iface, struct blob_attr *config);
void interface_set_proto_state(struct interface *iface, struct interface_proto_state *state);
void interface_set_available(struct interface *iface, bool new_state);
int interface_set_up(struct interface *iface);
int interface_set_down(struct interface *iface);
void __interface_set_down(struct interface *iface, bool force);
void interface_set_main_dev(struct interface *iface, struct device *dev);
void interface_set_l3_dev(struct interface *iface, struct device *dev);
void interface_add_user(struct interface_user *dep, struct interface *iface);
void interface_remove_user(struct interface_user *dep);
int interface_remove_link(struct interface *iface, struct device *dev);
int interface_handle_link(struct interface *iface, const char *name, bool add, bool link_ext);
void interface_add_error(struct interface *iface, const char *subsystem,
const char *code, const char **data, int n_data);
int interface_add_data(struct interface *iface, const struct blob_attr *data);
int interface_parse_data(struct interface *iface, const struct blob_attr *attr);
void interface_update_start(struct interface *iface);
void interface_update_complete(struct interface *iface);
void interface_start_pending(void);
#endif

256
src/3P/netifd/iprule.c Normal file
View File

@@ -0,0 +1,256 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include "netifd.h"
#include "device.h"
#include "interface.h"
#include "iprule.h"
#include "proto.h"
#include "ubus.h"
#include "system.h"
struct vlist_tree iprules;
static bool iprules_flushed = false;
static unsigned int iprules_counter[2];
enum {
RULE_INTERFACE_IN,
RULE_INTERFACE_OUT,
RULE_INVERT,
RULE_SRC,
RULE_DEST,
RULE_PRIORITY,
RULE_TOS,
RULE_FWMARK,
RULE_LOOKUP,
RULE_ACTION,
RULE_GOTO,
__RULE_MAX
};
static const struct blobmsg_policy rule_attr[__RULE_MAX] = {
[RULE_INTERFACE_IN] = { .name = "in", .type = BLOBMSG_TYPE_STRING },
[RULE_INTERFACE_OUT] = { .name = "out", .type = BLOBMSG_TYPE_STRING },
[RULE_INVERT] = { .name = "invert", .type = BLOBMSG_TYPE_BOOL },
[RULE_SRC] = { .name = "src", .type = BLOBMSG_TYPE_STRING },
[RULE_DEST] = { .name = "dest", .type = BLOBMSG_TYPE_STRING },
[RULE_PRIORITY] = { .name = "priority", .type = BLOBMSG_TYPE_INT32 },
[RULE_TOS] = { .name = "tos", .type = BLOBMSG_TYPE_INT32 },
[RULE_FWMARK] = { .name = "mark", .type = BLOBMSG_TYPE_STRING },
[RULE_LOOKUP] = { .name = "lookup", .type = BLOBMSG_TYPE_STRING },
[RULE_ACTION] = { .name = "action", .type = BLOBMSG_TYPE_STRING },
[RULE_GOTO] = { .name = "goto", .type = BLOBMSG_TYPE_INT32 },
};
const struct uci_blob_param_list rule_attr_list = {
.n_params = __RULE_MAX,
.params = rule_attr,
};
static bool
iprule_parse_mark(const char *mark, struct iprule *rule)
{
char *s, *e;
unsigned int n;
if ((s = strchr(mark, '/')) != NULL)
*s++ = 0;
n = strtoul(mark, &e, 0);
if (e == mark || *e)
return false;
rule->fwmark = n;
rule->flags |= IPRULE_FWMARK;
if (s) {
n = strtoul(s, &e, 0);
if (e == s || *e)
return false;
rule->fwmask = n;
rule->flags |= IPRULE_FWMASK;
}
return true;
}
void
iprule_add(struct blob_attr *attr, bool v6)
{
struct interface *iif = NULL, *oif = NULL;
struct blob_attr *tb[__RULE_MAX], *cur;
struct interface *iface;
struct iprule *rule;
int af = v6 ? AF_INET6 : AF_INET;
blobmsg_parse(rule_attr, __RULE_MAX, tb, blobmsg_data(attr), blobmsg_data_len(attr));
rule = calloc(1, sizeof(*rule));
if (!rule)
return;
rule->flags = v6 ? IPRULE_INET6 : IPRULE_INET4;
rule->order = iprules_counter[rule->flags]++;
if ((cur = tb[RULE_INVERT]) != NULL)
rule->invert = blobmsg_get_bool(cur);
if ((cur = tb[RULE_INTERFACE_IN]) != NULL) {
iif = vlist_find(&interfaces, blobmsg_data(cur), iface, node);
if (!iif || !iif->l3_dev.dev) {
DPRINTF("Failed to resolve device of network: %s\n", (char *) blobmsg_data(cur));
goto error;
}
memcpy(rule->in_dev, iif->l3_dev.dev->ifname, sizeof(rule->in_dev));
rule->flags |= IPRULE_IN;
}
if ((cur = tb[RULE_INTERFACE_OUT]) != NULL) {
oif = vlist_find(&interfaces, blobmsg_data(cur), iface, node);
if (!oif || !oif->l3_dev.dev) {
DPRINTF("Failed to resolve device of network: %s\n", (char *) blobmsg_data(cur));
goto error;
}
memcpy(rule->out_dev, oif->l3_dev.dev->ifname, sizeof(rule->out_dev));
rule->flags |= IPRULE_OUT;
}
if ((cur = tb[RULE_SRC]) != NULL) {
if (!parse_ip_and_netmask(af, blobmsg_data(cur), &rule->src_addr, &rule->src_mask)) {
DPRINTF("Failed to parse rule source: %s\n", (char *) blobmsg_data(cur));
goto error;
}
rule->flags |= IPRULE_SRC;
}
if ((cur = tb[RULE_DEST]) != NULL) {
if (!parse_ip_and_netmask(af, blobmsg_data(cur), &rule->dest_addr, &rule->dest_mask)) {
DPRINTF("Failed to parse rule destination: %s\n", (char *) blobmsg_data(cur));
goto error;
}
rule->flags |= IPRULE_DEST;
}
if ((cur = tb[RULE_PRIORITY]) != NULL) {
rule->priority = blobmsg_get_u32(cur);
rule->flags |= IPRULE_PRIORITY;
}
if ((cur = tb[RULE_TOS]) != NULL) {
if ((rule->tos = blobmsg_get_u32(cur)) > 255) {
DPRINTF("Invalid TOS value: %u\n", blobmsg_get_u32(cur));
goto error;
}
rule->flags |= IPRULE_TOS;
}
if ((cur = tb[RULE_FWMARK]) != NULL) {
if (!iprule_parse_mark(blobmsg_data(cur), rule)) {
DPRINTF("Failed to parse rule fwmark: %s\n", (char *) blobmsg_data(cur));
goto error;
}
/* flags set by iprule_parse_mark() */
}
if ((cur = tb[RULE_LOOKUP]) != NULL) {
if (!system_resolve_rt_table(blobmsg_data(cur), &rule->lookup)) {
DPRINTF("Failed to parse rule lookup table: %s\n", (char *) blobmsg_data(cur));
goto error;
}
rule->flags |= IPRULE_LOOKUP;
}
if ((cur = tb[RULE_ACTION]) != NULL) {
if (!system_resolve_iprule_action(blobmsg_data(cur), &rule->action)) {
DPRINTF("Failed to parse rule action: %s\n", (char *) blobmsg_data(cur));
goto error;
}
rule->flags |= IPRULE_ACTION;
}
if ((cur = tb[RULE_GOTO]) != NULL) {
rule->gotoid = blobmsg_get_u32(cur);
rule->flags |= IPRULE_GOTO;
}
vlist_add(&iprules, &rule->node, &rule->flags);
return;
error:
free(rule);
}
void
iprule_update_start(void)
{
if (!iprules_flushed) {
system_flush_iprules();
iprules_flushed = true;
}
iprules_counter[0] = 1;
iprules_counter[1] = 1;
vlist_update(&iprules);
}
void
iprule_update_complete(void)
{
vlist_flush(&iprules);
}
static int
rule_cmp(const void *k1, const void *k2, void *ptr)
{
return memcmp(k1, k2, sizeof(struct iprule)-offsetof(struct iprule, flags));
}
static void
iprule_update_rule(struct vlist_tree *tree,
struct vlist_node *node_new, struct vlist_node *node_old)
{
struct iprule *rule_old, *rule_new;
rule_old = container_of(node_old, struct iprule, node);
rule_new = container_of(node_new, struct iprule, node);
if (node_old) {
system_del_iprule(rule_old);
free(rule_old);
}
if (node_new)
system_add_iprule(rule_new);
}
static void __init
iprule_init_list(void)
{
vlist_init(&iprules, rule_cmp, iprule_update_rule);
}

101
src/3P/netifd/iprule.h Normal file
View File

@@ -0,0 +1,101 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __IPRULE_H
#define __IPRULE_H
#include "interface-ip.h"
#define IPRULE_PRIORITY_ADDR 10000
#define IPRULE_PRIORITY_ADDR_MASK 20000
#define IPRULE_PRIORITY_NW 90000
#define IPRULE_PRIORITY_REJECT 4200000000
enum iprule_flags {
/* address family for rule */
IPRULE_INET4 = (0 << 0),
IPRULE_INET6 = (1 << 0),
IPRULE_FAMILY = IPRULE_INET4 | IPRULE_INET6,
/* rule specifies input device */
IPRULE_IN = (1 << 2),
/* rule specifies output device */
IPRULE_OUT = (1 << 3),
/* rule specifies src */
IPRULE_SRC = (1 << 4),
/* rule specifies dest */
IPRULE_DEST = (1 << 5),
/* rule specifies priority */
IPRULE_PRIORITY = (1 << 6),
/* rule specifies diffserv/tos */
IPRULE_TOS = (1 << 7),
/* rule specifies fwmark */
IPRULE_FWMARK = (1 << 8),
/* rule specifies fwmask */
IPRULE_FWMASK = (1 << 9),
/* rule performs table lookup */
IPRULE_LOOKUP = (1 << 10),
/* rule performs routing action */
IPRULE_ACTION = (1 << 11),
/* rule is a goto */
IPRULE_GOTO = (1 << 12),
};
struct iprule {
struct vlist_node node;
unsigned int order;
/* everything below is used as avl tree key */
enum iprule_flags flags;
bool invert;
char in_dev[IFNAMSIZ + 1];
char out_dev[IFNAMSIZ + 1];
unsigned int src_mask;
union if_addr src_addr;
unsigned int dest_mask;
union if_addr dest_addr;
unsigned int priority;
unsigned int tos;
unsigned int fwmark;
unsigned int fwmask;
unsigned int lookup;
unsigned int action;
unsigned int gotoid;
};
extern struct vlist_tree iprules;
extern const struct uci_blob_param_list rule_attr_list;
void iprule_add(struct blob_attr *attr, bool v6);
void iprule_update_start(void);
void iprule_update_complete(void);
#endif

263
src/3P/netifd/macvlan.c Normal file
View File

@@ -0,0 +1,263 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <net/ethernet.h>
#ifdef linux
#include <netinet/ether.h>
#endif
#include "netifd.h"
#include "device.h"
#include "interface.h"
#include "system.h"
enum {
MACVLAN_ATTR_IFNAME,
MACVLAN_ATTR_MACADDR,
MACVLAN_ATTR_MODE,
__MACVLAN_ATTR_MAX
};
static const struct blobmsg_policy macvlan_attrs[__MACVLAN_ATTR_MAX] = {
[MACVLAN_ATTR_IFNAME] = { "ifname", BLOBMSG_TYPE_STRING },
[MACVLAN_ATTR_MACADDR] = { "macaddr", BLOBMSG_TYPE_STRING },
[MACVLAN_ATTR_MODE] = { "mode", BLOBMSG_TYPE_STRING },
};
static const struct uci_blob_param_list macvlan_attr_list = {
.n_params = __MACVLAN_ATTR_MAX,
.params = macvlan_attrs,
.n_next = 1,
.next = { &device_attr_list },
};
struct macvlan_device {
struct device dev;
struct device_user parent;
device_state_cb set_state;
struct blob_attr *config_data;
struct blob_attr *ifname;
struct macvlan_config config;
};
static void
macvlan_base_cb(struct device_user *dev, enum device_event ev)
{
struct macvlan_device *mvdev = container_of(dev, struct macvlan_device, parent);
switch (ev) {
case DEV_EVENT_ADD:
device_set_present(&mvdev->dev, true);
break;
case DEV_EVENT_REMOVE:
device_set_present(&mvdev->dev, false);
break;
default:
return;
}
}
static int
macvlan_set_down(struct macvlan_device *mvdev)
{
mvdev->set_state(&mvdev->dev, false);
system_macvlan_del(&mvdev->dev);
device_release(&mvdev->parent);
return 0;
}
static int
macvlan_set_up(struct macvlan_device *mvdev)
{
int ret;
ret = device_claim(&mvdev->parent);
if (ret < 0)
return ret;
ret = system_macvlan_add(&mvdev->dev, mvdev->parent.dev, &mvdev->config);
if (ret < 0)
goto release;
ret = mvdev->set_state(&mvdev->dev, true);
if (ret)
goto delete;
return 0;
delete:
system_macvlan_del(&mvdev->dev);
release:
device_release(&mvdev->parent);
return ret;
}
static int
macvlan_set_state(struct device *dev, bool up)
{
struct macvlan_device *mvdev;
D(SYSTEM, "macvlan_set_state(%s, %u)\n", dev->ifname, up);
mvdev = container_of(dev, struct macvlan_device, dev);
if (up)
return macvlan_set_up(mvdev);
else
return macvlan_set_down(mvdev);
}
static void
macvlan_free(struct device *dev)
{
struct macvlan_device *mvdev;
mvdev = container_of(dev, struct macvlan_device, dev);
device_remove_user(&mvdev->parent);
free(mvdev->config_data);
free(mvdev);
}
static void
macvlan_dump_info(struct device *dev, struct blob_buf *b)
{
struct macvlan_device *mvdev;
mvdev = container_of(dev, struct macvlan_device, dev);
blobmsg_add_string(b, "parent", mvdev->parent.dev->ifname);
system_if_dump_info(dev, b);
}
static void
macvlan_config_init(struct device *dev)
{
struct macvlan_device *mvdev;
struct device *basedev = NULL;
mvdev = container_of(dev, struct macvlan_device, dev);
if (mvdev->ifname)
basedev = device_get(blobmsg_data(mvdev->ifname), true);
device_add_user(&mvdev->parent, basedev);
}
static void
macvlan_apply_settings(struct macvlan_device *mvdev, struct blob_attr **tb)
{
struct macvlan_config *cfg = &mvdev->config;
struct blob_attr *cur;
struct ether_addr *ea;
cfg->flags = 0;
cfg->mode = NULL;
if ((cur = tb[MACVLAN_ATTR_MACADDR])) {
ea = ether_aton(blobmsg_data(cur));
if (ea) {
memcpy(cfg->macaddr, ea, 6);
cfg->flags |= MACVLAN_OPT_MACADDR;
}
}
if ((cur = tb[MACVLAN_ATTR_MODE]))
cfg->mode = blobmsg_data(cur);
}
static enum dev_change_type
macvlan_reload(struct device *dev, struct blob_attr *attr)
{
struct blob_attr *tb_dev[__DEV_ATTR_MAX];
struct blob_attr *tb_mv[__MACVLAN_ATTR_MAX];
enum dev_change_type ret = DEV_CONFIG_APPLIED;
struct macvlan_device *mvdev;
mvdev = container_of(dev, struct macvlan_device, dev);
attr = blob_memdup(attr);
blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, tb_dev,
blob_data(attr), blob_len(attr));
blobmsg_parse(macvlan_attrs, __MACVLAN_ATTR_MAX, tb_mv,
blob_data(attr), blob_len(attr));
device_init_settings(dev, tb_dev);
macvlan_apply_settings(mvdev, tb_mv);
mvdev->ifname = tb_mv[MACVLAN_ATTR_IFNAME];
if (mvdev->config_data) {
struct blob_attr *otb_dev[__DEV_ATTR_MAX];
struct blob_attr *otb_mv[__MACVLAN_ATTR_MAX];
blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, otb_dev,
blob_data(mvdev->config_data), blob_len(mvdev->config_data));
if (uci_blob_diff(tb_dev, otb_dev, &device_attr_list, NULL))
ret = DEV_CONFIG_RESTART;
blobmsg_parse(macvlan_attrs, __MACVLAN_ATTR_MAX, otb_mv,
blob_data(mvdev->config_data), blob_len(mvdev->config_data));
if (uci_blob_diff(tb_mv, otb_mv, &macvlan_attr_list, NULL))
ret = DEV_CONFIG_RESTART;
macvlan_config_init(dev);
}
free(mvdev->config_data);
mvdev->config_data = attr;
return ret;
}
static struct device *
macvlan_create(const char *name, struct blob_attr *attr)
{
struct macvlan_device *mvdev;
struct device *dev = NULL;
mvdev = calloc(1, sizeof(*mvdev));
if (!mvdev)
return NULL;
dev = &mvdev->dev;
device_init(dev, &macvlan_device_type, name);
dev->config_pending = true;
mvdev->set_state = dev->set_state;
dev->set_state = macvlan_set_state;
dev->hotplug_ops = NULL;
mvdev->parent.cb = macvlan_base_cb;
macvlan_reload(dev, attr);
return dev;
}
const struct device_type macvlan_device_type = {
.name = "MAC VLAN",
.config_params = &macvlan_attr_list,
.create = macvlan_create,
.config_init = macvlan_config_init,
.reload = macvlan_reload,
.free = macvlan_free,
.dump_info = macvlan_dump_info,
};

346
src/3P/netifd/main.c Normal file
View File

@@ -0,0 +1,346 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <signal.h>
#include <stdarg.h>
#include <syslog.h>
#include "netifd.h"
#include "ubus.h"
#include "config.h"
#include "system.h"
#include "interface.h"
#include "wireless.h"
#include "proto.h"
unsigned int debug_mask = 0;
const char *main_path = DEFAULT_MAIN_PATH;
const char *config_path = DEFAULT_CONFIG_PATH;
const char *resolv_conf = DEFAULT_RESOLV_CONF;
static char **global_argv;
static struct list_head process_list = LIST_HEAD_INIT(process_list);
#define DEFAULT_LOG_LEVEL L_NOTICE
static int log_level = DEFAULT_LOG_LEVEL;
static const int log_class[] = {
[L_CRIT] = LOG_CRIT,
[L_WARNING] = LOG_WARNING,
[L_NOTICE] = LOG_NOTICE,
[L_INFO] = LOG_INFO,
[L_DEBUG] = LOG_DEBUG
};
#ifdef DUMMY_MODE
#define use_syslog false
#else
static bool use_syslog = true;
#endif
static void
netifd_delete_process(struct netifd_process *proc)
{
list_del(&proc->list);
ustream_free(&proc->log.stream);
close(proc->log.fd.fd);
}
void
netifd_log_message(int priority, const char *format, ...)
{
va_list vl;
if (priority > log_level)
return;
va_start(vl, format);
if (use_syslog)
vsyslog(log_class[priority], format, vl);
else
vfprintf(stderr, format, vl);
va_end(vl);
}
static void
netifd_process_log_read_cb(struct ustream *s, int bytes)
{
struct netifd_process *proc;
const char *log_prefix;
char *data;
int len = 0;
proc = container_of(s, struct netifd_process, log.stream);
log_prefix = proc->log_prefix;
if (!log_prefix)
log_prefix = "process";
do {
char *newline;
data = ustream_get_read_buf(s, &len);
if (!len)
break;
newline = strchr(data, '\n');
if (proc->log_overflow) {
if (newline) {
len = newline + 1 - data;
proc->log_overflow = false;
}
} else if (newline) {
*newline = 0;
len = newline + 1 - data;
netifd_log_message(L_NOTICE, "%s (%d): %s\n",
log_prefix, proc->uloop.pid, data);
} else if (len == s->r.buffer_len) {
netifd_log_message(L_NOTICE, "%s (%d): %s [...]\n",
log_prefix, proc->uloop.pid, data);
proc->log_overflow = true;
} else
break;
ustream_consume(s, len);
} while (1);
}
static void
netifd_process_cb(struct uloop_process *proc, int ret)
{
struct netifd_process *np;
np = container_of(proc, struct netifd_process, uloop);
while (ustream_poll(&np->log.stream));
netifd_delete_process(np);
return np->cb(np, ret);
}
int
netifd_start_process(const char **argv, char **env, struct netifd_process *proc)
{
int pfds[2];
int pid;
netifd_kill_process(proc);
if (pipe(pfds) < 0)
return -1;
if ((pid = fork()) < 0)
goto error;
if (!pid) {
int i;
if (env) {
while (*env) {
putenv(*env);
env++;
}
}
if (proc->dir_fd >= 0)
if (fchdir(proc->dir_fd)) {}
close(pfds[0]);
for (i = 0; i <= 2; i++) {
if (pfds[1] == i)
continue;
dup2(pfds[1], i);
}
if (pfds[1] > 2)
close(pfds[1]);
execvp(argv[0], (char **) argv);
exit(127);
}
close(pfds[1]);
proc->uloop.cb = netifd_process_cb;
proc->uloop.pid = pid;
uloop_process_add(&proc->uloop);
list_add_tail(&proc->list, &process_list);
system_fd_set_cloexec(pfds[0]);
proc->log.stream.string_data = true;
proc->log.stream.notify_read = netifd_process_log_read_cb;
ustream_fd_init(&proc->log, pfds[0]);
return 0;
error:
close(pfds[0]);
close(pfds[1]);
return -1;
}
void
netifd_kill_process(struct netifd_process *proc)
{
if (!proc->uloop.pending)
return;
kill(proc->uloop.pid, SIGKILL);
uloop_process_delete(&proc->uloop);
netifd_delete_process(proc);
}
static void netifd_do_restart(struct uloop_timeout *timeout)
{
execvp(global_argv[0], global_argv);
}
void netifd_reload(void)
{
config_init_all();
}
void netifd_restart(void)
{
static struct uloop_timeout main_timer = {
.cb = netifd_do_restart
};
interface_set_down(NULL);
uloop_timeout_set(&main_timer, 1000);
}
static int usage(const char *progname)
{
fprintf(stderr, "Usage: %s [options]\n"
"Options:\n"
" -d <mask>: Mask for debug messages\n"
" -s <path>: Path to the ubus socket\n"
" -p <path>: Path to netifd addons (default: %s)\n"
" -c <path>: Path to UCI configuration\n"
" -h <path>: Path to the hotplug script\n"
" -r <path>: Path to resolv.conf\n"
" -l <level>: Log output level (default: %d)\n"
" -S: Use stderr instead of syslog for log messages\n"
" (default: "DEFAULT_HOTPLUG_PATH")\n"
"\n", progname, main_path, DEFAULT_LOG_LEVEL);
return 1;
}
static void
netifd_handle_signal(int signo)
{
uloop_end();
}
static void
netifd_setup_signals(void)
{
struct sigaction s;
memset(&s, 0, sizeof(s));
s.sa_handler = netifd_handle_signal;
s.sa_flags = 0;
sigaction(SIGINT, &s, NULL);
sigaction(SIGTERM, &s, NULL);
sigaction(SIGUSR1, &s, NULL);
sigaction(SIGUSR2, &s, NULL);
s.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &s, NULL);
}
static void
netifd_kill_processes(void)
{
struct netifd_process *proc, *tmp;
list_for_each_entry_safe(proc, tmp, &process_list, list)
netifd_kill_process(proc);
}
int main(int argc, char **argv)
{
const char *socket = NULL;
int ch;
global_argv = argv;
while ((ch = getopt(argc, argv, "d:s:p:c:h:r:l:S")) != -1) {
switch(ch) {
case 'd':
debug_mask = strtoul(optarg, NULL, 0);
break;
case 's':
socket = optarg;
break;
case 'p':
main_path = optarg;
break;
case 'c':
config_path = optarg;
break;
case 'h':
hotplug_cmd_path = optarg;
break;
case 'r':
resolv_conf = optarg;
break;
case 'l':
log_level = atoi(optarg);
if (log_level >= ARRAY_SIZE(log_class))
log_level = ARRAY_SIZE(log_class) - 1;
break;
#ifndef DUMMY_MODE
case 'S':
use_syslog = false;
break;
#endif
default:
return usage(argv[0]);
}
}
if (use_syslog)
openlog("netifd", 0, LOG_DAEMON);
netifd_setup_signals();
if (netifd_ubus_init(socket) < 0) {
fprintf(stderr, "Failed to connect to ubus\n");
return 1;
}
proto_shell_init();
wireless_init();
if (system_init()) {
fprintf(stderr, "Failed to initialize system control\n");
return 1;
}
config_init_all();
uloop_run();
netifd_kill_processes();
netifd_ubus_done();
if (use_syslog)
closelog();
return 0;
}

103
src/3P/netifd/netifd.h Normal file
View File

@@ -0,0 +1,103 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __NETIFD_H
#define __NETIFD_H
#include <sys/socket.h>
#include <net/if.h>
#include <stdbool.h>
#include <stdio.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include "utils.h"
#ifdef DUMMY_MODE
#define DEFAULT_MAIN_PATH "./examples"
#define DEFAULT_CONFIG_PATH "./config"
#define DEFAULT_HOTPLUG_PATH "./examples/hotplug-cmd"
#define DEFAULT_RESOLV_CONF "./tmp/resolv.conf"
#else
#define DEFAULT_MAIN_PATH "/lib/netifd"
#define DEFAULT_CONFIG_PATH NULL /* use the default set in libuci */
#define DEFAULT_HOTPLUG_PATH "/sbin/hotplug-call"
#define DEFAULT_RESOLV_CONF "/tmp/resolv.conf.auto"
#endif
extern const char *resolv_conf;
extern char *hotplug_cmd_path;
extern unsigned int debug_mask;
enum {
L_CRIT,
L_WARNING,
L_NOTICE,
L_INFO,
L_DEBUG
};
enum {
DEBUG_SYSTEM = 0,
DEBUG_DEVICE = 1,
DEBUG_INTERFACE = 2,
DEBUG_WIRELESS = 3,
};
#ifdef DEBUG
#define DPRINTF(format, ...) fprintf(stderr, "%s(%d): " format, __func__, __LINE__, ## __VA_ARGS__)
#define D(level, format, ...) do { \
if (debug_mask & (1 << (DEBUG_ ## level))) \
DPRINTF(format, ##__VA_ARGS__); \
} while (0)
#else
#define DPRINTF(format, ...) no_debug(0, format, ## __VA_ARGS__)
#define D(level, format, ...) no_debug(DEBUG_ ## level, format, ## __VA_ARGS__)
#endif
#define LOG_BUF_SIZE 256
static inline void no_debug(int level, const char *fmt, ...)
{
}
struct netifd_process {
struct list_head list;
struct uloop_process uloop;
void (*cb)(struct netifd_process *, int ret);
int dir_fd;
struct ustream_fd log;
const char *log_prefix;
bool log_overflow;
};
void netifd_log_message(int priority, const char *format, ...);
int netifd_start_process(const char **argv, char **env, struct netifd_process *proc);
void netifd_kill_process(struct netifd_process *proc);
struct device;
struct interface;
extern const char *main_path;
extern const char *config_path;
void netifd_restart(void);
void netifd_reload(void);
#endif

924
src/3P/netifd/proto-shell.c Normal file
View File

@@ -0,0 +1,924 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "netifd.h"
#include "interface.h"
#include "interface-ip.h"
#include "proto.h"
#include "system.h"
#include "handler.h"
static int proto_fd = -1;
enum proto_shell_sm {
S_IDLE,
S_SETUP,
S_SETUP_ABORT,
S_TEARDOWN,
};
struct proto_shell_handler {
struct list_head list;
struct proto_handler proto;
char *config_buf;
char *script_name;
bool init_available;
bool no_proto_task;
struct uci_blob_param_list config;
};
struct proto_shell_dependency {
struct list_head list;
struct proto_shell_state *proto;
struct interface_user dep;
union if_addr host;
bool v6;
bool any;
char interface[];
};
struct proto_shell_state {
struct interface_proto_state proto;
struct proto_shell_handler *handler;
struct blob_attr *config;
struct uloop_timeout teardown_timeout;
/*
* Teardown and setup interface again if it is still not up (IFS_UP)
* after checkup_interval seconds since previous attempt. This check
* will be disabled when the config option "checkup_interval" is
* missing or has a negative value
*/
int checkup_interval;
struct uloop_timeout checkup_timeout;
struct netifd_process script_task;
struct netifd_process proto_task;
enum proto_shell_sm sm;
bool proto_task_killed;
bool renew_pending;
int last_error;
struct list_head deps;
};
static void
proto_shell_check_dependencies(struct proto_shell_state *state)
{
struct proto_shell_dependency *dep;
bool available = true;
list_for_each_entry(dep, &state->deps, list) {
if (dep->dep.iface)
continue;
available = false;
break;
}
interface_set_available(state->proto.iface, available);
}
static void
proto_shell_if_up_cb(struct interface_user *dep, struct interface *iface,
enum interface_event ev);
static void
proto_shell_if_down_cb(struct interface_user *dep, struct interface *iface,
enum interface_event ev);
static void
proto_shell_update_host_dep(struct proto_shell_dependency *dep)
{
struct interface *iface = NULL;
if (dep->dep.iface)
goto out;
if (dep->interface[0]) {
iface = vlist_find(&interfaces, dep->interface, iface, node);
if (!iface || iface->state != IFS_UP)
goto out;
}
if (!dep->any)
iface = interface_ip_add_target_route(&dep->host, dep->v6, iface);
if (!iface)
goto out;
interface_remove_user(&dep->dep);
dep->dep.cb = proto_shell_if_down_cb;
interface_add_user(&dep->dep, iface);
out:
proto_shell_check_dependencies(dep->proto);
}
static void
proto_shell_clear_host_dep(struct proto_shell_state *state)
{
struct proto_shell_dependency *dep, *tmp;
list_for_each_entry_safe(dep, tmp, &state->deps, list) {
interface_remove_user(&dep->dep);
list_del(&dep->list);
free(dep);
}
}
static int
proto_shell_handler(struct interface_proto_state *proto,
enum interface_proto_cmd cmd, bool force)
{
struct proto_shell_state *state;
struct proto_shell_handler *handler;
struct netifd_process *proc;
static char error_buf[32];
const char *argv[7];
char *envp[2];
const char *action;
char *config;
int ret, i = 0, j = 0;
state = container_of(proto, struct proto_shell_state, proto);
handler = state->handler;
proc = &state->script_task;
if (cmd == PROTO_CMD_SETUP) {
switch (state->sm) {
case S_IDLE:
action = "setup";
state->last_error = -1;
proto_shell_clear_host_dep(state);
state->sm = S_SETUP;
break;
case S_SETUP_ABORT:
case S_TEARDOWN:
case S_SETUP:
return 0;
default:
return -1;
}
} else if (cmd == PROTO_CMD_RENEW) {
if (!(handler->proto.flags & PROTO_FLAG_RENEW_AVAILABLE))
return 0;
if (state->script_task.uloop.pending) {
state->renew_pending = true;
return 0;
}
state->renew_pending = false;
action = "renew";
} else {
switch (state->sm) {
case S_SETUP:
if (state->script_task.uloop.pending) {
uloop_timeout_set(&state->teardown_timeout, 1000);
kill(state->script_task.uloop.pid, SIGTERM);
if (state->proto_task.uloop.pending)
kill(state->proto_task.uloop.pid, SIGTERM);
state->renew_pending = false;
state->sm = S_SETUP_ABORT;
return 0;
}
/* fall through if no script task is running */
case S_IDLE:
action = "teardown";
state->renew_pending = false;
state->sm = S_TEARDOWN;
if (state->last_error >= 0) {
snprintf(error_buf, sizeof(error_buf), "ERROR=%d", state->last_error);
envp[j++] = error_buf;
}
uloop_timeout_set(&state->teardown_timeout, 5000);
break;
case S_TEARDOWN:
return 0;
default:
return -1;
}
}
D(INTERFACE, "run %s for interface '%s'\n", action, proto->iface->name);
config = blobmsg_format_json(state->config, true);
if (!config)
return -1;
argv[i++] = handler->script_name;
argv[i++] = handler->proto.name;
argv[i++] = action;
argv[i++] = proto->iface->name;
argv[i++] = config;
if (proto->iface->main_dev.dev)
argv[i++] = proto->iface->main_dev.dev->ifname;
argv[i] = NULL;
envp[j] = NULL;
ret = netifd_start_process(argv, envp, proc);
free(config);
return ret;
}
static void
proto_shell_if_up_cb(struct interface_user *dep, struct interface *iface,
enum interface_event ev)
{
struct proto_shell_dependency *pdep;
if (ev != IFEV_UP && ev != IFEV_UPDATE)
return;
pdep = container_of(dep, struct proto_shell_dependency, dep);
proto_shell_update_host_dep(pdep);
}
static void
proto_shell_if_down_cb(struct interface_user *dep, struct interface *iface,
enum interface_event ev)
{
struct proto_shell_dependency *pdep;
struct proto_shell_state *state;
if (ev == IFEV_UP || ev == IFEV_UPDATE)
return;
pdep = container_of(dep, struct proto_shell_dependency, dep);
interface_remove_user(dep);
dep->cb = proto_shell_if_up_cb;
interface_add_user(dep, NULL);
state = pdep->proto;
if (state->sm == S_IDLE) {
state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN, false);
}
}
static void
proto_shell_task_finish(struct proto_shell_state *state,
struct netifd_process *task)
{
switch (state->sm) {
case S_IDLE:
if (task == &state->proto_task)
state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
/* fall through */
case S_SETUP:
if (task == &state->proto_task)
proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN,
false);
else if (task == &state->script_task) {
if (state->renew_pending)
proto_shell_handler(&state->proto,
PROTO_CMD_RENEW, false);
else if (!state->handler->no_proto_task &&
!state->proto_task.uloop.pending &&
state->sm == S_SETUP)
proto_shell_handler(&state->proto,
PROTO_CMD_TEARDOWN,
false);
/* check up status after setup attempt by this script_task */
if (state->sm == S_SETUP && state->checkup_interval > 0) {
uloop_timeout_set(&state->checkup_timeout,
state->checkup_interval * 1000);
}
}
break;
case S_SETUP_ABORT:
if (state->script_task.uloop.pending ||
state->proto_task.uloop.pending)
break;
/* completed aborting all tasks, now idle */
uloop_timeout_cancel(&state->teardown_timeout);
uloop_timeout_cancel(&state->checkup_timeout);
state->sm = S_IDLE;
proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN, false);
break;
case S_TEARDOWN:
if (state->script_task.uloop.pending)
break;
if (state->proto_task.uloop.pending) {
if (!state->proto_task_killed)
kill(state->proto_task.uloop.pid, SIGTERM);
break;
}
/* completed tearing down all tasks, now idle */
uloop_timeout_cancel(&state->teardown_timeout);
uloop_timeout_cancel(&state->checkup_timeout);
state->sm = S_IDLE;
state->proto.proto_event(&state->proto, IFPEV_DOWN);
break;
}
}
static void
proto_shell_teardown_timeout_cb(struct uloop_timeout *timeout)
{
struct proto_shell_state *state;
state = container_of(timeout, struct proto_shell_state, teardown_timeout);
netifd_kill_process(&state->script_task);
netifd_kill_process(&state->proto_task);
proto_shell_task_finish(state, NULL);
}
static void
proto_shell_script_cb(struct netifd_process *p, int ret)
{
struct proto_shell_state *state;
state = container_of(p, struct proto_shell_state, script_task);
proto_shell_task_finish(state, p);
}
static void
proto_shell_task_cb(struct netifd_process *p, int ret)
{
struct proto_shell_state *state;
state = container_of(p, struct proto_shell_state, proto_task);
if (state->sm == S_IDLE || state->sm == S_SETUP)
state->last_error = WEXITSTATUS(ret);
proto_shell_task_finish(state, p);
}
static void
proto_shell_free(struct interface_proto_state *proto)
{
struct proto_shell_state *state;
state = container_of(proto, struct proto_shell_state, proto);
uloop_timeout_cancel(&state->teardown_timeout);
uloop_timeout_cancel(&state->checkup_timeout);
proto_shell_clear_host_dep(state);
netifd_kill_process(&state->script_task);
netifd_kill_process(&state->proto_task);
free(state->config);
free(state);
}
static void
proto_shell_parse_route_list(struct interface *iface, struct blob_attr *attr,
bool v6)
{
struct blob_attr *cur;
int rem;
blobmsg_for_each_attr(cur, attr, rem) {
if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) {
DPRINTF("Ignore wrong route type: %d\n", blobmsg_type(cur));
continue;
}
interface_ip_add_route(iface, cur, v6);
}
}
static void
proto_shell_parse_data(struct interface *iface, struct blob_attr *attr)
{
struct blob_attr *cur;
int rem;
blobmsg_for_each_attr(cur, attr, rem)
interface_add_data(iface, cur);
}
static struct device *
proto_shell_create_tunnel(const char *name, struct blob_attr *attr)
{
struct device *dev;
struct blob_buf b;
memset(&b, 0, sizeof(b));
blob_buf_init(&b, 0);
blob_put(&b, 0, blobmsg_data(attr), blobmsg_data_len(attr));
dev = device_create(name, &tunnel_device_type, blob_data(b.head));
blob_buf_free(&b);
return dev;
}
enum {
NOTIFY_ACTION,
NOTIFY_ERROR,
NOTIFY_COMMAND,
NOTIFY_ENV,
NOTIFY_SIGNAL,
NOTIFY_AVAILABLE,
NOTIFY_LINK_UP,
NOTIFY_IFNAME,
NOTIFY_ADDR_EXT,
NOTIFY_ROUTES,
NOTIFY_ROUTES6,
NOTIFY_TUNNEL,
NOTIFY_DATA,
NOTIFY_KEEP,
NOTIFY_HOST,
NOTIFY_DNS,
NOTIFY_DNS_SEARCH,
__NOTIFY_LAST
};
static const struct blobmsg_policy notify_attr[__NOTIFY_LAST] = {
[NOTIFY_ACTION] = { .name = "action", .type = BLOBMSG_TYPE_INT32 },
[NOTIFY_ERROR] = { .name = "error", .type = BLOBMSG_TYPE_ARRAY },
[NOTIFY_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_ARRAY },
[NOTIFY_ENV] = { .name = "env", .type = BLOBMSG_TYPE_ARRAY },
[NOTIFY_SIGNAL] = { .name = "signal", .type = BLOBMSG_TYPE_INT32 },
[NOTIFY_AVAILABLE] = { .name = "available", .type = BLOBMSG_TYPE_BOOL },
[NOTIFY_LINK_UP] = { .name = "link-up", .type = BLOBMSG_TYPE_BOOL },
[NOTIFY_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
[NOTIFY_ADDR_EXT] = { .name = "address-external", .type = BLOBMSG_TYPE_BOOL },
[NOTIFY_ROUTES] = { .name = "routes", .type = BLOBMSG_TYPE_ARRAY },
[NOTIFY_ROUTES6] = { .name = "routes6", .type = BLOBMSG_TYPE_ARRAY },
[NOTIFY_TUNNEL] = { .name = "tunnel", .type = BLOBMSG_TYPE_TABLE },
[NOTIFY_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE },
[NOTIFY_KEEP] = { .name = "keep", .type = BLOBMSG_TYPE_BOOL },
[NOTIFY_HOST] = { .name = "host", .type = BLOBMSG_TYPE_STRING },
[NOTIFY_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
[NOTIFY_DNS_SEARCH] = { .name = "dns_search", .type = BLOBMSG_TYPE_ARRAY },
};
static int
proto_shell_update_link(struct proto_shell_state *state, struct blob_attr *data, struct blob_attr **tb)
{
struct interface *iface = state->proto.iface;
struct blob_attr *cur;
struct device *dev;
const char *devname;
int dev_create = 1;
bool addr_ext = false;
bool keep = false;
bool up;
if (state->sm == S_TEARDOWN || state->sm == S_SETUP_ABORT)
return UBUS_STATUS_PERMISSION_DENIED;
if (!tb[NOTIFY_LINK_UP])
return UBUS_STATUS_INVALID_ARGUMENT;
up = blobmsg_get_bool(tb[NOTIFY_LINK_UP]);
if (!up) {
state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
return 0;
}
if ((cur = tb[NOTIFY_KEEP]) != NULL)
keep = blobmsg_get_bool(cur);
if ((cur = tb[NOTIFY_ADDR_EXT]) != NULL) {
addr_ext = blobmsg_get_bool(cur);
if (addr_ext)
dev_create = 2;
}
if (iface->state != IFS_UP || !iface->l3_dev.dev)
keep = false;
if (!keep) {
dev = iface->main_dev.dev;
if (tb[NOTIFY_IFNAME]) {
keep = false;
devname = blobmsg_data(tb[NOTIFY_IFNAME]);
if (tb[NOTIFY_TUNNEL])
dev = proto_shell_create_tunnel(devname, tb[NOTIFY_TUNNEL]);
else
dev = device_get(devname, dev_create);
}
if (!dev)
return UBUS_STATUS_INVALID_ARGUMENT;
interface_set_l3_dev(iface, dev);
if (device_claim(&iface->l3_dev) < 0)
return UBUS_STATUS_UNKNOWN_ERROR;
device_set_present(dev, true);
interface_update_start(iface);
}
proto_apply_ip_settings(iface, data, addr_ext);
if ((cur = tb[NOTIFY_ROUTES]) != NULL)
proto_shell_parse_route_list(state->proto.iface, cur, false);
if ((cur = tb[NOTIFY_ROUTES6]) != NULL)
proto_shell_parse_route_list(state->proto.iface, cur, true);
if ((cur = tb[NOTIFY_DNS]))
interface_add_dns_server_list(&iface->proto_ip, cur);
if ((cur = tb[NOTIFY_DNS_SEARCH]))
interface_add_dns_search_list(&iface->proto_ip, cur);
if ((cur = tb[NOTIFY_DATA]))
proto_shell_parse_data(state->proto.iface, cur);
interface_update_complete(state->proto.iface);
if ((state->sm != S_SETUP_ABORT) && (state->sm != S_TEARDOWN)) {
if (!keep)
state->proto.proto_event(&state->proto, IFPEV_UP);
state->sm = S_IDLE;
}
return 0;
}
static bool
fill_string_list(struct blob_attr *attr, char **argv, int max)
{
struct blob_attr *cur;
int argc = 0;
int rem;
if (!attr)
goto out;
blobmsg_for_each_attr(cur, attr, rem) {
if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
return false;
if (!blobmsg_check_attr(cur, NULL))
return false;
argv[argc++] = blobmsg_data(cur);
if (argc == max - 1)
return false;
}
out:
argv[argc] = NULL;
return true;
}
static int
proto_shell_run_command(struct proto_shell_state *state, struct blob_attr **tb)
{
static char *argv[64];
static char *env[32];
if (state->sm == S_TEARDOWN || state->sm == S_SETUP_ABORT)
return UBUS_STATUS_PERMISSION_DENIED;
if (!tb[NOTIFY_COMMAND])
goto error;
if (!fill_string_list(tb[NOTIFY_COMMAND], argv, ARRAY_SIZE(argv)))
goto error;
if (!fill_string_list(tb[NOTIFY_ENV], env, ARRAY_SIZE(env)))
goto error;
netifd_start_process((const char **) argv, (char **) env, &state->proto_task);
return 0;
error:
return UBUS_STATUS_INVALID_ARGUMENT;
}
static int
proto_shell_kill_command(struct proto_shell_state *state, struct blob_attr **tb)
{
unsigned int signal = ~0;
if (tb[NOTIFY_SIGNAL])
signal = blobmsg_get_u32(tb[NOTIFY_SIGNAL]);
if (signal > 31)
signal = SIGTERM;
if (state->proto_task.uloop.pending) {
if (signal == SIGTERM || signal == SIGKILL)
state->proto_task_killed = true;
kill(state->proto_task.uloop.pid, signal);
}
return 0;
}
static int
proto_shell_notify_error(struct proto_shell_state *state, struct blob_attr **tb)
{
struct blob_attr *cur;
char *data[16];
int n_data = 0;
int rem;
if (!tb[NOTIFY_ERROR])
return UBUS_STATUS_INVALID_ARGUMENT;
blobmsg_for_each_attr(cur, tb[NOTIFY_ERROR], rem) {
if (n_data + 1 == ARRAY_SIZE(data))
goto error;
if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
goto error;
if (!blobmsg_check_attr(cur, NULL))
goto error;
data[n_data++] = blobmsg_data(cur);
}
if (!n_data)
goto error;
interface_add_error(state->proto.iface, state->handler->proto.name,
data[0], (const char **) &data[1], n_data - 1);
return 0;
error:
return UBUS_STATUS_INVALID_ARGUMENT;
}
static int
proto_shell_block_restart(struct proto_shell_state *state, struct blob_attr **tb)
{
state->proto.iface->autostart = false;
return 0;
}
static int
proto_shell_set_available(struct proto_shell_state *state, struct blob_attr **tb)
{
if (!tb[NOTIFY_AVAILABLE])
return UBUS_STATUS_INVALID_ARGUMENT;
interface_set_available(state->proto.iface, blobmsg_get_bool(tb[NOTIFY_AVAILABLE]));
return 0;
}
static int
proto_shell_add_host_dependency(struct proto_shell_state *state, struct blob_attr **tb)
{
struct proto_shell_dependency *dep;
const char *ifname = tb[NOTIFY_IFNAME] ? blobmsg_data(tb[NOTIFY_IFNAME]) : "";
const char *host = tb[NOTIFY_HOST] ? blobmsg_data(tb[NOTIFY_HOST]) : "";
if (state->sm == S_TEARDOWN || state->sm == S_SETUP_ABORT)
return UBUS_STATUS_PERMISSION_DENIED;
dep = calloc(1, sizeof(*dep) + strlen(ifname) + 1);
if (!dep)
return UBUS_STATUS_UNKNOWN_ERROR;
if (!host[0] && ifname[0]) {
dep->any = true;
} else if (inet_pton(AF_INET, host, &dep->host) < 1) {
if (inet_pton(AF_INET6, host, &dep->host) < 1) {
free(dep);
return UBUS_STATUS_INVALID_ARGUMENT;
} else {
dep->v6 = true;
}
}
dep->proto = state;
strcpy(dep->interface, ifname);
dep->dep.cb = proto_shell_if_up_cb;
interface_add_user(&dep->dep, NULL);
list_add(&dep->list, &state->deps);
proto_shell_update_host_dep(dep);
if (!dep->dep.iface)
return UBUS_STATUS_NOT_FOUND;
return 0;
}
static int
proto_shell_setup_failed(struct proto_shell_state *state)
{
int ret = 0;
switch (state->sm) {
case S_IDLE:
state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
/* fall through */
case S_SETUP:
proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN, false);
break;
case S_SETUP_ABORT:
case S_TEARDOWN:
default:
ret = UBUS_STATUS_PERMISSION_DENIED;
break;
}
return ret;
}
static int
proto_shell_notify(struct interface_proto_state *proto, struct blob_attr *attr)
{
struct proto_shell_state *state;
struct blob_attr *tb[__NOTIFY_LAST];
state = container_of(proto, struct proto_shell_state, proto);
blobmsg_parse(notify_attr, __NOTIFY_LAST, tb, blob_data(attr), blob_len(attr));
if (!tb[NOTIFY_ACTION])
return UBUS_STATUS_INVALID_ARGUMENT;
switch(blobmsg_get_u32(tb[NOTIFY_ACTION])) {
case 0:
return proto_shell_update_link(state, attr, tb);
case 1:
return proto_shell_run_command(state, tb);
case 2:
return proto_shell_kill_command(state, tb);
case 3:
return proto_shell_notify_error(state, tb);
case 4:
return proto_shell_block_restart(state, tb);
case 5:
return proto_shell_set_available(state, tb);
case 6:
return proto_shell_add_host_dependency(state, tb);
case 7:
return proto_shell_setup_failed(state);
default:
return UBUS_STATUS_INVALID_ARGUMENT;
}
}
static void
proto_shell_checkup_timeout_cb(struct uloop_timeout *timeout)
{
struct proto_shell_state *state = container_of(timeout, struct
proto_shell_state, checkup_timeout);
struct interface_proto_state *proto = &state->proto;
struct interface *iface = proto->iface;
if (!iface->autostart)
return;
if (iface->state == IFS_UP)
return;
D(INTERFACE, "Interface '%s' is not up after %d sec\n",
iface->name, state->checkup_interval);
proto_shell_handler(proto, PROTO_CMD_TEARDOWN, false);
}
static void
proto_shell_checkup_attach(struct proto_shell_state *state,
const struct blob_attr *attr)
{
struct blob_attr *tb;
struct blobmsg_policy checkup_policy = {
.name = "checkup_interval",
.type = BLOBMSG_TYPE_INT32
};
blobmsg_parse(&checkup_policy, 1, &tb, blob_data(attr), blob_len(attr));
if (!tb) {
state->checkup_interval = -1;
state->checkup_timeout.cb = NULL;
} else {
state->checkup_interval = blobmsg_get_u32(tb);
state->checkup_timeout.cb = proto_shell_checkup_timeout_cb;
}
}
static struct interface_proto_state *
proto_shell_attach(const struct proto_handler *h, struct interface *iface,
struct blob_attr *attr)
{
struct proto_shell_state *state;
state = calloc(1, sizeof(*state));
if (!state)
return NULL;
INIT_LIST_HEAD(&state->deps);
state->config = malloc(blob_pad_len(attr));
if (!state->config)
goto error;
memcpy(state->config, attr, blob_pad_len(attr));
proto_shell_checkup_attach(state, state->config);
state->proto.free = proto_shell_free;
state->proto.notify = proto_shell_notify;
state->proto.cb = proto_shell_handler;
state->teardown_timeout.cb = proto_shell_teardown_timeout_cb;
state->script_task.cb = proto_shell_script_cb;
state->script_task.dir_fd = proto_fd;
state->script_task.log_prefix = iface->name;
state->proto_task.cb = proto_shell_task_cb;
state->proto_task.dir_fd = proto_fd;
state->proto_task.log_prefix = iface->name;
state->handler = container_of(h, struct proto_shell_handler, proto);
return &state->proto;
error:
free(state);
return NULL;
}
static void
proto_shell_add_handler(const char *script, const char *name, json_object *obj)
{
struct proto_shell_handler *handler;
struct proto_handler *proto;
json_object *config, *tmp;
char *proto_name, *script_name;
handler = calloc_a(sizeof(*handler),
&proto_name, strlen(name) + 1,
&script_name, strlen(script) + 1);
if (!handler)
return;
handler->script_name = strcpy(script_name, script);
proto = &handler->proto;
proto->name = strcpy(proto_name, name);
proto->config_params = &handler->config;
proto->attach = proto_shell_attach;
tmp = json_get_field(obj, "no-device", json_type_boolean);
if (tmp && json_object_get_boolean(tmp))
handler->proto.flags |= PROTO_FLAG_NODEV;
tmp = json_get_field(obj, "no-proto-task", json_type_boolean);
handler->no_proto_task = tmp && json_object_get_boolean(tmp);
tmp = json_get_field(obj, "available", json_type_boolean);
if (tmp && json_object_get_boolean(tmp))
handler->proto.flags |= PROTO_FLAG_INIT_AVAILABLE;
tmp = json_get_field(obj, "renew-handler", json_type_boolean);
if (tmp && json_object_get_boolean(tmp))
handler->proto.flags |= PROTO_FLAG_RENEW_AVAILABLE;
tmp = json_get_field(obj, "lasterror", json_type_boolean);
if (tmp && json_object_get_boolean(tmp))
handler->proto.flags |= PROTO_FLAG_LASTERROR;
config = json_get_field(obj, "config", json_type_array);
if (config)
handler->config_buf = netifd_handler_parse_config(&handler->config, config);
DPRINTF("Add handler for script %s: %s\n", script, proto->name);
add_proto_handler(proto);
}
void proto_shell_init(void)
{
proto_fd = netifd_open_subdir("proto");
if (proto_fd < 0)
return;
netifd_init_script_handlers(proto_fd, proto_shell_add_handler);
}

View File

@@ -0,0 +1,113 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "netifd.h"
#include "interface.h"
#include "interface-ip.h"
#include "proto.h"
#include "system.h"
struct static_proto_state {
struct interface_proto_state proto;
struct blob_attr *config;
};
static bool
static_proto_setup(struct static_proto_state *state)
{
struct interface *iface = state->proto.iface;
struct device *dev = iface->main_dev.dev;
interface_set_l3_dev(iface, dev);
return proto_apply_static_ip_settings(state->proto.iface, state->config) == 0;
}
static int
static_handler(struct interface_proto_state *proto,
enum interface_proto_cmd cmd, bool force)
{
struct static_proto_state *state;
int ret = 0;
state = container_of(proto, struct static_proto_state, proto);
switch (cmd) {
case PROTO_CMD_SETUP:
if (!static_proto_setup(state))
return -1;
break;
case PROTO_CMD_TEARDOWN:
case PROTO_CMD_RENEW:
break;
}
return ret;
}
static void
static_free(struct interface_proto_state *proto)
{
struct static_proto_state *state;
state = container_of(proto, struct static_proto_state, proto);
free(state->config);
free(state);
}
static struct interface_proto_state *
static_attach(const struct proto_handler *h, struct interface *iface,
struct blob_attr *attr)
{
struct static_proto_state *state;
state = calloc(1, sizeof(*state));
if (!state)
return NULL;
state->config = malloc(blob_pad_len(attr));
if (!state->config)
goto error;
memcpy(state->config, attr, blob_pad_len(attr));
state->proto.free = static_free;
state->proto.cb = static_handler;
return &state->proto;
error:
free(state);
return NULL;
}
static struct proto_handler static_proto = {
.name = "static",
.flags = PROTO_FLAG_IMMEDIATE |
PROTO_FLAG_FORCE_LINK_DEFAULT,
.config_params = &proto_ip_attr,
.attach = static_attach,
};
static void __init
static_proto_init(void)
{
add_proto_handler(&static_proto);
}

642
src/3P/netifd/proto.c Normal file
View File

@@ -0,0 +1,642 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2012 Steven Barth <steven@midlink.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "netifd.h"
#include "system.h"
#include "interface.h"
#include "interface-ip.h"
#include "proto.h"
static struct avl_tree handlers;
enum {
OPT_IPADDR,
OPT_IP6ADDR,
OPT_NETMASK,
OPT_BROADCAST,
OPT_GATEWAY,
OPT_IP6GW,
OPT_IP6PREFIX,
__OPT_MAX,
};
static const struct blobmsg_policy proto_ip_attributes[__OPT_MAX] = {
[OPT_IPADDR] = { .name = "ipaddr", .type = BLOBMSG_TYPE_ARRAY },
[OPT_IP6ADDR] = { .name = "ip6addr", .type = BLOBMSG_TYPE_ARRAY },
[OPT_NETMASK] = { .name = "netmask", .type = BLOBMSG_TYPE_STRING },
[OPT_BROADCAST] = { .name = "broadcast", .type = BLOBMSG_TYPE_STRING },
[OPT_GATEWAY] = { .name = "gateway", .type = BLOBMSG_TYPE_STRING },
[OPT_IP6GW] = { .name = "ip6gw", .type = BLOBMSG_TYPE_STRING },
[OPT_IP6PREFIX] = { .name = "ip6prefix", .type = BLOBMSG_TYPE_ARRAY },
};
static const struct uci_blob_param_info proto_ip_attr_info[__OPT_MAX] = {
[OPT_IPADDR] = { .type = BLOBMSG_TYPE_STRING },
[OPT_IP6ADDR] = { .type = BLOBMSG_TYPE_STRING },
[OPT_IP6PREFIX] = { .type = BLOBMSG_TYPE_STRING },
};
static const char * const proto_ip_validate[__OPT_MAX] = {
[OPT_IPADDR] = "ip4addr",
[OPT_IP6ADDR] = "ip6addr",
[OPT_NETMASK] = "netmask",
[OPT_BROADCAST] = "ipaddr",
[OPT_GATEWAY] = "ip4addr",
[OPT_IP6GW] = "ip6addr",
[OPT_IP6PREFIX] = "ip6addr",
};
const struct uci_blob_param_list proto_ip_attr = {
.n_params = __OPT_MAX,
.params = proto_ip_attributes,
.validate = proto_ip_validate,
.info = proto_ip_attr_info,
};
enum {
ADDR_IPADDR,
ADDR_MASK,
ADDR_BROADCAST,
ADDR_PTP,
ADDR_PREFERRED,
ADDR_VALID,
ADDR_OFFLINK,
ADDR_CLASS,
__ADDR_MAX
};
static const struct blobmsg_policy proto_ip_addr[__ADDR_MAX] = {
[ADDR_IPADDR] = { .name = "ipaddr", .type = BLOBMSG_TYPE_STRING },
[ADDR_MASK] = { .name = "mask", .type = BLOBMSG_TYPE_STRING },
[ADDR_BROADCAST] = { .name = "broadcast", .type = BLOBMSG_TYPE_STRING },
[ADDR_PTP] = { .name = "ptp", .type = BLOBMSG_TYPE_STRING },
[ADDR_PREFERRED] = { .name = "preferred", .type = BLOBMSG_TYPE_INT32 },
[ADDR_VALID] = { .name = "valid", .type = BLOBMSG_TYPE_INT32 },
[ADDR_OFFLINK] = { .name = "offlink", .type = BLOBMSG_TYPE_BOOL },
[ADDR_CLASS] = { .name = "class", .type = BLOBMSG_TYPE_STRING },
};
static struct device_addr *
alloc_device_addr(bool v6, bool ext)
{
struct device_addr *addr;
addr = calloc(1, sizeof(*addr));
if (!addr)
return NULL;
addr->flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
if (ext)
addr->flags |= DEVADDR_EXTERNAL;
return addr;
}
static bool
parse_addr(struct interface *iface, const char *str, bool v6, int mask,
bool ext, uint32_t broadcast)
{
struct device_addr *addr;
int af = v6 ? AF_INET6 : AF_INET;
addr = alloc_device_addr(v6, ext);
if (!addr)
return false;
addr->mask = mask;
if (!parse_ip_and_netmask(af, str, &addr->addr, &addr->mask)) {
interface_add_error(iface, "proto", "INVALID_ADDRESS", &str, 1);
free(addr);
return false;
}
if (broadcast)
addr->broadcast = broadcast;
vlist_add(&iface->proto_ip.addr, &addr->node, &addr->flags);
return true;
}
static int
parse_static_address_option(struct interface *iface, struct blob_attr *attr,
bool v6, int netmask, bool ext, uint32_t broadcast)
{
struct blob_attr *cur;
int n_addr = 0;
int rem;
blobmsg_for_each_attr(cur, attr, rem) {
if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
return -1;
n_addr++;
if (!parse_addr(iface, blobmsg_data(cur), v6, netmask, ext,
broadcast))
return -1;
}
return n_addr;
}
static struct device_addr *
parse_address_item(struct blob_attr *attr, bool v6, bool ext)
{
struct device_addr *addr;
struct blob_attr *tb[__ADDR_MAX];
struct blob_attr *cur;
if (blobmsg_type(attr) != BLOBMSG_TYPE_TABLE)
return NULL;
addr = alloc_device_addr(v6, ext);
if (!addr)
return NULL;
blobmsg_parse(proto_ip_addr, __ADDR_MAX, tb, blobmsg_data(attr), blobmsg_data_len(attr));
addr->mask = v6 ? 128 : 32;
if ((cur = tb[ADDR_MASK])) {
unsigned int new_mask;
new_mask = parse_netmask_string(blobmsg_data(cur), v6);
if (new_mask > addr->mask)
goto error;
addr->mask = new_mask;
}
cur = tb[ADDR_IPADDR];
if (!cur)
goto error;
if (!inet_pton(v6 ? AF_INET6 : AF_INET, blobmsg_data(cur), &addr->addr))
goto error;
if ((cur = tb[ADDR_OFFLINK]) && blobmsg_get_bool(cur))
addr->flags |= DEVADDR_OFFLINK;
if (!v6) {
if ((cur = tb[ADDR_BROADCAST]) &&
!inet_pton(AF_INET, blobmsg_data(cur), &addr->broadcast))
goto error;
if ((cur = tb[ADDR_PTP]) &&
!inet_pton(AF_INET, blobmsg_data(cur), &addr->point_to_point))
goto error;
} else {
time_t now = system_get_rtime();
if ((cur = tb[ADDR_PREFERRED])) {
int64_t preferred = blobmsg_get_u32(cur);
int64_t preferred_until = preferred + (int64_t)now;
if (preferred_until <= LONG_MAX && preferred != 0xffffffffLL)
addr->preferred_until = preferred_until;
}
if ((cur = tb[ADDR_VALID])) {
int64_t valid = blobmsg_get_u32(cur);
int64_t valid_until = valid + (int64_t)now;
if (valid_until <= LONG_MAX && valid != 0xffffffffLL)
addr->valid_until = valid_until;
}
if (addr->valid_until) {
if (!addr->preferred_until)
addr->preferred_until = addr->valid_until;
else if (addr->preferred_until > addr->valid_until)
goto error;
}
if ((cur = tb[ADDR_CLASS]))
addr->pclass = strdup(blobmsg_get_string(cur));
}
return addr;
error:
free(addr);
return NULL;
}
static int
parse_address_list(struct interface *iface, struct blob_attr *attr, bool v6,
bool ext)
{
struct device_addr *addr;
struct blob_attr *cur;
int n_addr = 0;
int rem;
blobmsg_for_each_attr(cur, attr, rem) {
addr = parse_address_item(cur, v6, ext);
if (!addr)
return -1;
n_addr++;
vlist_add(&iface->proto_ip.addr, &addr->node, &addr->flags);
}
return n_addr;
}
static bool
parse_gateway_option(struct interface *iface, struct blob_attr *attr, bool v6)
{
struct device_route *route;
const char *str = blobmsg_data(attr);
int af = v6 ? AF_INET6 : AF_INET;
route = calloc(1, sizeof(*route));
if (!route)
return NULL;
if (!inet_pton(af, str, &route->nexthop)) {
interface_add_error(iface, "proto", "INVALID_GATEWAY", &str, 1);
free(route);
return false;
}
route->mask = 0;
route->flags = (v6 ? DEVADDR_INET6 : DEVADDR_INET4);
route->metric = iface->metric;
unsigned int table = (v6) ? iface->ip6table : iface->ip4table;
if (table) {
route->table = table;
route->flags |= DEVROUTE_SRCTABLE;
}
vlist_add(&iface->proto_ip.route, &route->node, route);
return true;
}
static bool
parse_prefix_option(struct interface *iface, const char *str, size_t len)
{
char buf[128] = {0}, *saveptr;
if (len >= sizeof(buf))
return false;
memcpy(buf, str, len);
char *addrstr = strtok_r(buf, "/", &saveptr);
if (!addrstr)
return false;
char *lengthstr = strtok_r(NULL, ",", &saveptr);
if (!lengthstr)
return false;
char *prefstr = strtok_r(NULL, ",", &saveptr);
char *validstr = (!prefstr) ? NULL : strtok_r(NULL, ",", &saveptr);
char *addstr = (!validstr) ? NULL : strtok_r(NULL, ",", &saveptr);
const char *pclass = NULL;
int64_t pref = (!prefstr) ? 0 : strtoul(prefstr, NULL, 10);
int64_t valid = (!validstr) ? 0 : strtoul(validstr, NULL, 10);
uint8_t length = strtoul(lengthstr, NULL, 10), excl_length = 0;
if (length < 1 || length > 64)
return false;
struct in6_addr addr, excluded, *excludedp = NULL;
if (inet_pton(AF_INET6, addrstr, &addr) < 1)
return false;
for (; addstr; addstr = strtok_r(NULL, ",", &saveptr)) {
char *key = NULL, *val = NULL, *addsaveptr;
if (!(key = strtok_r(addstr, "=", &addsaveptr)) ||
!(val = strtok_r(NULL, ",", &addsaveptr)))
continue;
if (!strcmp(key, "excluded")) {
char *sep = strchr(val, '/');
if (!sep)
return false;
*sep = 0;
excl_length = atoi(sep + 1);
if (inet_pton(AF_INET6, val, &excluded) < 1)
return false;
excludedp = &excluded;
} else if (!strcmp(key, "class")) {
pclass = val;
}
}
int64_t now = system_get_rtime();
time_t preferred_until = 0;
if (prefstr && pref != 0xffffffffLL && pref + now <= LONG_MAX)
preferred_until = pref + now;
time_t valid_until = 0;
if (validstr && valid != 0xffffffffLL && valid + now <= LONG_MAX)
valid_until = valid + now;
interface_ip_add_device_prefix(iface, &addr, length,
valid_until, preferred_until,
excludedp, excl_length, pclass);
return true;
}
static int
parse_prefix_list(struct interface *iface, struct blob_attr *attr)
{
struct blob_attr *cur;
int n_addr = 0;
int rem;
blobmsg_for_each_attr(cur, attr, rem) {
if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
return -1;
n_addr++;
if (!parse_prefix_option(iface, blobmsg_data(cur),
blobmsg_data_len(cur)))
return -1;
}
return n_addr;
}
int
proto_apply_static_ip_settings(struct interface *iface, struct blob_attr *attr)
{
struct blob_attr *tb[__OPT_MAX];
struct blob_attr *cur;
const char *error;
unsigned int netmask = 32;
int n_v4 = 0, n_v6 = 0;
struct in_addr bcast = {};
blobmsg_parse(proto_ip_attributes, __OPT_MAX, tb, blob_data(attr), blob_len(attr));
if ((cur = tb[OPT_NETMASK])) {
netmask = parse_netmask_string(blobmsg_data(cur), false);
if (netmask > 32) {
error = "INVALID_NETMASK";
goto error;
}
}
if ((cur = tb[OPT_BROADCAST])) {
if (!inet_pton(AF_INET, blobmsg_data(cur), &bcast)) {
error = "INVALID_BROADCAST";
goto error;
}
}
if ((cur = tb[OPT_IPADDR]))
n_v4 = parse_static_address_option(iface, cur, false,
netmask, false, bcast.s_addr);
if ((cur = tb[OPT_IP6ADDR]))
n_v6 = parse_static_address_option(iface, cur, true,
128, false, 0);
if ((cur = tb[OPT_IP6PREFIX]))
if (parse_prefix_list(iface, cur) < 0)
goto out;
if (n_v4 < 0 || n_v6 < 0)
goto out;
if ((cur = tb[OPT_GATEWAY])) {
if (n_v4 && !parse_gateway_option(iface, cur, false))
goto out;
}
if ((cur = tb[OPT_IP6GW])) {
if (n_v6 && !parse_gateway_option(iface, cur, true))
goto out;
}
return 0;
error:
interface_add_error(iface, "proto", error, NULL, 0);
out:
return -1;
}
int
proto_apply_ip_settings(struct interface *iface, struct blob_attr *attr, bool ext)
{
struct blob_attr *tb[__OPT_MAX];
struct blob_attr *cur;
int n_v4 = 0, n_v6 = 0;
blobmsg_parse(proto_ip_attributes, __OPT_MAX, tb, blob_data(attr), blob_len(attr));
if ((cur = tb[OPT_IPADDR]))
n_v4 = parse_address_list(iface, cur, false, ext);
if ((cur = tb[OPT_IP6ADDR]))
n_v6 = parse_address_list(iface, cur, true, ext);
if ((cur = tb[OPT_IP6PREFIX]))
if (parse_prefix_list(iface, cur) < 0)
goto out;
if (n_v4 < 0 || n_v6 < 0)
goto out;
if ((cur = tb[OPT_GATEWAY])) {
if (n_v4 && !parse_gateway_option(iface, cur, false))
goto out;
}
if ((cur = tb[OPT_IP6GW])) {
if (n_v6 && !parse_gateway_option(iface, cur, true))
goto out;
}
return 0;
out:
return -1;
}
void add_proto_handler(struct proto_handler *p)
{
if (!handlers.comp)
avl_init(&handlers, avl_strcmp, false, NULL);
if (p->avl.key)
return;
p->avl.key = p->name;
avl_insert(&handlers, &p->avl);
}
static void
default_proto_free(struct interface_proto_state *proto)
{
free(proto);
}
static int
invalid_proto_handler(struct interface_proto_state *proto,
enum interface_proto_cmd cmd, bool force)
{
return -1;
}
static int
no_proto_handler(struct interface_proto_state *proto,
enum interface_proto_cmd cmd, bool force)
{
return 0;
}
static struct interface_proto_state *
default_proto_attach(const struct proto_handler *h,
struct interface *iface, struct blob_attr *attr)
{
struct interface_proto_state *proto;
proto = calloc(1, sizeof(*proto));
if (!proto)
return NULL;
proto->free = default_proto_free;
proto->cb = no_proto_handler;
return proto;
}
static const struct proto_handler no_proto = {
.name = "none",
.flags = PROTO_FLAG_IMMEDIATE,
.attach = default_proto_attach,
};
static const struct proto_handler *
get_proto_handler(const char *name)
{
struct proto_handler *proto;
if (!strcmp(name, "none"))
return &no_proto;
if (!handlers.comp)
return NULL;
return avl_find_element(&handlers, name, proto, avl);
}
void
proto_dump_handlers(struct blob_buf *b)
{
struct proto_handler *p;
void *c;
avl_for_each_element(&handlers, p, avl) {
void *v;
c = blobmsg_open_table(b, p->name);
if (p->config_params->validate) {
int i;
v = blobmsg_open_table(b, "validate");
for (i = 0; i < p->config_params->n_params; i++)
blobmsg_add_string(b, p->config_params->params[i].name, uci_get_validate_string(p->config_params, i));
blobmsg_close_table(b, v);
}
blobmsg_add_u8(b, "no_device", !!(p->flags & PROTO_FLAG_NODEV));
blobmsg_close_table(b, c);
}
}
void
proto_init_interface(struct interface *iface, struct blob_attr *attr)
{
const struct proto_handler *proto = iface->proto_handler;
struct interface_proto_state *state = NULL;
if (!proto)
proto = &no_proto;
state = proto->attach(proto, iface, attr);
if (!state) {
state = no_proto.attach(&no_proto, iface, attr);
state->cb = invalid_proto_handler;
}
state->handler = proto;
interface_set_proto_state(iface, state);
}
void
proto_attach_interface(struct interface *iface, const char *proto_name)
{
const struct proto_handler *proto = &no_proto;
const char *error = NULL;
if (proto_name) {
proto = get_proto_handler(proto_name);
if (!proto) {
error = "INVALID_PROTO";
proto = &no_proto;
}
}
iface->proto_handler = proto;
if (error)
interface_add_error(iface, "proto", error, NULL, 0);
}
int
interface_proto_event(struct interface_proto_state *proto,
enum interface_proto_cmd cmd, bool force)
{
enum interface_proto_event ev;
int ret;
ret = proto->cb(proto, cmd, force);
if (ret || !(proto->handler->flags & PROTO_FLAG_IMMEDIATE))
goto out;
switch(cmd) {
case PROTO_CMD_SETUP:
ev = IFPEV_UP;
break;
case PROTO_CMD_TEARDOWN:
ev = IFPEV_DOWN;
break;
case PROTO_CMD_RENEW:
ev = IFPEV_RENEW;
break;
default:
return -EINVAL;
}
proto->proto_event(proto, ev);
out:
return ret;
}

83
src/3P/netifd/proto.h Normal file
View File

@@ -0,0 +1,83 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __NETIFD_PROTO_H
#define __NETIFD_PROTO_H
struct interface;
struct interface_proto_state;
struct proto_handler;
enum interface_proto_event {
IFPEV_UP,
IFPEV_DOWN,
IFPEV_LINK_LOST,
IFPEV_RENEW,
};
enum interface_proto_cmd {
PROTO_CMD_SETUP,
PROTO_CMD_TEARDOWN,
PROTO_CMD_RENEW,
};
enum {
PROTO_FLAG_IMMEDIATE = (1 << 0),
PROTO_FLAG_NODEV = (1 << 1),
PROTO_FLAG_INIT_AVAILABLE = (1 << 2),
PROTO_FLAG_RENEW_AVAILABLE = (1 << 3),
PROTO_FLAG_FORCE_LINK_DEFAULT = (1 << 4),
PROTO_FLAG_LASTERROR = (1 << 5),
};
struct interface_proto_state {
const struct proto_handler *handler;
struct interface *iface;
/* filled in by the protocol user */
void (*proto_event)(struct interface_proto_state *, enum interface_proto_event ev);
/* filled in by the protocol handler */
int (*notify)(struct interface_proto_state *, struct blob_attr *data);
int (*cb)(struct interface_proto_state *, enum interface_proto_cmd cmd, bool force);
void (*free)(struct interface_proto_state *);
};
struct proto_handler {
struct avl_node avl;
unsigned int flags;
const char *name;
const struct uci_blob_param_list *config_params;
struct interface_proto_state *(*attach)(const struct proto_handler *h,
struct interface *iface, struct blob_attr *attr);
};
extern const struct uci_blob_param_list proto_ip_attr;
void add_proto_handler(struct proto_handler *p);
void proto_init_interface(struct interface *iface, struct blob_attr *attr);
void proto_attach_interface(struct interface *iface, const char *proto_name);
int interface_proto_event(struct interface_proto_state *proto,
enum interface_proto_cmd cmd, bool force);
unsigned int parse_netmask_string(const char *str, bool v6);
int proto_apply_static_ip_settings(struct interface *iface, struct blob_attr *attr);
int proto_apply_ip_settings(struct interface *iface, struct blob_attr *attr, bool ext);
void proto_dump_handlers(struct blob_buf *b);
void proto_shell_init(void);
#endif

View File

@@ -0,0 +1,401 @@
NETIFD_MAIN_DIR="${NETIFD_MAIN_DIR:-/lib/netifd}"
. /usr/share/libubox/jshn.sh
. $NETIFD_MAIN_DIR/utils.sh
proto_config_add_int() {
config_add_int "$@"
}
proto_config_add_string() {
config_add_string "$@"
}
proto_config_add_boolean() {
config_add_boolean "$@"
}
_proto_do_teardown() {
json_load "$data"
eval "proto_$1_teardown \"$interface\" \"$ifname\""
}
_proto_do_renew() {
json_load "$data"
eval "proto_$1_renew \"$interface\" \"$ifname\""
}
_proto_do_setup() {
json_load "$data"
_EXPORT_VAR=0
_EXPORT_VARS=
eval "proto_$1_setup \"$interface\" \"$ifname\""
}
proto_init_update() {
local ifname="$1"
local up="$2"
local external="$3"
PROTO_KEEP=0
PROTO_INIT=1
PROTO_TUNNEL_OPEN=
PROTO_IPADDR=
PROTO_IP6ADDR=
PROTO_ROUTE=
PROTO_ROUTE6=
PROTO_PREFIX6=
PROTO_DNS=
PROTO_DNS_SEARCH=
json_init
json_add_int action 0
[ -n "$ifname" -a "*" != "$ifname" ] && json_add_string "ifname" "$ifname"
json_add_boolean "link-up" "$up"
[ -n "$3" ] && json_add_boolean "address-external" "$external"
}
proto_set_keep() {
PROTO_KEEP="$1"
}
proto_close_nested() {
[ -n "$PROTO_NESTED_OPEN" ] && json_close_object
PROTO_NESTED_OPEN=
}
proto_add_nested() {
PROTO_NESTED_OPEN=1
json_add_object "$1"
}
proto_add_tunnel() {
proto_add_nested "tunnel"
}
proto_close_tunnel() {
proto_close_nested
}
proto_add_data() {
proto_add_nested "data"
}
proto_close_data() {
proto_close_nested
}
proto_add_dns_server() {
local address="$1"
append PROTO_DNS "$address"
}
proto_add_dns_search() {
local address="$1"
append PROTO_DNS_SEARCH "$address"
}
proto_add_ipv4_address() {
local address="$1"
local mask="$2"
local broadcast="$3"
local ptp="$4"
append PROTO_IPADDR "$address/$mask/$broadcast/$ptp"
}
proto_add_ipv6_address() {
local address="$1"
local mask="$2"
local preferred="$3"
local valid="$4"
local offlink="$5"
local class="$6"
append PROTO_IP6ADDR "$address/$mask/$preferred/$valid/$offlink/$class"
}
proto_add_ipv4_route() {
local target="$1"
local mask="$2"
local gw="$3"
local source="$4"
local metric="$5"
append PROTO_ROUTE "$target/$mask/$gw/$metric///$source"
}
proto_add_ipv6_route() {
local target="$1"
local mask="$2"
local gw="$3"
local metric="$4"
local valid="$5"
local source="$6"
local table="$7"
append PROTO_ROUTE6 "$target/$mask/$gw/$metric/$valid/$table/$source"
}
proto_add_ipv6_prefix() {
local prefix="$1"
local valid="$2"
local preferred="$3"
if [ -z "$valid" ]; then
append PROTO_PREFIX6 "$prefix"
else
[ -z "$preferred" ] && preferred="$valid"
append PROTO_PREFIX6 "$prefix,$valid,$preferred"
fi
}
_proto_push_ipv4_addr() {
local str="$1"
local address mask broadcast ptp
address="${str%%/*}"
str="${str#*/}"
mask="${str%%/*}"
str="${str#*/}"
broadcast="${str%%/*}"
str="${str#*/}"
ptp="$str"
json_add_object ""
json_add_string ipaddr "$address"
[ -n "$mask" ] && json_add_string mask "$mask"
[ -n "$broadcast" ] && json_add_string broadcast "$broadcast"
[ -n "$ptp" ] && json_add_string ptp "$ptp"
json_close_object
}
_proto_push_ipv6_addr() {
local str="$1"
local address mask preferred valid offlink
address="${str%%/*}"
str="${str#*/}"
mask="${str%%/*}"
str="${str#*/}"
preferred="${str%%/*}"
str="${str#*/}"
valid="${str%%/*}"
str="${str#*/}"
offlink="${str%%/*}"
str="${str#*/}"
class="${str%%/*}"
json_add_object ""
json_add_string ipaddr "$address"
[ -n "$mask" ] && json_add_string mask "$mask"
[ -n "$preferred" ] && json_add_int preferred "$preferred"
[ -n "$valid" ] && json_add_int valid "$valid"
[ -n "$offlink" ] && json_add_boolean offlink "$offlink"
[ -n "$class" ] && json_add_string class "$class"
json_close_object
}
_proto_push_string() {
json_add_string "" "$1"
}
_proto_push_route() {
local str="$1";
local target="${str%%/*}"
str="${str#*/}"
local mask="${str%%/*}"
str="${str#*/}"
local gw="${str%%/*}"
str="${str#*/}"
local metric="${str%%/*}"
str="${str#*/}"
local valid="${str%%/*}"
str="${str#*/}"
local table="${str%%/*}"
str="${str#*/}"
local source="${str}"
json_add_object ""
json_add_string target "$target"
json_add_string netmask "$mask"
[ -n "$gw" ] && json_add_string gateway "$gw"
[ -n "$metric" ] && json_add_int metric "$metric"
[ -n "$valid" ] && json_add_int valid "$valid"
[ -n "$source" ] && json_add_string source "$source"
[ -n "$table" ] && json_add_string table "$table"
json_close_object
}
_proto_push_array() {
local name="$1"
local val="$2"
local cb="$3"
[ -n "$val" ] || return 0
json_add_array "$name"
for item in $val; do
eval "$cb \"\$item\""
done
json_close_array
}
_proto_notify() {
local interface="$1"
local options="$2"
json_add_string "interface" "$interface"
ubus $options call network.interface notify_proto "$(json_dump)"
}
proto_send_update() {
local interface="$1"
proto_close_nested
json_add_boolean keep "$PROTO_KEEP"
_proto_push_array "ipaddr" "$PROTO_IPADDR" _proto_push_ipv4_addr
_proto_push_array "ip6addr" "$PROTO_IP6ADDR" _proto_push_ipv6_addr
_proto_push_array "routes" "$PROTO_ROUTE" _proto_push_route
_proto_push_array "routes6" "$PROTO_ROUTE6" _proto_push_route
_proto_push_array "ip6prefix" "$PROTO_PREFIX6" _proto_push_string
_proto_push_array "dns" "$PROTO_DNS" _proto_push_string
_proto_push_array "dns_search" "$PROTO_DNS_SEARCH" _proto_push_string
_proto_notify "$interface"
}
proto_export() {
local var="VAR${_EXPORT_VAR}"
_EXPORT_VAR="$(($_EXPORT_VAR + 1))"
export -- "$var=$1"
append _EXPORT_VARS "$var"
}
proto_run_command() {
local interface="$1"; shift
json_init
json_add_int action 1
json_add_array command
while [ $# -gt 0 ]; do
json_add_string "" "$1"
shift
done
json_close_array
[ -n "$_EXPORT_VARS" ] && {
json_add_array env
for var in $_EXPORT_VARS; do
eval "json_add_string \"\" \"\${$var}\""
done
json_close_array
}
_proto_notify "$interface"
}
proto_kill_command() {
local interface="$1"; shift
json_init
json_add_int action 2
[ -n "$1" ] && json_add_int signal "$1"
_proto_notify "$interface"
}
proto_notify_error() {
local interface="$1"; shift
json_init
json_add_int action 3
json_add_array error
while [ $# -gt 0 ]; do
json_add_string "" "$1"
shift
done
json_close_array
_proto_notify "$interface"
}
proto_block_restart() {
local interface="$1"; shift
json_init
json_add_int action 4
_proto_notify "$interface"
}
proto_set_available() {
local interface="$1"
local state="$2"
json_init
json_add_int action 5
json_add_boolean available "$state"
_proto_notify "$interface"
}
proto_add_host_dependency() {
local interface="$1"
local host="$2"
local ifname="$3"
# execute in subshell to not taint callers env
# see tickets #11046, #11545, #11570
(
json_init
json_add_int action 6
json_add_string host "$host"
[ -n "$ifname" ] && json_add_string ifname "$ifname"
_proto_notify "$interface" -S
)
}
proto_setup_failed() {
local interface="$1"
json_init
json_add_int action 7
_proto_notify "$interface"
}
init_proto() {
proto="$1"; shift
cmd="$1"; shift
case "$cmd" in
dump)
add_protocol() {
no_device=0
no_proto_task=0
available=0
renew_handler=0
add_default_handler "proto_$1_init_config"
json_init
json_add_string "name" "$1"
json_add_array "config"
eval "proto_$1_init_config"
json_close_array
json_add_boolean no-device "$no_device"
json_add_boolean no-proto-task "$no_proto_task"
json_add_boolean available "$available"
json_add_boolean renew-handler "$renew_handler"
json_add_boolean lasterror "$lasterror"
json_dump
}
;;
setup|teardown|renew)
interface="$1"; shift
data="$1"; shift
ifname="$1"; shift
add_protocol() {
[[ "$proto" == "$1" ]] || return 0
case "$cmd" in
setup) _proto_do_setup "$1";;
teardown) _proto_do_teardown "$1" ;;
renew) _proto_do_renew "$1" ;;
*) return 1 ;;
esac
}
;;
esac
}

View File

@@ -0,0 +1,337 @@
NETIFD_MAIN_DIR="${NETIFD_MAIN_DIR:-/lib/netifd}"
. /usr/share/libubox/jshn.sh
. $NETIFD_MAIN_DIR/utils.sh
CMD_UP=0
CMD_SET_DATA=1
CMD_PROCESS_ADD=2
CMD_PROCESS_KILL_ALL=3
CMD_SET_RETRY=4
add_driver() {
return
}
wireless_setup_vif_failed() {
local error="$1"
echo "Interface $_w_iface setup failed: $error"
}
wireless_setup_failed() {
local error="$1"
echo "Device setup failed: $error"
wireless_set_retry 0
}
prepare_key_wep() {
local key="$1"
local hex=1
echo -n "$key" | grep -qE "[^a-fA-F0-9]" && hex=0
[ "${#key}" -eq 10 -a $hex -eq 1 ] || \
[ "${#key}" -eq 26 -a $hex -eq 1 ] || {
[ "${key:0:2}" = "s:" ] && key="${key#s:}"
key="$(echo -n "$key" | hexdump -ve '1/1 "%02x" ""')"
}
echo "$key"
}
_wdev_prepare_channel() {
json_get_vars channel hwmode
auto_channel=0
enable_ht=0
htmode=
hwmode="${hwmode##11}"
hwmode_n="${hwmode##n}"
case "$channel" in
""|0|auto)
channel=0
auto_channel=1
;;
[0-9]*) ;;
*)
wireless_setup_failed "INVALID_CHANNEL"
;;
esac
[[ "$hwmode_n" = "$hwmode" ]] || {
enable_ht=1
hwmode="$hwmode_n"
json_get_vars htmode
case "$htmode" in
HT20|HT40+|HT40-);;
*) htmode= ;;
esac
}
case "$hwmode" in
a|b|g) ;;
*)
if [ "$channel" -gt 14 ]; then
hwmode=a
else
hwmode=g
fi
;;
esac
}
_wdev_handler() {
json_load "$data"
json_select config
_wdev_prepare_channel
json_select ..
eval "drv_$1_$2 \"$interface\""
}
_wdev_msg_call() {
local old_cb
json_set_namespace wdev old_cb
"$@"
json_set_namespace $old_cb
}
_wdev_wrapper() {
while [ -n "$1" ]; do
eval "$1() { _wdev_msg_call _$1 \"\$@\"; }"
shift
done
}
_wdev_notify_init() {
local command="$1"
local interface="$2"
json_init
json_add_int "command" "$command"
json_add_string "device" "$__netifd_device"
[ -n "$interface" ] && json_add_string "interface" "$interface"
json_add_object "data"
}
_wdev_notify() {
local options="$1"
json_close_object
ubus $options call network.wireless notify "$(json_dump)"
}
_wdev_add_variables() {
while [ -n "$1" ]; do
local var="${1%%=*}"
local val="$1"
shift
[[ "$var" = "$val" ]] && continue
val="${val#*=}"
json_add_string "$var" "$val"
done
}
_wireless_add_vif() {
local name="$1"; shift
local ifname="$1"; shift
_wdev_notify_init $CMD_SET_DATA "$name"
json_add_string "ifname" "$ifname"
_wdev_add_variables "$@"
_wdev_notify
}
_wireless_set_up() {
_wdev_notify_init $CMD_UP
_wdev_notify
}
_wireless_set_data() {
_wdev_notify_init $CMD_SET_DATA
_wdev_add_variables "$@"
_wdev_notify
}
_wireless_add_process() {
_wdev_notify_init $CMD_PROCESS_ADD
local exe="$2"
[ -L "$exe" ] && exe="$(readlink -f "$exe")"
json_add_int pid "$1"
json_add_string exe "$exe"
[ -n "$3" ] && json_add_boolean required 1
exe2="$(readlink -f /proc/$pid/exe)"
[ "$exe" = "$exe2" ] && echo "WARNING (wireless_add_process): executable path $exe does not match process $1 path ($exe2)"
_wdev_notify
}
_wireless_process_kill_all() {
_wdev_notify_init $CMD_PROCESS_KILL_ALL
[ -n "$1" ] && json_add_int signal "$1"
_wdev_notify
}
_wireless_set_retry() {
_wdev_notify_init $CMD_SET_RETRY
json_add_int retry "$1"
_wdev_notify
}
_wdev_wrapper \
wireless_add_vif \
wireless_set_up \
wireless_set_data \
wireless_add_process \
wireless_process_kill_all \
wireless_set_retry \
wireless_vif_parse_encryption() {
json_get_vars encryption
set_default encryption none
auth_mode_open=1
auth_mode_shared=0
auth_type=none
wpa_cipher=CCMP
case "$encryption" in
*tkip+aes|*tkip+ccmp|*aes+tkip|*ccmp+tkip) wpa_cipher="CCMP TKIP";;
*aes|*ccmp) wpa_cipher="CCMP";;
*tkip) wpa_cipher="TKIP";;
esac
# 802.11n requires CCMP for WPA
[ "$enable_ht:$wpa_cipher" = "1:TKIP" ] && wpa_cipher="CCMP TKIP"
# Examples:
# psk-mixed/tkip => WPA1+2 PSK, TKIP
# wpa-psk2/tkip+aes => WPA2 PSK, CCMP+TKIP
# wpa2/tkip+aes => WPA2 RADIUS, CCMP+TKIP
case "$encryption" in
wpa2*|*psk2*)
wpa=2
;;
*mixed*)
wpa=3
;;
wpa*|*psk*)
wpa=1
;;
*)
wpa=0
wpa_cipher=
;;
esac
wpa_pairwise="$wpa_cipher"
case "$encryption" in
*psk*)
auth_type=psk
;;
*wpa*|*8021x*)
auth_type=eap
;;
*wep*)
auth_type=wep
case "$encryption" in
*shared*)
auth_mode_open=0
auth_mode_shared=1
;;
*mixed*)
auth_mode_shared=1
;;
esac
;;
esac
}
_wireless_set_brsnoop_isolation() {
local multicast_to_unicast="$1"
local isolate
json_get_var isolate isolate
[ ${isolate:-0} -gt 0 -o -z "$network_bridge" ] && return
[ ${multicast_to_unicast:-1} -gt 0 ] && json_add_boolean isolate 1
}
for_each_interface() {
local _w_types="$1"; shift
local _w_ifaces _w_iface
local _w_type
local _w_found
local multicast_to_unicast
json_get_keys _w_ifaces interfaces
json_select interfaces
for _w_iface in $_w_ifaces; do
json_select "$_w_iface"
if [ -n "$_w_types" ]; then
json_get_var network_bridge bridge
json_get_var multicast_to_unicast multicast_to_unicast
json_select config
_wireless_set_brsnoop_isolation "$multicast_to_unicast"
json_get_var _w_type mode
json_select ..
_w_types=" $_w_types "
[[ "${_w_types%$_w_type*}" = "$_w_types" ]] && {
json_select ..
continue
}
fi
"$@" "$_w_iface"
json_select ..
done
json_select ..
}
_wdev_common_device_config() {
config_add_string channel hwmode htmode
}
_wdev_common_iface_config() {
config_add_string mode ssid encryption 'key:wpakey'
}
init_wireless_driver() {
name="$1"; shift
cmd="$1"; shift
case "$cmd" in
dump)
add_driver() {
eval "drv_$1_cleanup"
json_init
json_add_string name "$1"
json_add_array device
_wdev_common_device_config
eval "drv_$1_init_device_config"
json_close_array
json_add_array iface
_wdev_common_iface_config
eval "drv_$1_init_iface_config"
json_close_array
json_dump
}
;;
setup|teardown)
interface="$1"; shift
data="$1"; shift
export __netifd_device="$interface"
add_driver() {
[[ "$name" == "$1" ]] || return 0
_wdev_handler "$1" "$cmd"
}
;;
esac
}

View File

@@ -0,0 +1,50 @@
N="
"
append() {
local var="$1"
local value="$2"
local sep="${3:- }"
eval "export -- \"$var=\${$var:+\${$var}\${value:+\$sep}}\$value\""
}
add_default_handler() {
case "$(type $1 2>/dev/null)" in
*function*) return;;
*) eval "$1() { return; }"
esac
}
set_default() {
local __s_var="$1"
local __s_val="$2"
eval "export -- \"$__s_var=\${$__s_var:-\$__s_val}\""
}
_config_add_generic() {
local type="$1"; shift
for name in "$@"; do
json_add_array ""
json_add_string "" "$name"
json_add_int "" "$type"
json_close_array
done
}
config_add_int() {
_config_add_generic 5 "$@"
}
config_add_array() {
_config_add_generic 1 "$@"
}
config_add_string() {
_config_add_generic 3 "$@"
}
config_add_boolean() {
_config_add_generic 7 "$@"
}

View File

@@ -0,0 +1,286 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <sys/time.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#ifndef DEBUG
#define DEBUG
#endif
#include "netifd.h"
#include "device.h"
#include "system.h"
int system_init(void)
{
return 0;
}
int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg)
{
D(SYSTEM, "brctl addbr %s\n", bridge->ifname);
return 0;
}
int system_bridge_delbr(struct device *bridge)
{
D(SYSTEM, "brctl delbr %s\n", bridge->ifname);
return 0;
}
int system_bridge_addif(struct device *bridge, struct device *dev)
{
D(SYSTEM, "brctl addif %s %s\n", bridge->ifname, dev->ifname);
return 0;
}
int system_bridge_delif(struct device *bridge, struct device *dev)
{
D(SYSTEM, "brctl delif %s %s\n", bridge->ifname, dev->ifname);
return 0;
}
int system_vlan_add(struct device *dev, int id)
{
D(SYSTEM, "vconfig add %s %d\n", dev->ifname, id);
return 0;
}
int system_vlan_del(struct device *dev)
{
D(SYSTEM, "vconfig rem %s\n", dev->ifname);
return 0;
}
bool system_if_force_external(const char *ifname)
{
return false;
}
int system_if_up(struct device *dev)
{
D(SYSTEM, "ifconfig %s up\n", dev->ifname);
return 0;
}
int system_if_down(struct device *dev)
{
D(SYSTEM, "ifconfig %s down\n", dev->ifname);
return 0;
}
void system_if_get_settings(struct device *dev, struct device_settings *s)
{
}
void system_if_clear_state(struct device *dev)
{
}
int system_if_check(struct device *dev)
{
dev->ifindex = 0;
device_set_present(dev, true);
device_set_link(dev, true);
return 0;
}
int system_if_resolve(struct device *dev)
{
return 0;
}
struct device *
system_if_get_parent(struct device *dev)
{
return NULL;
}
int
system_if_dump_info(struct device *dev, struct blob_buf *b)
{
blobmsg_add_u8(b, "link", dev->present);
return 0;
}
int
system_if_dump_stats(struct device *dev, struct blob_buf *b)
{
return 0;
}
void
system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned int apply_mask)
{
}
static int system_address_msg(struct device *dev, struct device_addr *addr, const char *type)
{
char ipaddr[64];
int af = system_get_addr_family(addr->flags);
D(SYSTEM, "ifconfig %s %s %s/%d\n",
dev->ifname, type, inet_ntop(af, &addr->addr.in, ipaddr, sizeof(ipaddr)),
addr->mask);
return 0;
}
int system_add_address(struct device *dev, struct device_addr *addr)
{
return system_address_msg(dev, addr, "add");
}
int system_del_address(struct device *dev, struct device_addr *addr)
{
return system_address_msg(dev, addr, "del");
}
static int system_route_msg(struct device *dev, struct device_route *route, const char *type)
{
char addr[64], gw[64] = " gw ", devstr[64] = "";
int af = system_get_addr_family(route->flags);
int alen = system_get_addr_len(route->flags);
static uint32_t zero_addr[4];
if ((route->flags & DEVADDR_FAMILY) != DEVADDR_INET4)
return -1;
if (!route->mask)
sprintf(addr, "default");
else
inet_ntop(af, &route->addr.in, addr, sizeof(addr));
if (memcmp(&route->nexthop.in, (void *) zero_addr, alen) != 0)
inet_ntop(af, &route->nexthop.in, gw + 4, sizeof(gw) - 4);
else
gw[0] = 0;
if (dev)
sprintf(devstr, " dev %s", dev->ifname);
if (route->metric > 0)
sprintf(devstr, " metric %d", route->metric);
D(SYSTEM, "route %s %s%s%s\n", type, addr, gw, devstr);
return 0;
}
int system_add_route(struct device *dev, struct device_route *route)
{
return system_route_msg(dev, route, "add");
}
int system_del_route(struct device *dev, struct device_route *route)
{
return system_route_msg(dev, route, "del");
}
int system_flush_routes(void)
{
return 0;
}
bool system_resolve_rt_type(const char *type, unsigned int *id)
{
*id = 0;
return true;
}
bool system_resolve_rt_table(const char *name, unsigned int *id)
{
*id = 0;
return true;
}
bool system_is_default_rt_table(unsigned int id)
{
return true;
}
bool system_resolve_rpfilter(const char *filter, unsigned int *id)
{
*id = 0;
return true;
}
int system_add_iprule(struct iprule *rule)
{
return 0;
}
int system_del_iprule(struct iprule *rule)
{
return 0;
}
int system_flush_iprules(void)
{
return 0;
}
bool system_resolve_iprule_action(const char *action, unsigned int *id)
{
*id = 0;
return true;
}
time_t system_get_rtime(void)
{
struct timeval tv;
if (gettimeofday(&tv, NULL) == 0)
return tv.tv_sec;
return 0;
}
int system_del_ip_tunnel(const char *name, struct blob_attr *attr)
{
return 0;
}
int system_add_ip_tunnel(const char *name, struct blob_attr *attr)
{
return 0;
}
int system_update_ipv6_mtu(struct device *dev, int mtu)
{
return 0;
}
int system_macvlan_add(struct device *macvlan, struct device *dev, struct macvlan_config *cfg)
{
return 0;
}
int system_macvlan_del(struct device *macvlan)
{
return 0;
}
int system_vlandev_add(struct device *vlandev, struct device *dev, struct vlandev_config *cfg)
{
return 0;
}
int system_vlandev_del(struct device *vlandev)
{
return 0;
}

2687
src/3P/netifd/system-linux.c Normal file

File diff suppressed because it is too large Load Diff

43
src/3P/netifd/system.c Normal file
View File

@@ -0,0 +1,43 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "netifd.h"
#include "system.h"
#include <fcntl.h>
static const struct blobmsg_policy tunnel_attrs[__TUNNEL_ATTR_MAX] = {
[TUNNEL_ATTR_TYPE] = { .name = "mode", .type = BLOBMSG_TYPE_STRING },
[TUNNEL_ATTR_LOCAL] = { .name = "local", .type = BLOBMSG_TYPE_STRING },
[TUNNEL_ATTR_REMOTE] = { .name = "remote", .type = BLOBMSG_TYPE_STRING },
[TUNNEL_ATTR_MTU] = { .name = "mtu", .type = BLOBMSG_TYPE_INT32 },
[TUNNEL_ATTR_DF] = { .name = "df", .type = BLOBMSG_TYPE_BOOL },
[TUNNEL_ATTR_TTL] = { .name = "ttl", .type = BLOBMSG_TYPE_INT32 },
[TUNNEL_ATTR_TOS] = { .name = "tos", .type = BLOBMSG_TYPE_STRING },
[TUNNEL_ATTR_6RD_PREFIX] = {.name = "6rd-prefix", .type = BLOBMSG_TYPE_STRING },
[TUNNEL_ATTR_6RD_RELAY_PREFIX] = { .name = "6rd-relay-prefix", .type = BLOBMSG_TYPE_STRING },
[TUNNEL_ATTR_LINK] = { .name = "link", .type = BLOBMSG_TYPE_STRING },
[TUNNEL_ATTR_FMRS] = { .name = "fmrs", .type = BLOBMSG_TYPE_ARRAY },
[TUNNEL_ATTR_INFO] = { .name = "info", .type = BLOBMSG_TYPE_STRING },
};
const struct uci_blob_param_list tunnel_attr_list = {
.n_params = __TUNNEL_ATTR_MAX,
.params = tunnel_attrs,
};
void system_fd_set_cloexec(int fd)
{
#ifdef FD_CLOEXEC
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
#endif
}

168
src/3P/netifd/system.h Normal file
View File

@@ -0,0 +1,168 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __NETIFD_SYSTEM_H
#define __NETIFD_SYSTEM_H
#include <sys/time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "device.h"
#include "interface-ip.h"
#include "iprule.h"
enum tunnel_param {
TUNNEL_ATTR_TYPE,
TUNNEL_ATTR_REMOTE,
TUNNEL_ATTR_LOCAL,
TUNNEL_ATTR_MTU,
TUNNEL_ATTR_DF,
TUNNEL_ATTR_TTL,
TUNNEL_ATTR_TOS,
TUNNEL_ATTR_6RD_PREFIX,
TUNNEL_ATTR_6RD_RELAY_PREFIX,
TUNNEL_ATTR_LINK,
TUNNEL_ATTR_FMRS,
TUNNEL_ATTR_INFO,
__TUNNEL_ATTR_MAX
};
extern const struct uci_blob_param_list tunnel_attr_list;
enum bridge_opt {
/* stp and forward delay always set */
BRIDGE_OPT_AGEING_TIME = (1 << 0),
BRIDGE_OPT_HELLO_TIME = (1 << 1),
BRIDGE_OPT_MAX_AGE = (1 << 2),
BRIDGE_OPT_ROBUSTNESS = (1 << 3),
BRIDGE_OPT_QUERY_INTERVAL = (1 << 4),
BRIDGE_OPT_QUERY_RESPONSE_INTERVAL = (1 << 5),
BRIDGE_OPT_LAST_MEMBER_INTERVAL = (1 << 6),
};
struct bridge_config {
enum bridge_opt flags;
bool stp;
bool igmp_snoop;
bool multicast_querier;
int robustness;
int query_interval;
int query_response_interval;
int last_member_interval;
unsigned short priority;
int forward_delay;
bool bridge_empty;
int ageing_time;
int hello_time;
int max_age;
int hash_max;
};
enum macvlan_opt {
MACVLAN_OPT_MACADDR = (1 << 0),
};
struct macvlan_config {
const char *mode;
enum macvlan_opt flags;
unsigned char macaddr[6];
};
enum vlan_proto {
VLAN_PROTO_8021Q = 0x8100,
VLAN_PROTO_8021AD = 0x88A8
};
struct vlandev_config {
enum vlan_proto proto;
uint16_t vid;
};
static inline int system_get_addr_family(unsigned int flags)
{
if ((flags & DEVADDR_FAMILY) == DEVADDR_INET6)
return AF_INET6;
else
return AF_INET;
}
static inline int system_get_addr_len(unsigned int flags)
{
if ((flags & DEVADDR_FAMILY) != DEVADDR_INET6)
return sizeof(struct in_addr);
else
return sizeof(struct in6_addr);
}
int system_init(void);
int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg);
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_macvlan_add(struct device *macvlan, struct device *dev, struct macvlan_config *cfg);
int system_macvlan_del(struct device *macvlan);
int system_vlan_add(struct device *dev, int id);
int system_vlan_del(struct device *dev);
int system_vlandev_add(struct device *vlandev, struct device *dev, struct vlandev_config *cfg);
int system_vlandev_del(struct device *vlandev);
void system_if_get_settings(struct device *dev, struct device_settings *s);
void system_if_clear_state(struct device *dev);
int system_if_up(struct device *dev);
int system_if_down(struct device *dev);
int system_if_check(struct device *dev);
int system_if_resolve(struct device *dev);
int system_if_dump_info(struct device *dev, struct blob_buf *b);
int system_if_dump_stats(struct device *dev, struct blob_buf *b);
struct device *system_if_get_parent(struct device *dev);
bool system_if_force_external(const char *ifname);
void system_if_apply_settings(struct device *dev, struct device_settings *s,
unsigned int apply_mask);
int system_add_address(struct device *dev, struct device_addr *addr);
int system_del_address(struct device *dev, struct device_addr *addr);
int system_add_route(struct device *dev, struct device_route *route);
int system_del_route(struct device *dev, struct device_route *route);
int system_flush_routes(void);
bool system_resolve_rt_type(const char *type, unsigned int *id);
bool system_resolve_rt_table(const char *name, unsigned int *id);
bool system_is_default_rt_table(unsigned int id);
bool system_resolve_rpfilter(const char *filter, unsigned int *id);
int system_del_ip_tunnel(const char *name, struct blob_attr *attr);
int system_add_ip_tunnel(const char *name, struct blob_attr *attr);
int system_add_iprule(struct iprule *rule);
int system_del_iprule(struct iprule *rule);
int system_flush_iprules(void);
bool system_resolve_iprule_action(const char *action, unsigned int *id);
time_t system_get_rtime(void);
void system_fd_set_cloexec(int fd);
int system_update_ipv6_mtu(struct device *device, int mtu);
#endif

99
src/3P/netifd/tunnel.c Normal file
View File

@@ -0,0 +1,99 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "netifd.h"
#include "device.h"
#include "config.h"
#include "system.h"
struct tunnel {
struct device dev;
device_state_cb set_state;
};
static int
tunnel_set_state(struct device *dev, bool up)
{
struct tunnel *tun = container_of(dev, struct tunnel, dev);
int ret;
if (up) {
ret = system_add_ip_tunnel(dev->ifname, dev->config);
if (ret != 0)
return ret;
}
ret = tun->set_state(dev, up);
if (ret || !up)
system_del_ip_tunnel(dev->ifname, dev->config);
return ret;
}
static enum dev_change_type
tunnel_reload(struct device *dev, struct blob_attr *attr)
{
struct blob_attr *tb_dev[__DEV_ATTR_MAX];
const struct uci_blob_param_list *cfg = dev->type->config_params;
if (uci_blob_check_equal(dev->config, attr, cfg))
return DEV_CONFIG_NO_CHANGE;
memset(tb_dev, 0, sizeof(tb_dev));
if (attr)
blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, tb_dev,
blob_data(attr), blob_len(attr));
device_init_settings(dev, tb_dev);
return DEV_CONFIG_RESTART;
}
static struct device *
tunnel_create(const char *name, struct blob_attr *attr)
{
struct tunnel *tun;
struct device *dev;
tun = calloc(1, sizeof(*tun));
if (!tun)
return NULL;
dev = &tun->dev;
device_init(dev, &tunnel_device_type, name);
tun->set_state = dev->set_state;
dev->set_state = tunnel_set_state;
device_apply_config(dev, &tunnel_device_type, attr);
device_set_present(dev, true);
return dev;
}
static void
tunnel_free(struct device *dev)
{
struct tunnel *tun = container_of(dev, struct tunnel, dev);
free(tun);
}
const struct device_type tunnel_device_type = {
.name = "IP tunnel",
.config_params = &tunnel_attr_list,
.reload = tunnel_reload,
.create = tunnel_create,
.free = tunnel_free,
};

1216
src/3P/netifd/ubus.c Normal file

File diff suppressed because it is too large Load Diff

26
src/3P/netifd/ubus.h Normal file
View File

@@ -0,0 +1,26 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __NETIFD_UBUS_H
#define __NETIFD_UBUS_H
extern struct ubus_context *ubus_ctx;
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);
void netifd_ubus_interface_event(struct interface *iface, bool up);
void netifd_ubus_interface_notify(struct interface *iface, bool up);
#endif

234
src/3P/netifd/utils.c Normal file
View File

@@ -0,0 +1,234 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <string.h>
#include <stdlib.h>
#include "utils.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#ifdef __APPLE__
#include <libproc.h>
#endif
void
__vlist_simple_init(struct vlist_simple_tree *tree, int offset)
{
INIT_LIST_HEAD(&tree->list);
tree->version = 1;
tree->head_offset = offset;
}
void
vlist_simple_delete(struct vlist_simple_tree *tree, struct vlist_simple_node *node)
{
char *ptr;
list_del(&node->list);
ptr = (char *) node - tree->head_offset;
free(ptr);
}
void
vlist_simple_flush(struct vlist_simple_tree *tree)
{
struct vlist_simple_node *n, *tmp;
list_for_each_entry_safe(n, tmp, &tree->list, list) {
if ((n->version == tree->version || n->version == -1) &&
tree->version != -1)
continue;
vlist_simple_delete(tree, n);
}
}
void
vlist_simple_replace(struct vlist_simple_tree *dest, struct vlist_simple_tree *old)
{
struct vlist_simple_node *n, *tmp;
vlist_simple_update(dest);
list_for_each_entry_safe(n, tmp, &old->list, list) {
list_del(&n->list);
vlist_simple_add(dest, n);
}
vlist_simple_flush(dest);
}
void
vlist_simple_flush_all(struct vlist_simple_tree *tree)
{
tree->version = -1;
vlist_simple_flush(tree);
}
unsigned int
parse_netmask_string(const char *str, bool v6)
{
struct in_addr addr;
unsigned int ret;
char *err = NULL;
if (!strchr(str, '.')) {
ret = strtoul(str, &err, 0);
if (err && *err)
goto error;
return ret;
}
if (v6)
goto error;
if (inet_aton(str, &addr) != 1)
goto error;
return 32 - fls(~(ntohl(addr.s_addr)));
error:
return ~0;
}
bool
split_netmask(char *str, unsigned int *netmask, bool v6)
{
char *delim = strchr(str, '/');
if (delim) {
*(delim++) = 0;
*netmask = parse_netmask_string(delim, v6);
}
return true;
}
int
parse_ip_and_netmask(int af, const char *str, void *addr, unsigned int *netmask)
{
char *astr = alloca(strlen(str) + 1);
int ret = 0;
strcpy(astr, str);
if (!split_netmask(astr, netmask, af == AF_INET6))
return 0;
if (af == AF_INET6) {
if (*netmask > 128)
return 0;
} else {
if (*netmask > 32)
return 0;
}
ret = inet_pton(af, astr, addr);
if (ret > 0) {
if (af == AF_INET) {
struct in_addr *ip4_addr = (struct in_addr *)addr;
uint32_t host_addr = ntohl(ip4_addr->s_addr);
if (IN_EXPERIMENTAL(host_addr)) {
return 0;
}
}
else if (af == AF_INET6) {
if (IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr)) {
return 0;
}
}
}
return ret;
}
char *
format_macaddr(uint8_t *mac)
{
static char str[sizeof("ff:ff:ff:ff:ff:ff ")];
snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return str;
}
uint32_t
crc32_file(FILE *fp)
{
static uint32_t *crcvals = NULL;
if (!crcvals) {
crcvals = malloc(sizeof(*crcvals) * 256);
for (size_t i = 0; i < 256; ++i) {
uint32_t c = i;
for (size_t j = 0; j < 8; ++j)
c = (c & 1) ? (0xEDB88320 ^ (c >> 1)) : (c >> 1);
crcvals[i] = c;
}
}
uint8_t buf[1024];
size_t len;
uint32_t c = 0xFFFFFFFF;
do {
len = fread(buf, 1, sizeof(buf), fp);
for (size_t i = 0; i < len; ++i)
c = crcvals[(c ^ buf[i]) & 0xFF] ^ (c >> 8);
} while (len == sizeof(buf));
return c ^ 0xFFFFFFFF;
}
bool check_pid_path(int pid, const char *exe)
{
int proc_exe_len;
int exe_len = strlen(exe);
#ifdef __APPLE__
char proc_exe_buf[PROC_PIDPATHINFO_SIZE];
proc_exe_len = proc_pidpath(pid, proc_exe_buf, sizeof(proc_exe_buf));
#else
char proc_exe[32];
char *proc_exe_buf = alloca(exe_len);
sprintf(proc_exe, "/proc/%d/exe", pid);
proc_exe_len = readlink(proc_exe, proc_exe_buf, exe_len);
#endif
if (proc_exe_len != exe_len)
return false;
return !memcmp(exe, proc_exe_buf, exe_len);
}
static const char * const uci_validate_name[__BLOBMSG_TYPE_LAST] = {
[BLOBMSG_TYPE_STRING] = "string",
[BLOBMSG_TYPE_ARRAY] = "list(string)",
[BLOBMSG_TYPE_INT32] = "uinteger",
[BLOBMSG_TYPE_BOOL] = "bool",
};
const char*
uci_get_validate_string(const struct uci_blob_param_list *p, int i)
{
if (p->validate[i])
return p->validate[i];
else if (uci_validate_name[p->params[i].type])
return uci_validate_name[p->params[i].type];
return p->validate[BLOBMSG_TYPE_STRING];
}

122
src/3P/netifd/utils.h Normal file
View File

@@ -0,0 +1,122 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __NETIFD_UTILS_H
#define __NETIFD_UTILS_H
#include <unistd.h>
#include <stdio.h>
#include <uci_blob.h>
#include <libubox/list.h>
#include <libubox/avl.h>
#include <libubox/avl-cmp.h>
#include <libubox/blobmsg.h>
#include <libubox/vlist.h>
#include <libubox/utils.h>
static inline bool blobmsg_get_bool_default(struct blob_attr *attr, bool val)
{
if (!attr)
return val;
return blobmsg_get_bool(attr);
}
#define __init __attribute__((constructor))
struct vlist_simple_tree {
struct list_head list;
int head_offset;
int version;
};
struct vlist_simple_node {
struct list_head list;
int version;
};
#define vlist_for_each_element_safe(tree, element, node_member, ptr) \
avl_for_each_element_safe(&(tree)->avl, element, node_member.avl, ptr)
#define vlist_simple_init(tree, node, member) \
__vlist_simple_init(tree, offsetof(node, member))
void __vlist_simple_init(struct vlist_simple_tree *tree, int offset);
void vlist_simple_delete(struct vlist_simple_tree *tree, struct vlist_simple_node *node);
void vlist_simple_flush(struct vlist_simple_tree *tree);
void vlist_simple_flush_all(struct vlist_simple_tree *tree);
void vlist_simple_replace(struct vlist_simple_tree *dest, struct vlist_simple_tree *old);
static inline void vlist_simple_update(struct vlist_simple_tree *tree)
{
tree->version++;
}
static inline void vlist_simple_add(struct vlist_simple_tree *tree, struct vlist_simple_node *node)
{
node->version = tree->version;
list_add_tail(&node->list, &tree->list);
}
#define vlist_simple_for_each_element(tree, element, node_member) \
list_for_each_entry(element, &(tree)->list, node_member.list)
#define vlist_simple_empty(tree) \
list_empty(&(tree)->list)
#ifdef __linux__
static inline int fls(int x)
{
int r = 32;
if (!x)
return 0;
if (!(x & 0xffff0000u)) {
x <<= 16;
r -= 16;
}
if (!(x & 0xff000000u)) {
x <<= 8;
r -= 8;
}
if (!(x & 0xf0000000u)) {
x <<= 4;
r -= 4;
}
if (!(x & 0xc0000000u)) {
x <<= 2;
r -= 2;
}
if (!(x & 0x80000000u))
r -= 1;
return r;
}
#endif
unsigned int parse_netmask_string(const char *str, bool v6);
bool split_netmask(char *str, unsigned int *netmask, bool v6);
int parse_ip_and_netmask(int af, const char *str, void *addr, unsigned int *netmask);
bool check_pid_path(int pid, const char *exe);
char * format_macaddr(uint8_t *mac);
uint32_t crc32_file(FILE *fp);
const char * uci_get_validate_string(const struct uci_blob_param_list *c, int i);
#ifdef __APPLE__
#define s6_addr32 __u6_addr.__u6_addr32
#endif
#endif

192
src/3P/netifd/vlan.c Normal file
View File

@@ -0,0 +1,192 @@
/*
* netifd - network interface daemon
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#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);
device_remove_user(&vldev->dep);
device_cleanup(&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);
device_release(&vldev->dep);
return 0;
}
ret = device_claim(&vldev->dep);
if (ret < 0)
return ret;
system_vlan_add(vldev->dep.dev, vldev->id);
ret = vldev->set_state(dev, true);
if (ret)
device_release(&vldev->dep);
return ret;
}
static void vlan_dev_set_name(struct vlan_device *vldev, struct device *dev)
{
char name[IFNAMSIZ];
vldev->dev.hidden = dev->hidden;
snprintf(name, IFNAMSIZ, "%s.%d", dev->ifname, vldev->id);
device_set_ifname(&vldev->dev, name);
}
static void vlan_dev_cb(struct device_user *dep, enum device_event ev)
{
struct vlan_device *vldev;
bool new_state = false;
vldev = container_of(dep, struct vlan_device, dep);
switch(ev) {
case DEV_EVENT_ADD:
new_state = true;
case DEV_EVENT_REMOVE:
device_set_present(&vldev->dev, new_state);
break;
case DEV_EVENT_UPDATE_IFNAME:
vlan_dev_set_name(vldev, dep->dev);
break;
case DEV_EVENT_TOPO_CHANGE:
/* Propagate topo changes */
device_broadcast_event(&vldev->dev, DEV_EVENT_TOPO_CHANGE);
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",
.config_params = &device_attr_list,
.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, list.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;
D(DEVICE, "Create vlan device '%s.%d'\n", dev->ifname, id);
vldev = calloc(1, sizeof(*vldev));
if (!vldev)
return NULL;
vldev->id = id;
device_init(&vldev->dev, &vlan_type, NULL);
vlan_dev_set_name(vldev, dev);
vldev->dev.default_config = true;
vldev->set_state = vldev->dev.set_state;
vldev->dev.set_state = vlan_set_device_state;
vldev->dep.cb = vlan_dev_cb;
device_add_user(&vldev->dep, dev);
return &vldev->dev;
}
static 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 *dev = NULL;
char *buf, *s, *next, *err = NULL;
int id;
buf = strdup(ifname);
if (!buf)
return NULL;
s = split_vlan(buf);
dev = device_get(buf, create);
if (!dev)
goto error;
do {
next = split_vlan(s);
id = strtoul(s, &err, 10);
if (err && *err)
goto error;
dev = get_vlan_device(dev, id, create);
if (!dev)
goto error;
s = next;
if (!s)
goto out;
} while (1);
error:
dev = NULL;
out:
free(buf);
return dev;
}

251
src/3P/netifd/vlandev.c Normal file
View File

@@ -0,0 +1,251 @@
/*
* netifd - network interface daemon
* Copyright (C) 2014 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <string.h>
#include "netifd.h"
#include "device.h"
#include "interface.h"
#include "system.h"
enum {
VLANDEV_ATTR_TYPE,
VLANDEV_ATTR_IFNAME,
VLANDEV_ATTR_VID,
__VLANDEV_ATTR_MAX
};
static const struct blobmsg_policy vlandev_attrs[__VLANDEV_ATTR_MAX] = {
[VLANDEV_ATTR_TYPE] = { "type", BLOBMSG_TYPE_STRING },
[VLANDEV_ATTR_IFNAME] = { "ifname", BLOBMSG_TYPE_STRING },
[VLANDEV_ATTR_VID] = { "vid", BLOBMSG_TYPE_INT32 },
};
static const struct uci_blob_param_list vlandev_attr_list = {
.n_params = __VLANDEV_ATTR_MAX,
.params = vlandev_attrs,
.n_next = 1,
.next = { &device_attr_list },
};
struct vlandev_device {
struct device dev;
struct device_user parent;
device_state_cb set_state;
struct blob_attr *config_data;
struct blob_attr *ifname;
struct vlandev_config config;
};
static void
vlandev_base_cb(struct device_user *dev, enum device_event ev)
{
struct vlandev_device *mvdev = container_of(dev, struct vlandev_device, parent);
switch (ev) {
case DEV_EVENT_ADD:
device_set_present(&mvdev->dev, true);
break;
case DEV_EVENT_REMOVE:
device_set_present(&mvdev->dev, false);
break;
default:
return;
}
}
static int
vlandev_set_down(struct vlandev_device *mvdev)
{
mvdev->set_state(&mvdev->dev, false);
system_vlandev_del(&mvdev->dev);
device_release(&mvdev->parent);
return 0;
}
static int
vlandev_set_up(struct vlandev_device *mvdev)
{
int ret;
ret = device_claim(&mvdev->parent);
if (ret < 0)
return ret;
ret = system_vlandev_add(&mvdev->dev, mvdev->parent.dev, &mvdev->config);
if (ret < 0)
goto release;
ret = mvdev->set_state(&mvdev->dev, true);
if (ret)
goto delete;
return 0;
delete:
system_vlandev_del(&mvdev->dev);
release:
device_release(&mvdev->parent);
return ret;
}
static int
vlandev_set_state(struct device *dev, bool up)
{
struct vlandev_device *mvdev;
D(SYSTEM, "vlandev_set_state(%s, %u)\n", dev->ifname, up);
mvdev = container_of(dev, struct vlandev_device, dev);
if (up)
return vlandev_set_up(mvdev);
else
return vlandev_set_down(mvdev);
}
static void
vlandev_free(struct device *dev)
{
struct vlandev_device *mvdev;
mvdev = container_of(dev, struct vlandev_device, dev);
device_remove_user(&mvdev->parent);
free(mvdev->config_data);
free(mvdev);
}
static void
vlandev_dump_info(struct device *dev, struct blob_buf *b)
{
struct vlandev_device *mvdev;
mvdev = container_of(dev, struct vlandev_device, dev);
blobmsg_add_string(b, "parent", mvdev->parent.dev->ifname);
system_if_dump_info(dev, b);
}
static void
vlandev_config_init(struct device *dev)
{
struct vlandev_device *mvdev;
struct device *basedev = NULL;
mvdev = container_of(dev, struct vlandev_device, dev);
if (mvdev->ifname)
basedev = device_get(blobmsg_data(mvdev->ifname), true);
device_add_user(&mvdev->parent, basedev);
}
static void
vlandev_apply_settings(struct vlandev_device *mvdev, struct blob_attr **tb)
{
struct vlandev_config *cfg = &mvdev->config;
struct blob_attr *cur;
cfg->proto = VLAN_PROTO_8021Q;
cfg->vid = 1;
if ((cur = tb[VLANDEV_ATTR_TYPE]))
{
if(!strcmp(blobmsg_data(cur), "8021ad"))
cfg->proto = VLAN_PROTO_8021AD;
}
if ((cur = tb[VLANDEV_ATTR_VID]))
cfg->vid = (uint16_t) blobmsg_get_u32(cur);
}
static enum dev_change_type
vlandev_reload(struct device *dev, struct blob_attr *attr)
{
struct blob_attr *tb_dev[__DEV_ATTR_MAX];
struct blob_attr *tb_mv[__VLANDEV_ATTR_MAX];
enum dev_change_type ret = DEV_CONFIG_APPLIED;
struct vlandev_device *mvdev;
mvdev = container_of(dev, struct vlandev_device, dev);
attr = blob_memdup(attr);
blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, tb_dev,
blob_data(attr), blob_len(attr));
blobmsg_parse(vlandev_attrs, __VLANDEV_ATTR_MAX, tb_mv,
blob_data(attr), blob_len(attr));
device_init_settings(dev, tb_dev);
vlandev_apply_settings(mvdev, tb_mv);
mvdev->ifname = tb_mv[VLANDEV_ATTR_IFNAME];
if (mvdev->config_data) {
struct blob_attr *otb_dev[__DEV_ATTR_MAX];
struct blob_attr *otb_mv[__VLANDEV_ATTR_MAX];
blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, otb_dev,
blob_data(mvdev->config_data), blob_len(mvdev->config_data));
if (uci_blob_diff(tb_dev, otb_dev, &device_attr_list, NULL))
ret = DEV_CONFIG_RESTART;
blobmsg_parse(vlandev_attrs, __VLANDEV_ATTR_MAX, otb_mv,
blob_data(mvdev->config_data), blob_len(mvdev->config_data));
if (uci_blob_diff(tb_mv, otb_mv, &vlandev_attr_list, NULL))
ret = DEV_CONFIG_RESTART;
vlandev_config_init(dev);
}
free(mvdev->config_data);
mvdev->config_data = attr;
return ret;
}
static struct device *
vlandev_create(const char *name, struct blob_attr *attr)
{
struct vlandev_device *mvdev;
struct device *dev = NULL;
mvdev = calloc(1, sizeof(*mvdev));
if (!mvdev)
return NULL;
dev = &mvdev->dev;
device_init(dev, &vlandev_device_type, name);
dev->config_pending = true;
mvdev->set_state = dev->set_state;
dev->set_state = vlandev_set_state;
dev->hotplug_ops = NULL;
mvdev->parent.cb = vlandev_base_cb;
vlandev_reload(dev, attr);
return dev;
}
const struct device_type vlandev_device_type = {
.name = "VLANDEV",
.config_params = &vlandev_attr_list,
.create = vlandev_create,
.config_init = vlandev_config_init,
.reload = vlandev_reload,
.free = vlandev_free,
.dump_info = vlandev_dump_info,
};

996
src/3P/netifd/wireless.c Normal file
View File

@@ -0,0 +1,996 @@
/*
* netifd - network interface daemon
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <signal.h>
#include "netifd.h"
#include "wireless.h"
#include "handler.h"
#include "ubus.h"
#define WIRELESS_SETUP_RETRY 3
struct vlist_tree wireless_devices;
struct avl_tree wireless_drivers;
static struct blob_buf b;
static int drv_fd;
static const struct blobmsg_policy wdev_policy =
{ .name = "disabled", .type = BLOBMSG_TYPE_BOOL };
static const struct uci_blob_param_list wdev_param = {
.n_params = 1,
.params = &wdev_policy,
};
enum {
VIF_ATTR_DISABLED,
VIF_ATTR_NETWORK,
VIF_ATTR_ISOLATE,
VIF_ATTR_MODE,
__VIF_ATTR_MAX,
};
static const struct blobmsg_policy vif_policy[__VIF_ATTR_MAX] = {
[VIF_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL },
[VIF_ATTR_NETWORK] = { .name = "network", .type = BLOBMSG_TYPE_ARRAY },
[VIF_ATTR_ISOLATE] = { .name = "isolate", .type = BLOBMSG_TYPE_BOOL },
[VIF_ATTR_MODE] = { .name = "mode", .type = BLOBMSG_TYPE_STRING },
};
static const struct uci_blob_param_list vif_param = {
.n_params = ARRAY_SIZE(vif_policy),
.params = vif_policy,
};
static void
put_container(struct blob_buf *buf, struct blob_attr *attr, const char *name)
{
void *c = blobmsg_open_table(buf, name);
blob_put_raw(buf, blob_data(attr), blob_len(attr));
blobmsg_close_table(buf, c);
}
static void
vif_config_add_bridge(struct blob_buf *buf, struct blob_attr *networks, bool prepare)
{
struct interface *iface;
struct device *dev = NULL;
struct blob_attr *cur;
const char *network;
int rem;
if (!networks)
return;
blobmsg_for_each_attr(cur, networks, rem) {
network = blobmsg_data(cur);
iface = vlist_find(&interfaces, network, iface, node);
if (!iface)
continue;
dev = iface->main_dev.dev;
if (!dev)
return;
if (dev->type != &bridge_device_type)
return;
}
if (!dev)
return;
if (dev->hotplug_ops && dev->hotplug_ops->prepare)
dev->hotplug_ops->prepare(dev);
blobmsg_add_string(buf, "bridge", dev->ifname);
if (dev->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST)
blobmsg_add_u8(buf, "multicast_to_unicast",
dev->settings.multicast_to_unicast);
}
static void
prepare_config(struct wireless_device *wdev, struct blob_buf *buf, bool up)
{
struct wireless_interface *vif;
void *l, *i;
blob_buf_init(&b, 0);
put_container(&b, wdev->config, "config");
if (wdev->data)
blobmsg_add_blob(&b, wdev->data);
l = blobmsg_open_table(&b, "interfaces");
vlist_for_each_element(&wdev->interfaces, vif, node) {
i = blobmsg_open_table(&b, vif->name);
vif_config_add_bridge(&b, vif->network, up);
put_container(&b, vif->config, "config");
if (vif->data)
blobmsg_add_blob(&b, vif->data);
blobmsg_close_table(&b, i);
}
blobmsg_close_table(&b, l);
}
static bool
wireless_process_check(struct wireless_process *proc)
{
return check_pid_path(proc->pid, proc->exe);
}
static void
wireless_complete_kill_request(struct wireless_device *wdev)
{
if (!wdev->kill_request)
return;
ubus_complete_deferred_request(ubus_ctx, wdev->kill_request, 0);
free(wdev->kill_request);
wdev->kill_request = NULL;
}
static void
wireless_process_free(struct wireless_device *wdev, struct wireless_process *proc)
{
D(WIRELESS, "Wireless device '%s' free pid %d\n", wdev->name, proc->pid);
list_del(&proc->list);
free(proc);
if (list_empty(&wdev->script_proc))
wireless_complete_kill_request(wdev);
}
static void
wireless_close_script_proc_fd(struct wireless_device *wdev)
{
if (wdev->script_proc_fd.fd < 0)
return;
uloop_fd_delete(&wdev->script_proc_fd);
close(wdev->script_proc_fd.fd);
wdev->script_proc_fd.fd = -1;
}
static void
wireless_process_kill_all(struct wireless_device *wdev, int signal, bool free)
{
struct wireless_process *proc, *tmp;
list_for_each_entry_safe(proc, tmp, &wdev->script_proc, list) {
bool check = wireless_process_check(proc);
if (check) {
D(WIRELESS, "Wireless device '%s' kill pid %d\n", wdev->name, proc->pid);
kill(proc->pid, signal);
}
if (free || !check)
wireless_process_free(wdev, proc);
}
if (free)
wireless_close_script_proc_fd(wdev);
}
static void
wireless_device_free_state(struct wireless_device *wdev)
{
struct wireless_interface *vif;
uloop_timeout_cancel(&wdev->script_check);
uloop_timeout_cancel(&wdev->timeout);
wireless_complete_kill_request(wdev);
free(wdev->data);
wdev->data = NULL;
vlist_for_each_element(&wdev->interfaces, vif, node) {
free(vif->data);
vif->data = NULL;
vif->ifname = NULL;
}
}
static void wireless_interface_handle_link(struct wireless_interface *vif, bool up)
{
struct interface *iface;
struct blob_attr *cur;
const char *network;
int rem;
if (!vif->network || !vif->ifname)
return;
if (up) {
struct device *dev = device_get(vif->ifname, 2);
if (dev) {
dev->wireless_isolate = vif->isolate;
dev->wireless = true;
dev->wireless_ap = vif->ap_mode;
}
}
blobmsg_for_each_attr(cur, vif->network, rem) {
network = blobmsg_data(cur);
iface = vlist_find(&interfaces, network, iface, node);
if (!iface)
continue;
interface_handle_link(iface, vif->ifname, up, true);
}
}
static void
wireless_device_setup_cancel(struct wireless_device *wdev)
{
if (wdev->cancel)
return;
D(WIRELESS, "Cancel wireless device '%s' setup\n", wdev->name);
wdev->cancel = true;
uloop_timeout_set(&wdev->timeout, 10 * 1000);
}
static void
wireless_device_run_handler(struct wireless_device *wdev, bool up)
{
const char *action = up ? "setup" : "teardown";
const char *argv[6];
char *config;
int i = 0;
int fds[2] = { -1, -1 };
D(WIRELESS, "Wireless device '%s' run %s handler\n", wdev->name, action);
if (!up && wdev->prev_config) {
config = blobmsg_format_json(wdev->prev_config, true);
free(wdev->prev_config);
wdev->prev_config = NULL;
} else {
prepare_config(wdev, &b, up);
config = blobmsg_format_json(b.head, true);
}
argv[i++] = wdev->drv->script;
argv[i++] = wdev->drv->name;
argv[i++] = action;
argv[i++] = wdev->name;
argv[i++] = config;
argv[i] = NULL;
if (up && pipe(fds) == 0) {
wdev->script_proc_fd.fd = fds[0];
uloop_fd_add(&wdev->script_proc_fd,
ULOOP_READ | ULOOP_EDGE_TRIGGER);
}
netifd_start_process(argv, NULL, &wdev->script_task);
if (fds[1] >= 0)
close(fds[1]);
free(config);
}
static void
__wireless_device_set_up(struct wireless_device *wdev)
{
if (wdev->disabled)
return;
if (wdev->state != IFS_DOWN || config_init)
return;
free(wdev->prev_config);
wdev->prev_config = NULL;
wdev->state = IFS_SETUP;
wireless_device_run_handler(wdev, true);
}
static void
wireless_device_free(struct wireless_device *wdev)
{
vlist_flush_all(&wdev->interfaces);
avl_delete(&wireless_devices.avl, &wdev->node.avl);
free(wdev->config);
free(wdev->prev_config);
free(wdev);
}
static void
wdev_handle_config_change(struct wireless_device *wdev)
{
enum interface_config_state state = wdev->config_state;
switch(state) {
case IFC_NORMAL:
case IFC_RELOAD:
wdev->config_state = IFC_NORMAL;
if (wdev->autostart)
__wireless_device_set_up(wdev);
break;
case IFC_REMOVE:
wireless_device_free(wdev);
break;
}
}
static void
wireless_device_mark_down(struct wireless_device *wdev)
{
struct wireless_interface *vif;
D(WIRELESS, "Wireless device '%s' is now down\n", wdev->name);
vlist_for_each_element(&wdev->interfaces, vif, node)
wireless_interface_handle_link(vif, false);
wireless_process_kill_all(wdev, SIGTERM, true);
wdev->cancel = false;
wdev->state = IFS_DOWN;
wireless_device_free_state(wdev);
wdev_handle_config_change(wdev);
}
static void
wireless_device_setup_timeout(struct uloop_timeout *timeout)
{
struct wireless_device *wdev = container_of(timeout, struct wireless_device, timeout);
netifd_kill_process(&wdev->script_task);
wdev->script_task.cb(&wdev->script_task, -1);
wireless_device_mark_down(wdev);
}
void
wireless_device_set_up(struct wireless_device *wdev)
{
wdev->retry = WIRELESS_SETUP_RETRY;
wdev->autostart = true;
__wireless_device_set_up(wdev);
}
static void
__wireless_device_set_down(struct wireless_device *wdev)
{
if (wdev->state == IFS_TEARDOWN || wdev->state == IFS_DOWN)
return;
if (wdev->script_task.uloop.pending) {
wireless_device_setup_cancel(wdev);
return;
}
wdev->state = IFS_TEARDOWN;
wireless_device_run_handler(wdev, false);
}
static void
wireless_device_mark_up(struct wireless_device *wdev)
{
struct wireless_interface *vif;
if (wdev->cancel) {
wdev->cancel = false;
__wireless_device_set_down(wdev);
return;
}
D(WIRELESS, "Wireless device '%s' is now up\n", wdev->name);
wdev->state = IFS_UP;
vlist_for_each_element(&wdev->interfaces, vif, node)
wireless_interface_handle_link(vif, true);
}
static void
wireless_device_retry_setup(struct wireless_device *wdev)
{
if (wdev->state == IFS_TEARDOWN || wdev->state == IFS_DOWN || wdev->cancel)
return;
if (--wdev->retry < 0)
wdev->autostart = false;
__wireless_device_set_down(wdev);
}
static void
wireless_device_script_task_cb(struct netifd_process *proc, int ret)
{
struct wireless_device *wdev = container_of(proc, struct wireless_device, script_task);
switch (wdev->state) {
case IFS_SETUP:
wireless_device_retry_setup(wdev);
break;
case IFS_TEARDOWN:
wireless_device_mark_down(wdev);
break;
default:
break;
}
}
void
wireless_device_set_down(struct wireless_device *wdev)
{
wdev->autostart = false;
__wireless_device_set_down(wdev);
}
static void
wdev_set_config_state(struct wireless_device *wdev, enum interface_config_state s)
{
if (wdev->config_state != IFC_NORMAL)
return;
wdev->config_state = s;
if (wdev->state == IFS_DOWN)
wdev_handle_config_change(wdev);
else
__wireless_device_set_down(wdev);
}
static void
wdev_prepare_prev_config(struct wireless_device *wdev)
{
if (wdev->prev_config)
return;
prepare_config(wdev, &b, false);
wdev->prev_config = blob_memdup(b.head);
}
static void
wdev_change_config(struct wireless_device *wdev, struct wireless_device *wd_new)
{
struct blob_attr *new_config = wd_new->config;
bool disabled = wd_new->disabled;
free(wd_new);
wdev_prepare_prev_config(wdev);
if (blob_attr_equal(wdev->config, new_config) && wdev->disabled == disabled)
return;
D(WIRELESS, "Update configuration of wireless device '%s'\n", wdev->name);
free(wdev->config);
wdev->config = blob_memdup(new_config);
wdev->disabled = disabled;
wdev_set_config_state(wdev, IFC_RELOAD);
}
static void
wdev_create(struct wireless_device *wdev)
{
wdev->retry = WIRELESS_SETUP_RETRY;
wdev->config = blob_memdup(wdev->config);
}
static void
wdev_update(struct vlist_tree *tree, struct vlist_node *node_new,
struct vlist_node *node_old)
{
struct wireless_device *wd_old = container_of(node_old, struct wireless_device, node);
struct wireless_device *wd_new = container_of(node_new, struct wireless_device, node);
if (wd_old && wd_new) {
wdev_change_config(wd_old, wd_new);
} else if (wd_old) {
D(WIRELESS, "Delete wireless device '%s'\n", wd_old->name);
wdev_set_config_state(wd_old, IFC_REMOVE);
} else if (wd_new) {
D(WIRELESS, "Create wireless device '%s'\n", wd_new->name);
wdev_create(wd_new);
}
}
static void
wireless_add_handler(const char *script, const char *name, json_object *obj)
{
struct wireless_driver *drv;
char *name_str, *script_str;
json_object *dev_config_obj, *iface_config_obj;
struct uci_blob_param_list *dev_config, *iface_config;
dev_config_obj = json_get_field(obj, "device", json_type_array);
iface_config_obj = json_get_field(obj, "iface", json_type_array);
if (!dev_config_obj || !iface_config_obj)
return;
drv = calloc_a(sizeof(*drv),
&dev_config, sizeof(*dev_config) + sizeof(void *),
&iface_config, sizeof(*iface_config) + sizeof(void *),
&name_str, strlen(name) + 1,
&script_str, strlen(script) + 1);
drv->name = strcpy(name_str, name);
drv->script = strcpy(script_str, script);
dev_config->n_next = 1;
dev_config->next[0] = &wdev_param;
drv->device.config = dev_config;
iface_config->n_next = 1;
iface_config->next[0] = &vif_param;
drv->interface.config = iface_config;
drv->device.buf = netifd_handler_parse_config(drv->device.config, dev_config_obj);
drv->interface.buf = netifd_handler_parse_config(drv->interface.config, iface_config_obj);
drv->node.key = drv->name;
avl_insert(&wireless_drivers, &drv->node);
D(WIRELESS, "Add handler for script %s: %s\n", script, name);
}
void wireless_init(void)
{
vlist_init(&wireless_devices, avl_strcmp, wdev_update);
wireless_devices.keep_old = true;
wireless_devices.no_delete = true;
avl_init(&wireless_drivers, avl_strcmp, false, NULL);
drv_fd = netifd_open_subdir("wireless");
if (drv_fd < 0)
return;
netifd_init_script_handlers(drv_fd, wireless_add_handler);
}
static void
wireless_interface_init_config(struct wireless_interface *vif)
{
struct blob_attr *tb[__VIF_ATTR_MAX];
struct blob_attr *cur;
vif->network = NULL;
blobmsg_parse(vif_policy, __VIF_ATTR_MAX, tb, blob_data(vif->config), blob_len(vif->config));
if ((cur = tb[VIF_ATTR_NETWORK]))
vif->network = cur;
cur = tb[VIF_ATTR_ISOLATE];
if (cur)
vif->isolate = blobmsg_get_bool(cur);
cur = tb[VIF_ATTR_MODE];
if (cur)
vif->ap_mode = !strcmp(blobmsg_get_string(cur), "ap");
}
static void
vif_update(struct vlist_tree *tree, struct vlist_node *node_new,
struct vlist_node *node_old)
{
struct wireless_interface *vif_old = container_of(node_old, struct wireless_interface, node);
struct wireless_interface *vif_new = container_of(node_new, struct wireless_interface, node);
struct wireless_device *wdev;
if (vif_old)
wdev = vif_old->wdev;
else
wdev = vif_new->wdev;
if (vif_old && vif_new) {
free((void *) vif_old->section);
vif_old->section = strdup(vif_new->section);
if (blob_attr_equal(vif_old->config, vif_new->config)) {
free(vif_new);
return;
}
D(WIRELESS, "Update wireless interface %s on device %s\n", vif_new->name, wdev->name);
wireless_interface_handle_link(vif_old, false);
free(vif_old->config);
vif_old->config = blob_memdup(vif_new->config);
vif_old->isolate = vif_new->isolate;
vif_old->ap_mode = vif_new->ap_mode;
wireless_interface_init_config(vif_old);
free(vif_new);
} else if (vif_new) {
D(WIRELESS, "Create new wireless interface %s on device %s\n", vif_new->name, wdev->name);
vif_new->section = strdup(vif_new->section);
vif_new->config = blob_memdup(vif_new->config);
wireless_interface_init_config(vif_new);
} else if (vif_old) {
D(WIRELESS, "Delete wireless interface %s on device %s\n", vif_old->name, wdev->name);
free((void *) vif_old->section);
free(vif_old->config);
free(vif_old);
}
wdev_set_config_state(wdev, IFC_RELOAD);
}
static void
wireless_proc_poll_fd(struct uloop_fd *fd, unsigned int events)
{
struct wireless_device *wdev = container_of(fd, struct wireless_device, script_proc_fd);
char buf[128];
while (1) {
int b = read(fd->fd, buf, sizeof(buf));
if (b < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN)
return;
goto done;
}
if (!b)
goto done;
}
done:
uloop_timeout_set(&wdev->script_check, 0);
wireless_close_script_proc_fd(wdev);
}
static void
wireless_device_check_script_tasks(struct uloop_timeout *timeout)
{
struct wireless_device *wdev = container_of(timeout, struct wireless_device, script_check);
struct wireless_process *proc, *tmp;
bool restart = false;
list_for_each_entry_safe(proc, tmp, &wdev->script_proc, list) {
if (wireless_process_check(proc))
continue;
D(WIRELESS, "Wireless device '%s' pid %d has terminated\n", wdev->name, proc->pid);
if (proc->required)
restart = true;
wireless_process_free(wdev, proc);
}
if (restart)
wireless_device_retry_setup(wdev);
else
uloop_timeout_set(&wdev->script_check, 1000);
}
void
wireless_device_create(struct wireless_driver *drv, const char *name, struct blob_attr *data)
{
struct wireless_device *wdev;
char *name_buf;
struct blob_attr *disabled;
blobmsg_parse(&wdev_policy, 1, &disabled, blob_data(data), blob_len(data));
wdev = calloc_a(sizeof(*wdev), &name_buf, strlen(name) + 1);
if (disabled && blobmsg_get_bool(disabled))
wdev->disabled = true;
wdev->drv = drv;
wdev->state = IFS_DOWN;
wdev->config_state = IFC_NORMAL;
wdev->name = strcpy(name_buf, name);
wdev->config = data;
wdev->config_autostart = true;
wdev->autostart = wdev->config_autostart;
INIT_LIST_HEAD(&wdev->script_proc);
vlist_init(&wdev->interfaces, avl_strcmp, vif_update);
wdev->interfaces.keep_old = true;
wdev->timeout.cb = wireless_device_setup_timeout;
wdev->script_task.cb = wireless_device_script_task_cb;
wdev->script_task.dir_fd = drv_fd;
wdev->script_task.log_prefix = wdev->name;
wdev->script_proc_fd.fd = -1;
wdev->script_proc_fd.cb = wireless_proc_poll_fd;
wdev->script_check.cb = wireless_device_check_script_tasks;
vlist_add(&wireless_devices, &wdev->node, wdev->name);
}
void wireless_interface_create(struct wireless_device *wdev, struct blob_attr *data, const char *section)
{
struct wireless_interface *vif;
struct blob_attr *tb[__VIF_ATTR_MAX];
struct blob_attr *cur;
char *name_buf;
char name[8];
blobmsg_parse(vif_policy, __VIF_ATTR_MAX, tb, blob_data(data), blob_len(data));
cur = tb[VIF_ATTR_DISABLED];
if (cur && blobmsg_get_bool(cur))
return;
sprintf(name, "%d", wdev->vif_idx++);
vif = calloc_a(sizeof(*vif),
&name_buf, strlen(name) + 1);
vif->name = strcpy(name_buf, name);
vif->wdev = wdev;
vif->config = data;
vif->section = section;
vif->isolate = false;
vlist_add(&wdev->interfaces, &vif->node, vif->name);
}
static void
wireless_interface_status(struct wireless_interface *iface, struct blob_buf *b)
{
void *i;
i = blobmsg_open_table(b, NULL);
if (iface->section)
blobmsg_add_string(b, "section", iface->section);
if (iface->ifname)
blobmsg_add_string(b, "ifname", iface->ifname);
put_container(b, iface->config, "config");
blobmsg_close_table(b, i);
}
void
wireless_device_status(struct wireless_device *wdev, struct blob_buf *b)
{
struct wireless_interface *iface;
void *c, *i;
c = blobmsg_open_table(b, wdev->name);
blobmsg_add_u8(b, "up", wdev->state == IFS_UP);
blobmsg_add_u8(b, "pending", wdev->state == IFS_SETUP || wdev->state == IFS_TEARDOWN);
blobmsg_add_u8(b, "autostart", wdev->autostart);
blobmsg_add_u8(b, "disabled", wdev->disabled);
put_container(b, wdev->config, "config");
i = blobmsg_open_array(b, "interfaces");
vlist_for_each_element(&wdev->interfaces, iface, node)
wireless_interface_status(iface, b);
blobmsg_close_array(b, i);
blobmsg_close_table(b, c);
}
void
wireless_device_get_validate(struct wireless_device *wdev, struct blob_buf *b)
{
struct uci_blob_param_list *p;
void *c, *d;
int i;
c = blobmsg_open_table(b, wdev->name);
d = blobmsg_open_table(b, "device");
p = wdev->drv->device.config;
for (i = 0; i < p->n_params; i++)
blobmsg_add_string(b, p->params[i].name, uci_get_validate_string(p, i));
blobmsg_close_table(b, d);
d = blobmsg_open_table(b, "interface");
p = wdev->drv->interface.config;
for (i = 0; i < p->n_params; i++)
blobmsg_add_string(b, p->params[i].name, uci_get_validate_string(p, i));
blobmsg_close_table(b, d);
blobmsg_close_table(b, c);
}
static void
wireless_interface_set_data(struct wireless_interface *vif)
{
enum {
VIF_DATA_IFNAME,
__VIF_DATA_MAX,
};
static const struct blobmsg_policy data_policy[__VIF_DATA_MAX] = {
[VIF_DATA_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
};
struct blob_attr *tb[__VIF_DATA_MAX];
struct blob_attr *cur;
blobmsg_parse(data_policy, __VIF_DATA_MAX, tb,
blobmsg_data(vif->data), blobmsg_data_len(vif->data));
if ((cur = tb[VIF_DATA_IFNAME]))
vif->ifname = blobmsg_data(cur);
}
static int
wireless_device_add_process(struct wireless_device *wdev, struct blob_attr *data)
{
enum {
PROC_ATTR_PID,
PROC_ATTR_EXE,
PROC_ATTR_REQUIRED,
__PROC_ATTR_MAX
};
static const struct blobmsg_policy proc_policy[__PROC_ATTR_MAX] = {
[PROC_ATTR_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
[PROC_ATTR_EXE] = { .name = "exe", .type = BLOBMSG_TYPE_STRING },
[PROC_ATTR_REQUIRED] = { .name = "required", .type = BLOBMSG_TYPE_BOOL },
};
struct blob_attr *tb[__PROC_ATTR_MAX];
struct wireless_process *proc;
char *name;
int pid;
if (!data)
return UBUS_STATUS_INVALID_ARGUMENT;
blobmsg_parse(proc_policy, __PROC_ATTR_MAX, tb, blobmsg_data(data), blobmsg_data_len(data));
if (!tb[PROC_ATTR_PID] || !tb[PROC_ATTR_EXE])
return UBUS_STATUS_INVALID_ARGUMENT;
pid = blobmsg_get_u32(tb[PROC_ATTR_PID]);
if (pid < 2)
return UBUS_STATUS_INVALID_ARGUMENT;
proc = calloc_a(sizeof(*proc),
&name, strlen(blobmsg_data(tb[PROC_ATTR_EXE])) + 1);
proc->pid = pid;
proc->exe = strcpy(name, blobmsg_data(tb[PROC_ATTR_EXE]));
if (tb[PROC_ATTR_REQUIRED])
proc->required = blobmsg_get_bool(tb[PROC_ATTR_REQUIRED]);
D(WIRELESS, "Wireless device '%s' add pid %d\n", wdev->name, proc->pid);
list_add(&proc->list, &wdev->script_proc);
uloop_timeout_set(&wdev->script_check, 0);
return 0;
}
static int
wireless_device_process_kill_all(struct wireless_device *wdev, struct blob_attr *data,
struct ubus_request_data *req)
{
enum {
KILL_ATTR_SIGNAL,
KILL_ATTR_IMMEDIATE,
__KILL_ATTR_MAX
};
static const struct blobmsg_policy kill_policy[__KILL_ATTR_MAX] = {
[KILL_ATTR_SIGNAL] = { .name = "signal", .type = BLOBMSG_TYPE_INT32 },
[KILL_ATTR_IMMEDIATE] = { .name = "immediate", .type = BLOBMSG_TYPE_BOOL },
};
struct blob_attr *tb[__KILL_ATTR_MAX];
struct blob_attr *cur;
bool immediate = false;
int signal = SIGTERM;
blobmsg_parse(kill_policy, __KILL_ATTR_MAX, tb, blobmsg_data(data), blobmsg_data_len(data));
if ((cur = tb[KILL_ATTR_SIGNAL]))
signal = blobmsg_get_u32(cur);
if ((cur = tb[KILL_ATTR_IMMEDIATE]))
immediate = blobmsg_get_bool(cur);
if (wdev->state != IFS_TEARDOWN || wdev->kill_request)
return UBUS_STATUS_PERMISSION_DENIED;
wireless_process_kill_all(wdev, signal, immediate);
if (list_empty(&wdev->script_proc))
return 0;
wdev->kill_request = calloc(1, sizeof(*wdev->kill_request));
ubus_defer_request(ubus_ctx, req, wdev->kill_request);
return 0;
}
static int
wireless_device_set_retry(struct wireless_device *wdev, struct blob_attr *data)
{
static const struct blobmsg_policy retry_policy = {
.name = "retry", .type = BLOBMSG_TYPE_INT32
};
struct blob_attr *val;
blobmsg_parse(&retry_policy, 1, &val, blobmsg_data(data), blobmsg_data_len(data));
if (!val)
return UBUS_STATUS_INVALID_ARGUMENT;
wdev->retry = blobmsg_get_u32(val);
return 0;
}
enum {
NOTIFY_CMD_UP = 0,
NOTIFY_CMD_SET_DATA = 1,
NOTIFY_CMD_PROCESS_ADD = 2,
NOTIFY_CMD_PROCESS_KILL_ALL = 3,
NOTIFY_CMD_SET_RETRY = 4,
};
int
wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data,
struct ubus_request_data *req)
{
enum {
NOTIFY_ATTR_COMMAND,
NOTIFY_ATTR_VIF,
NOTIFY_ATTR_DATA,
__NOTIFY_MAX,
};
static const struct blobmsg_policy notify_policy[__NOTIFY_MAX] = {
[NOTIFY_ATTR_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_INT32 },
[NOTIFY_ATTR_VIF] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
[NOTIFY_ATTR_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE },
};
struct wireless_interface *vif = NULL;
struct blob_attr *tb[__NOTIFY_MAX];
struct blob_attr *cur, **pdata;
blobmsg_parse(notify_policy, __NOTIFY_MAX, tb, blob_data(data), blob_len(data));
if (!tb[NOTIFY_ATTR_COMMAND])
return UBUS_STATUS_INVALID_ARGUMENT;
if ((cur = tb[NOTIFY_ATTR_VIF]) != NULL) {
vif = vlist_find(&wdev->interfaces, blobmsg_data(cur), vif, node);
if (!vif)
return UBUS_STATUS_NOT_FOUND;
}
cur = tb[NOTIFY_ATTR_DATA];
if (!cur)
return UBUS_STATUS_INVALID_ARGUMENT;
switch (blobmsg_get_u32(tb[NOTIFY_ATTR_COMMAND])) {
case NOTIFY_CMD_UP:
if (vif)
return UBUS_STATUS_INVALID_ARGUMENT;
if (wdev->state != IFS_SETUP)
return UBUS_STATUS_PERMISSION_DENIED;
wireless_device_mark_up(wdev);
break;
case NOTIFY_CMD_SET_DATA:
if (vif)
pdata = &vif->data;
else
pdata = &wdev->data;
if (*pdata)
return UBUS_STATUS_INVALID_ARGUMENT;
*pdata = blob_memdup(cur);
if (vif)
wireless_interface_set_data(vif);
break;
case NOTIFY_CMD_PROCESS_ADD:
return wireless_device_add_process(wdev, cur);
case NOTIFY_CMD_PROCESS_KILL_ALL:
return wireless_device_process_kill_all(wdev, cur, req);
case NOTIFY_CMD_SET_RETRY:
return wireless_device_set_retry(wdev, cur);
default:
return UBUS_STATUS_INVALID_ARGUMENT;
}
return 0;
}
void
wireless_start_pending(void)
{
struct wireless_device *wdev;
vlist_for_each_element(&wireless_devices, wdev, node)
if (wdev->autostart)
__wireless_device_set_up(wdev);
}

105
src/3P/netifd/wireless.h Normal file
View File

@@ -0,0 +1,105 @@
/*
* netifd - network interface daemon
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __NETIFD_WIRELESS_H
#define __NETIFD_WIRELESS_H
#include <libubox/utils.h>
#include "interface.h"
extern struct vlist_tree wireless_devices;
extern struct avl_tree wireless_drivers;
struct wireless_driver {
struct avl_node node;
const char *name;
const char *script;
struct {
char *buf;
struct uci_blob_param_list *config;
} device, interface;
};
struct wireless_device {
struct vlist_node node;
struct wireless_driver *drv;
struct vlist_tree interfaces;
char *name;
struct netifd_process script_task;
struct uloop_timeout timeout;
struct uloop_timeout poll;
struct list_head script_proc;
struct uloop_fd script_proc_fd;
struct uloop_timeout script_check;
struct ubus_request_data *kill_request;
struct blob_attr *prev_config;
struct blob_attr *config;
struct blob_attr *data;
bool config_autostart;
bool autostart;
bool disabled;
enum interface_state state;
enum interface_config_state config_state;
bool cancel;
int retry;
int vif_idx;
};
struct wireless_interface {
struct vlist_node node;
const char *section;
char *name;
struct wireless_device *wdev;
struct blob_attr *config;
struct blob_attr *data;
const char *ifname;
struct blob_attr *network;
bool isolate;
bool ap_mode;
};
struct wireless_process {
struct list_head list;
const char *exe;
int pid;
bool required;
};
void wireless_device_create(struct wireless_driver *drv, const char *name, struct blob_attr *data);
void wireless_device_set_up(struct wireless_device *wdev);
void wireless_device_set_down(struct wireless_device *wdev);
void wireless_device_status(struct wireless_device *wdev, struct blob_buf *b);
void wireless_device_get_validate(struct wireless_device *wdev, struct blob_buf *b);
void wireless_interface_create(struct wireless_device *wdev, struct blob_attr *data, const char *section);
int wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data,
struct ubus_request_data *req);
void wireless_start_pending(void);
void wireless_init(void);
#endif