Compare commits

..

10 commits

Author SHA1 Message Date
29f3ef36af
device: add bridge port configuration 2019-06-01 19:10:17 +02:00
8d15854eb5
device: bridge: implement initial bridge setup 2019-06-01 14:28:03 +02:00
8b5af11ebe
device: move more common functions 2019-06-01 12:30:14 +02:00
f43788c947
device: split out common device settings 2019-05-31 21:56:08 +02:00
7745aa34ad
config: fix resolve of device type 2019-05-31 20:40:13 +02:00
bcef77a7fb
Switch to a JSON-based config format
We can get rid of a lot of code by ditching out INI format parser. We also
remove the support for "subtype" and "generator" templates for now; rather
than implementing this in NeCo itself, templates could be implemented as
a Lua DSL later.
2019-05-31 18:42:01 +02:00
44cb17317c
Implement simple setting of link state and IP addresses 2019-01-06 07:20:08 +01:00
b4bdc79702
Add LICENSE 2019-01-05 22:55:01 +01:00
daa68c4b2d
Add first parts of interface config handling 2019-01-05 22:06:45 +01:00
9e059f898a
Load config dirs 2018-12-29 17:00:31 +01:00
23 changed files with 1046 additions and 261 deletions

22
LICENSE Normal file
View file

@ -0,0 +1,22 @@
Copyright (c) 2012-2019, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,6 +1,9 @@
project('pnc', 'c') project('neco', 'c', default_options : ['c_std=gnu11'])
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
ubox_dep = cc.find_library('ubox') libubox_dep = cc.find_library('ubox')
libjson_c_dep = dependency('json-c', method : 'pkg-config')
libmnl_dep = dependency('libmnl', method : 'pkg-config')
subdir('src') subdir('src')

View file

@ -1,147 +0,0 @@
#include "config-ini.h"
#include "types.h"
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void free_field(ini_field_t *field) {
free(field->key);
free(field->value);
free(field);
}
static void free_section(ini_section_t *section) {
ini_field_t *field, *tmp;
list_for_each_entry_safe(field, tmp, &section->fields, node)
free_field(field);
free(section->name);
free(section);
}
void free_ini_file(ini_file_t *file) {
ini_section_t *section, *tmp;
list_for_each_entry_safe(section, tmp, &file->sections, node)
free_section(section);
free(file);
}
static bool add_field(ini_section_t *section, const char *key, const char *value) {
ini_field_t *field = calloc(1, sizeof(*field));
if (!field)
return false;
field->key = strdup(key);
field->value = strdup(value);
if (!field->key || !field->value) {
free_field(field);
return false;
}
list_add_tail(&field->node, &section->fields);
return true;
}
static ini_section_t * add_section(ini_file_t *file, const char *name) {
ini_section_t *section = calloc(1, sizeof(*section));
if (!section)
return NULL;
section->name = strdup(name);
if (!section->name) {
free(section);
return false;
}
INIT_LIST_HEAD(&section->fields);
list_add_tail(&section->node, &file->sections);
return section;
}
ini_file_t * read_ini_file(const char *filename) {
FILE *f = fopen(filename, "r");
if (!f)
return NULL;
ini_file_t *file = calloc(1, sizeof(*file));
if (!file)
goto error;
INIT_LIST_HEAD(&file->sections);
ini_section_t *section = NULL;
int err = 0;
char *line = NULL;
size_t n = 0;
while (getline(&line, &n, f) >= 0) {
char *input = line;
while (isspace(input[0]))
input++;
if (input[0] == '#' || input[0] == '\0')
continue;
size_t len = strlen(input);
while (isspace(input[len-1]))
len--;
if (input[0] == '[') {
if (input[len-1] != ']') {
err = EINVAL;
goto error;
}
input[len-1] = '\0';
section = add_section(file, input+1);
if (!section)
goto error;
} else {
if (!section) {
err = EINVAL;
goto error;
}
input[len] = '\0';
char *delim = strchr(input, '=');
if (!delim) {
err = EINVAL;
goto error;
}
*delim = '\0';
if (!add_field(section, input, delim+1))
goto error;
}
}
if (ferror(f))
err = EIO;
error:
free(line);
fclose(f);
if (err) {
free_ini_file(file);
errno = err;
return NULL;
}
return file;
}

View file

