Bump netifd-2016-06-06-99e6dc68bbac5a57a0ebca810a9dc36e38667821
This commit is contained in:
51
src/3P/netifd/CMakeLists.txt
Normal file
51
src/3P/netifd/CMakeLists.txt
Normal 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
140
src/3P/netifd/DESIGN
Normal 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
199
src/3P/netifd/alias.c
Normal 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
726
src/3P/netifd/bridge.c
Normal 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
434
src/3P/netifd/config.c
Normal 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
24
src/3P/netifd/config.h
Normal 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
|
||||
74
src/3P/netifd/config/network
Normal file
74
src/3P/netifd/config/network
Normal 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
|
||||
|
||||
20
src/3P/netifd/config/wireless
Normal file
20
src/3P/netifd/config/wireless
Normal 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
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
282
src/3P/netifd/device.h
Normal 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
|
||||
1
src/3P/netifd/examples/hotplug-cmd
Executable file
1
src/3P/netifd/examples/hotplug-cmd
Executable file
@@ -0,0 +1 @@
|
||||
echo "Action: $ACTION, Interface: $INTERFACE"
|
||||
71
src/3P/netifd/examples/proto/ppp.sh
Executable file
71
src/3P/netifd/examples/proto/ppp.sh
Executable 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
|
||||
44
src/3P/netifd/examples/proto/pptp.sh
Executable file
44
src/3P/netifd/examples/proto/pptp.sh
Executable 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
|
||||
|
||||
66
src/3P/netifd/examples/wireless/mac80211.sh
Executable file
66
src/3P/netifd/examples/wireless/mac80211.sh
Executable 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
212
src/3P/netifd/handler.c
Normal 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
46
src/3P/netifd/handler.h
Normal 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
|
||||
200
src/3P/netifd/interface-event.c
Normal file
200
src/3P/netifd/interface-event.c
Normal 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(¤t->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
1422
src/3P/netifd/interface-ip.c
Normal file
File diff suppressed because it is too large
Load Diff
175
src/3P/netifd/interface-ip.h
Normal file
175
src/3P/netifd/interface-ip.h
Normal 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
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
204
src/3P/netifd/interface.h
Normal 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
256
src/3P/netifd/iprule.c
Normal 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
101
src/3P/netifd/iprule.h
Normal 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
263
src/3P/netifd/macvlan.c
Normal 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
346
src/3P/netifd/main.c
Normal 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
103
src/3P/netifd/netifd.h
Normal 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
924
src/3P/netifd/proto-shell.c
Normal 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);
|
||||
}
|
||||
113
src/3P/netifd/proto-static.c
Normal file
113
src/3P/netifd/proto-static.c
Normal 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
642
src/3P/netifd/proto.c
Normal 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
83
src/3P/netifd/proto.h
Normal 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
|
||||
401
src/3P/netifd/scripts/netifd-proto.sh
Normal file
401
src/3P/netifd/scripts/netifd-proto.sh
Normal 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
|
||||
}
|
||||
337
src/3P/netifd/scripts/netifd-wireless.sh
Normal file
337
src/3P/netifd/scripts/netifd-wireless.sh
Normal 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
|
||||
}
|
||||
50
src/3P/netifd/scripts/utils.sh
Normal file
50
src/3P/netifd/scripts/utils.sh
Normal 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 "$@"
|
||||
}
|
||||
286
src/3P/netifd/system-dummy.c
Normal file
286
src/3P/netifd/system-dummy.c
Normal 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
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
43
src/3P/netifd/system.c
Normal 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
168
src/3P/netifd/system.h
Normal 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
99
src/3P/netifd/tunnel.c
Normal 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
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
26
src/3P/netifd/ubus.h
Normal 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
234
src/3P/netifd/utils.c
Normal 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
122
src/3P/netifd/utils.h
Normal 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
192
src/3P/netifd/vlan.c
Normal 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
251
src/3P/netifd/vlandev.c
Normal 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
996
src/3P/netifd/wireless.c
Normal 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
105
src/3P/netifd/wireless.h
Normal 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
|
||||
Reference in New Issue
Block a user