@ -1,22 +0,0 @@
#pragma once
#include <libubox/list.h>
typedef struct {
struct list_head node;
char *key;
char *value;
} ini_field_t;
typedef struct {
struct list_head node;
struct list_head fields;
char *name;
} ini_section_t;
typedef struct {
struct list_head sections;
} ini_file_t;
ini_file_t * read_ini_file(const char *filename);
void free_ini_file(ini_file_t *file);

View file

@ -1,55 +1,41 @@
#include "config-load.h" #include "config-load.h"
#include "config-ini.h" #include "config-process.h"
#include "types.h" #include "device.h"
#include "util.h"
#include <libubox/avl-cmp.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
static const char * simple_basename(const char *path) { bool read_config(const char *path) {
const char *slash = strrchr(path, '/'); struct json_object *config = json_object_from_file(path);
return slash ? (slash+1) : path; if (!config)
}
static const char * extname(const char *filename) {
const char *dot = strrchr(filename, '.');
return dot ? (dot+1) : NULL;
}
static interface_config_t * read_interface_config(const char *path) {
ini_file_t *file = read_ini_file(path);
ini_section_t *section;
ini_field_t *field;
list_for_each_entry(section, &file->sections, node) {
printf("%s\n", section->name);
list_for_each_entry(field, &section->fields, node)
printf("%s.%s=%s\n", section->name, field->key, field->value);
}
free_ini_file(file);
return NULL;
}
bool read_config_file(const char *path) {
const char *filename = simple_basename(path);
const char *ext = extname(filename);
if (!ext) {
errno = EINVAL;
return false; return false;
}
char *name = strndup(filename, (ext - filename) - 1); struct avl_tree *devices = config_process(config);
json_object_put(config);
if (strcmp(ext, "interface") == 0) { if (!devices)
interface_config_t *iface = read_interface_config(path); return false;
free(iface);
}
free(name); device_t *dev, *tmp;
avl_for_each_element(devices, dev, node)
dev->type->init(dev);
//avl_for_each_element(devices, dev, node)
// dev->type->release(dev);
avl_remove_all_elements(devices, dev, node, tmp)
dev->type->free(dev);
free(devices);
return true; return true;
} }

View file

@ -2,4 +2,4 @@
#include <stdbool.h> #include <stdbool.h>
bool read_config_file(const char *path); bool read_config(const char *path);

58
src/config-process.c Normal file
View file

@ -0,0 +1,58 @@
#include "config-process.h"
#include "device.h"
#include "util.h"
#include <libubox/avl-cmp.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
typedef struct _process_ctx {
struct avl_tree *ret;
} process_ctx_t;
typedef struct _config_subtype {
struct avl_node node;
} config_subtype_t;
static device_t * config_process_device(const char *name, struct json_object *obj) {
const char *typename = NULL;
struct json_object *device = neco_json_get_value(obj, "device", json_type_object);
if (device)
typename = neco_json_get_string(device, "type");
const device_type_t *type = get_device_type(typename ?: "interface");
if (!type)
return NULL;
return type->process_config(name, obj);
}
struct avl_tree * config_process(struct json_object *config) {
if (!json_object_is_type(config, json_type_object))
return NULL;
struct json_object *devices = neco_json_get_value(config, "devices", json_type_object);
if (!devices)
return NULL;
process_ctx_t ctx;
ctx.ret = calloc(1, sizeof(*ctx.ret));
if (!ctx.ret)
return NULL;
avl_init(ctx.ret, avl_strcmp, false, NULL);
json_object_object_foreach(devices, name, obj) {
device_t *device = config_process_device(name, obj);
if (!device)
continue;
avl_insert(ctx.ret, &device->node);
}
return ctx.ret;
}

6
src/config-process.h Normal file
View file

@ -0,0 +1,6 @@
#pragma once
#include <json-c/json.h>
#include <libubox/avl.h>
struct avl_tree * config_process(struct json_object *config);

131
src/device-bridge.c Normal file
View file

@ -0,0 +1,131 @@
#include "device-common.h"
#include "netlink.h"
#include "util.h"
#include <string.h>
#include <net/if.h>
#include <linux/if.h>
#include <linux/if_link.h>
#include <linux/rtnetlink.h>
static device_type_t device_type_bridge;
typedef struct _device_bridge {
device_t device;
device_common_t common;
} device_bridge_t;
static void bridge_free(device_t *dev) {
device_bridge_t *iface = container_of(dev, device_bridge_t, device);
VECTOR_FREE(iface->common.addrs);
free(NODE_NAME(dev));
free(iface);
}
static device_t * bridge_process_config(const char *name, struct json_object *config) {
device_bridge_t *iface = calloc(1, sizeof(*iface));
if (!iface)
return NULL;
device_t *dev = &iface->device;
dev->type = &device_type_bridge;
NODE_NAME(dev) = strdup(name);
if (!NODE_NAME(dev)) {
free(iface);
return NULL;
}
if (!device_common_process_config(&iface->common, config))
goto err;
return dev;
err:
bridge_free(dev);
return NULL;
}
static bool bridge_nl_create(device_bridge_t *bridge) {
char buf[MNL_SOCKET_BUFFER_SIZE];
struct mnl_socket *nl = nl_socket();
struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = RTM_NEWLINK;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE;
int seq = nlh->nlmsg_seq = nl_seq();
struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi));
ifi->ifi_family = AF_UNSPEC;
mnl_attr_put_str(nlh, IFLA_IFNAME, NODE_NAME(&bridge->device));
struct nlattr *linkinfo = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
mnl_attr_put_str(nlh, IFLA_INFO_KIND, "bridge");
mnl_attr_nest_end(nlh, linkinfo);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_sendto");
return false;
}
int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1) {
perror("mnl_socket_recvfrom");
return false;
}
ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL);
if (ret == -1) {
perror("mnl_cb_run");
return false;
}
return true;
}
static void bridge_init(device_t *dev) {
device_bridge_t *bridge = container_of(dev, device_bridge_t, device);
if (!bridge_nl_create(bridge))
return;
unsigned ifindex = if_nametoindex(NODE_NAME(dev));
if (!ifindex)
return;
device_common_init(&bridge->common, ifindex);
}
static void bridge_update(device_t *dev) {
}
static void bridge_release(device_t *dev) {
/*
device_bridge_t *bridge = container_of(dev, device_bridge_t, device);
unsigned ifindex = if_nametoindex(NODE_NAME(dev));
if (!ifindex)
return;
device_common_release(&bridge->common, ifindex);
*/
}
static device_type_t device_type_bridge = {
.process_config = bridge_process_config,
.free = bridge_free,
.init = bridge_init,
.update = bridge_update,
.release = bridge_release,
};
__attribute__((constructor))
static void bridge_constructor(void) {
register_device_type("bridge", &device_type_bridge);
}

299
src/device-common.c Normal file
View file

@ -0,0 +1,299 @@
#include "device-common.h"
#include "netlink.h"
#include "util.h"
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <linux/if_link.h>
#include <linux/rtnetlink.h>
static unsigned long strtoul_safe(const char *str) {
char *endptr;
errno = 0;
unsigned long val = strtoul(str, &endptr, 10);
if (endptr == str || *endptr)
errno = EINVAL;
return val;
}
static bool parse_address(ipaddr_t *addr, const char *str) {
if (inet_pton(AF_INET, str, &addr->addr4)) {
addr->af = AF_INET;
return true;
}
if (inet_pton(AF_INET6, str, &addr->addr6)) {
addr->af = AF_INET6;
return true;
}
return false;
}
static bool parse_prefix(ipaddr_prefix_t *prefix, const char *str, bool allow_host) {
const char *slash = strrchr(str, '/');
if (!slash)
return false;
size_t len = slash - str;
char buf[len+1];
memcpy(buf, str, len);
buf[len] = 0;
if (!parse_address(&prefix->addr, buf))
return false;
long plen = strtoul_safe(slash + 1);
if (errno)
return false;
switch (prefix->addr.af) {
case AF_INET:
if (plen > 32)
return false;
break;
case AF_INET6:
if (plen > 128)
return false;
break;
default:
assert(false);
__builtin_unreachable();
}
prefix->plen = plen;
// TODO: Implement allow_host
return true;
}
static bool process_section_device(device_common_t *device, struct json_object *section) {
return true;
}
static bool process_section_static(device_common_t *device, struct json_object *section) {
struct json_object *addresses = neco_json_get_value(section, "addresses", json_type_array);
if (addresses) {
for (size_t i = 0; i < json_object_array_length(addresses); i++) {
struct json_object *address = json_object_array_get_idx(addresses, i);
if (!json_object_is_type(address, json_type_string)) {
fprintf(stderr, "interface: static: invalid address entry of type %s\n", json_type_to_name(json_object_get_type(address)));
continue;
}
ipaddr_prefix_t p;
if (!parse_prefix(&p, json_object_get_string(address), true)) {
fprintf(stderr, "interface: static: unable to parse Address %s\n", json_object_get_string(address));
break;
}
if (!VECTOR_ADD(device->addrs, p)) {
fprintf(stderr, "interface: static: adding address failed\n");
return false;
}
}
}
return true;
}
static bool process_section_bridge(device_common_t *device, struct json_object *section) {
const char *master = neco_json_get_string(section, "master");
if (master)
strncpy(device->master, master, sizeof(device->master)-1);
return true;
}
bool device_common_process_config(device_common_t *device, struct json_object *config) {
struct json_object *sec_device = neco_json_get_value(config, "device", json_type_object);
if (sec_device) {
if (!process_section_device(device, sec_device))
return false;
}
struct json_object *sec_static = neco_json_get_value(config, "static", json_type_object);
if (sec_static) {
if (!process_section_static(device, sec_static))
return false;
}
struct json_object *sec_bridge = neco_json_get_value(config, "bridge", json_type_object);
if (sec_bridge) {
if (!process_section_bridge(device, sec_bridge))
return false;
}
return true;
}
static bool device_common_set_link_flags(unsigned ifindex, unsigned change, unsigned flags) {
char buf[MNL_SOCKET_BUFFER_SIZE];
struct mnl_socket *nl = nl_socket();
struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = RTM_SETLINK;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
int seq = nlh->nlmsg_seq = nl_seq();
struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi));
ifi->ifi_index = ifindex;
ifi->ifi_family = AF_UNSPEC;
ifi->ifi_change = change;
ifi->ifi_flags = flags;
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_sendto");
return false;
}
int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1) {
perror("mnl_socket_recvfrom");
return false;
}
ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL);
if (ret == -1) {
perror("mnl_cb_run");
return false;
}
return true;
}
static bool device_common_set_link_state(unsigned ifindex, bool up) {
return device_common_set_link_flags(ifindex, IFF_UP, up ? IFF_UP : 0);
}
static bool device_common_set_ipaddr(unsigned ifindex, ipaddr_prefix_t *addr, bool add) {
char buf[MNL_SOCKET_BUFFER_SIZE];
struct mnl_socket *nl = nl_socket();
struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = add ? RTM_NEWADDR : RTM_DELADDR;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
if (add)
nlh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
int seq = nlh->nlmsg_seq = nl_seq();
struct ifaddrmsg *ifa = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifa));
ifa->ifa_index = ifindex;
ifa->ifa_family = addr->addr.af;
ifa->ifa_prefixlen = addr->plen;
switch (addr->addr.af) {
case AF_INET:
mnl_attr_put(nlh, IFA_LOCAL, 4, &addr->addr.addr4);
break;
case AF_INET6:
mnl_attr_put(nlh, IFA_LOCAL, 16, &addr->addr.addr6);
break;
default:
errno = EINVAL;
return false;
}
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_sendto");
return false;
}
int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1) {
perror("mnl_socket_recvfrom");
return false;
}
ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL);
if (ret == -1) {
perror("mnl_cb_run");
return false;
}
return true;
}
static bool device_common_set_master(unsigned ifindex, unsigned master) {
char buf[MNL_SOCKET_BUFFER_SIZE];
struct mnl_socket *nl = nl_socket();
struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = RTM_SETLINK;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
int seq = nlh->nlmsg_seq = nl_seq();
struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi));
ifi->ifi_index = ifindex;
ifi->ifi_family = AF_UNSPEC;
mnl_attr_put_u32(nlh, IFLA_MASTER, master);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_sendto");
return false;
}
int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1) {
perror("mnl_socket_recvfrom");
return false;
}
ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL);
if (ret == -1) {
perror("mnl_cb_run");
return false;
}
return true;
}
void device_common_init(device_common_t *device, int ifindex) {
if (*device->master) {
unsigned master = if_nametoindex(device->master);
if (!master)
return;
device_common_set_master(ifindex, master);
}
device_common_set_link_state(ifindex, true);
if (!*device->master) {
for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++)
device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), true);
}
}
void device_common_release(device_common_t *device, int ifindex) {
if (!*device->master) {
for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++)
device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), false);
}
device_common_set_link_state(ifindex, false);
if (*device->master)
device_common_set_master(ifindex, 0);
}

36
src/device-common.h Normal file
View file

@ -0,0 +1,36 @@
#pragma once
#include "device.h"
#include "vector.h"
#include <net/if.h>
#include <netinet/ether.h>
#include <netinet/in.h>
typedef struct _ipaddr {
int af;
union {
struct in_addr addr4;
struct in6_addr addr6;
};
} ipaddr_t;
typedef struct _ipprefix {
ipaddr_t addr;
uint8_t plen;
} ipaddr_prefix_t;
typedef VECTOR(ipaddr_prefix_t) ipaddr_prefix_vector_t;
typedef struct _device_common {
struct ether_addr macaddr;
uint16_t mtu;
ipaddr_prefix_vector_t addrs;
char master[IF_NAMESIZE];
} device_common_t;
bool device_common_process_config(device_common_t *device, struct json_object *config);
void device_common_init(device_common_t *device, int ifindex);
void device_common_release(device_common_t *device, int ifindex);

85
src/device-interface.c Normal file
View file

@ -0,0 +1,85 @@
#include "device-common.h"
#include "util.h"
#include "vector.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>
static device_type_t device_type_interface;
typedef struct _device_interface {
device_t device;
device_common_t common;
} device_interface_t;
static void interface_free(device_t *dev) {
device_interface_t *iface = container_of(dev, device_interface_t, device);
VECTOR_FREE(iface->common.addrs);
free(NODE_NAME(dev));
free(iface);
}
static device_t * interface_process_config(const char *name, struct json_object *config) {
device_interface_t *iface = calloc(1, sizeof(*iface));
if (!iface)
return NULL;
device_t *dev = &iface->device;
dev->type = &device_type_interface;
NODE_NAME(dev) = strdup(name);
if (!NODE_NAME(dev)) {
free(iface);
return NULL;
}
if (!device_common_process_config(&iface->common, config))
goto err;
return dev;
err:
interface_free(dev);
return NULL;
}
static void interface_init(device_t *dev) {
device_interface_t *iface = container_of(dev, device_interface_t, device);
unsigned ifindex = if_nametoindex(NODE_NAME(dev));
if (!ifindex)
return;
device_common_init(&iface->common, ifindex);
}
static void interface_update(device_t *dev) {
}
static void interface_release(device_t *dev) {
device_interface_t *iface = container_of(dev, device_interface_t, device);
unsigned ifindex = if_nametoindex(NODE_NAME(dev));
if (!ifindex)
return;
device_common_release(&iface->common, ifindex);
}
static device_type_t device_type_interface = {
.process_config = interface_process_config,
.free = interface_free,
.init = interface_init,
.update = interface_update,
.release = interface_release,
};
__attribute__((constructor))
static void interface_constructor(void) {
register_device_type("interface", &device_type_interface);
}

15
src/device.c Normal file
View file

@ -0,0 +1,15 @@
#include "device.h"
#include "util.h"
#include <libubox/avl-cmp.h>
AVL_TREE(device_types, avl_strcmp, false, NULL);
void register_device_type(const char *name, device_type_t *device_type) {
NODE_NAME(device_type) = (char *)name;
avl_insert(&device_types, &device_type->node);
}
const device_type_t * get_device_type(const char *name) {
return avl_find_element(&device_types, name, (device_type_t *)NULL, node);
}

26
src/device.h Normal file
View file

@ -0,0 +1,26 @@
#pragma once
#include <json-c/json.h>
#include <libubox/avl.h>
extern struct avl_tree device_types;
typedef struct _device_type device_type_t;
typedef struct _device {
struct avl_node node;
const device_type_t *type;
} device_t;
struct _device_type {
struct avl_node node;
device_t * (*process_config)(const char *name, struct json_object *config);
void (*free)(device_t *device);
void (*init)(device_t *device);
void (*update)(device_t *device);
void (*release)(device_t *device);
};
void register_device_type(const char *name, device_type_t *device_type);
const device_type_t * get_device_type(const char *name);

View file

@ -1,7 +1,19 @@
src = [ src = [
'pnc.c', 'neco.c',
'config-ini.c',
'config-load.c', 'config-load.c',
'config-process.c',
'device.c',
'device-bridge.c',
'device-common.c',
'device-interface.c',
'netlink.c',
'vector.c',
] ]
executable('pnc', sources: src, dependencies: [ubox_dep]) dep = [
libjson_c_dep,
libmnl_dep,
libubox_dep,
]
executable('neco', sources: src, dependencies: dep)

13
src/neco.c Normal file
View file

@ -0,0 +1,13 @@
#include "config-load.h"
#include "netlink.h"
int main(int argc, char *argv[]) {
if (!nl_init())
return 1;
read_config(argv[1]);
nl_deinit();
return 0;
}

38
src/netlink.c Normal file
View file

@ -0,0 +1,38 @@
#include "netlink.h"
#include <stdio.h>
static struct mnl_socket *nl;
static uint32_t seq;
bool nl_init(void) {
nl = mnl_socket_open(NETLINK_ROUTE);
if (nl == NULL) {
perror("mnl_socket_open");
return false;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
nl_deinit();
return false;
}
return true;
}
void nl_deinit(void) {
if (!nl)
return;
mnl_socket_close(nl);
nl = NULL;
}
struct mnl_socket * nl_socket(void) {
return nl;
}
uint32_t nl_seq(void) {
return seq++;
}

12
src/netlink.h Normal file
View file

@ -0,0 +1,12 @@
#pragma once
#include <libmnl/libmnl.h>
#include <stdbool.h>
#include <stdbool.h>
bool nl_init(void);
void nl_deinit(void);
struct mnl_socket * nl_socket(void);
uint32_t nl_seq(void);

View file

@ -1,6 +0,0 @@
#include "config-load.h"
int main(int argc, char *argv[]) {
read_config_file(argv[1]);
return 0;
}

View file

@ -1,38 +0,0 @@
#pragma once
#include <libubox/avl.h>
#include <net/if.h>
#include <stdint.h>
typedef struct {
uint8_t addr[6];
} macaddr_t;
#define NODE_NAME(c) (*(char **)&(c)->node.key)
typedef struct {
struct avl_tree subtypes;
struct avl_tree interfaces;
} config_t;
typedef struct {
struct avl_node node;
} subconfig_type_t;
typedef struct {
struct avl_node node;
struct avl_tree subconfigs;
macaddr_t macaddr;
uint16_t mtu;
} interface_config_t;
typedef struct {
struct avl_node node;
const char *type;
} interface_subconfig_t;
typedef struct {
} interface_generator_t;

29
src/util.h Normal file
View file

@ -0,0 +1,29 @@
#pragma once
#include <json-c/json.h>
#define NODE_NAME(c) (*(char **)&(c)->node.key)
static inline struct json_object * neco_json_get_value(
struct json_object *obj,
const char *key,
enum json_type type
) {
struct json_object *value;
if (!json_object_object_get_ex(obj, key, &value))
return NULL;
if (!json_object_is_type(value, type))
return NULL;
return value;
}
static inline const char * neco_json_get_string(struct json_object *obj, const char *key) {
struct json_object *value = neco_json_get_value(obj, key, json_type_string);
if (!value)
return NULL;
return json_object_get_string(value);
}

114
src/vector.c Normal file
View file

@ -0,0 +1,114 @@
/**
\file
Typesafe dynamically sized arrays
*/
#include "vector.h"
#include <errno.h>
#include <stdint.h>
#include <string.h>
/** The minimum number of elements to allocate even when fewer elements are used */
#define MIN_VECTOR_ALLOC 4
/**
Multiplies two size_t values, checking for overflows
Both arguments are limited to the size of a uint32_t (to keep the check simple)
Returns SIZE_MAX and sets errno on failure.
*/
static inline size_t mul_check(size_t members, size_t size) {
uint64_t v = (uint64_t)members * size;
if (members > UINT32_MAX || size > UINT32_MAX || v > SIZE_MAX) {
errno = EOVERFLOW;
return SIZE_MAX;
}
return v;
}
/**
Reallocates a block of memory for an array on the heap
May fail with EOVERFLOW (when members or size does not fit into a uint32_t,
or their product does not fit into a size_t) or ENOMEM.
*/
static inline void * realloc_array(void *ptr, size_t members, size_t size) {
errno = 0;
size_t array_size = mul_check(members, size);
if (errno)
return NULL;
return realloc(ptr, array_size);
}
/**
Resizes a vector
Vector allocations are always powers of 2.
Internal function, use VECTOR_RESIZE() instead.
*/
bool _vector_resize(vector_desc_t *desc, void **data, size_t n, size_t elemsize) {
desc->length = n;
size_t alloc = desc->allocated;
if (!alloc) {
alloc = MIN_VECTOR_ALLOC;
n = n*3/2;
}
while (alloc < n) {
alloc <<= 1;
if (!alloc) {
errno = ENOMEM;
return false;
}
}
if (alloc != desc->allocated) {
desc->allocated = alloc;
void *tmp = realloc_array(*data, alloc, elemsize);
if (!tmp)
return false;
*data = tmp;
}
return true;
}
/**
Inserts an element into a vector
Internal function, use VECTOR_INSERT() and VECTOR_ADD() instead.
*/
bool _vector_insert(vector_desc_t *desc, void **data, void *element, size_t pos, size_t elemsize) {
if (!_vector_resize(desc, data, desc->length+1, elemsize))
return false;
void *p = *data + pos*elemsize;
memmove(p+elemsize, p, (desc->length-pos-1)*elemsize);
memcpy(p, element, elemsize);
return true;
}
/**
Deletes an element from a vector
Internal function, use VECTOR_DELETE() instead.
*/
void _vector_delete(vector_desc_t *desc, void **data, size_t pos, size_t elemsize) {
void *p = *data + pos*elemsize;
memmove(p, p+elemsize, (desc->length-pos-1)*elemsize);
desc->length--;
}

113
src/vector.h Normal file
View file

@ -0,0 +1,113 @@
/**
\file
Typesafe dynamically sized arrays
*/
#pragma once
#include <stdbool.h>
#include <stdlib.h>
/** A vector descriptor */
typedef struct _vector_desc {
size_t allocated; /**< The number of elements currently allocated */
size_t length; /**< The actual number of elements in the vector */
} vector_desc_t;
/**
A type for a vector of \e type elements
\hideinitializer
*/
#define VECTOR(type) struct { \
vector_desc_t desc; \
type *data; \
}
bool _vector_resize(vector_desc_t *desc, void **data, size_t n, size_t elemsize);
bool _vector_insert(vector_desc_t *desc, void **data, void *element, size_t pos, size_t elemsize);
void _vector_delete(vector_desc_t *desc, void **data, size_t pos, size_t elemsize);
/**
Resizes the vector \e a to \e n elements
\hideinitializer
*/
#define VECTOR_RESIZE(v, n) ({ \
__typeof__(v) *_v = &(v); \
_vector_resize(&_v->desc, (void **)&_v->data, (n), sizeof(*_v->data)); \
})
/**
Frees all resources used by the vector \e v
\hideinitializer
*/
#define VECTOR_FREE(v) free((v).data)
/**
Returns the number of elements in the vector \e v
\hideinitializer
*/
#define VECTOR_LEN(v) ((v).desc.length)
/**
Returns the element with index \e i in the vector \e v
\hideinitializer
*/
#define VECTOR_INDEX(v, i) ((v).data[i])
/**
Returns a pointer to the vector elements of \e v
\hideinitializer
*/
#define VECTOR_DATA(v) ((v).data)
/**
Inserts the element \e elem at index \e pos of vector \e v
\hideinitializer
*/
#define VECTOR_INSERT(v, elem, pos) ({ \
__typeof__(v) *_v = &(v); \
__typeof__(*_v->data) _e = (elem); \
_vector_insert(&_v->desc, (void **)&_v->data, &_e, (pos), sizeof(_e)); \
})
/**
Adds the element \e elem at the end of vector \e v
\hideinitializer
*/
#define VECTOR_ADD(v, elem) ({ \
__typeof__(v) *_v = &(v); \
__typeof__(*_v->data) _e = (elem); \
_vector_insert(&_v->desc, (void **)&_v->data, &_e, _v->desc.length, sizeof(_e)); \
})
/**
Deletes the element at index \e pos of vector \e v
\hideinitializer
*/
#define VECTOR_DELETE(v, pos) ({ \
__typeof__(v) *_v = &(v); \
_vector_delete(&_v->desc, (void **)&_v->data, (pos), sizeof(*_v->data)); \
})
/**
Performs a binary search on the vector \e v, returning a pointer to a matching vector element
\hideinitializer
*/
#define VECTOR_BSEARCH(key, v, cmp) ({ \
__typeof__(v) *_v = &(v); \
const __typeof__(*_v->data) *_key = (key); \
int (*_cmp)(__typeof__(_key), __typeof__(_key)) = (cmp); \
(__typeof__(_v->data))bsearch(_key, _v->data, _v->desc.length, sizeof(*_v->data), (int (*)(const void *, const void *))_cmp); \
})