Compare commits
10 commits
f4881eb5e2
...
29f3ef36af
Author | SHA1 | Date | |
---|---|---|---|
29f3ef36af | |||
8d15854eb5 | |||
8b5af11ebe | |||
f43788c947 | |||
7745aa34ad | |||
bcef77a7fb | |||
44cb17317c | |||
b4bdc79702 | |||
daa68c4b2d | |||
9e059f898a |
23 changed files with 1046 additions and 261 deletions
22
LICENSE
Normal file
22
LICENSE
Normal 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.
|
|
@ -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')
|
||||||
|
|
147
src/config-ini.c
147
src/config-ini.c
|
@ -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, §ion->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, §ion->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(§ion->fields);
|
|
||||||
list_add_tail(§ion->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;
|
|
||||||
}
|
|
|
@ -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);
|
|
|
@ -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, §ion->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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
58
src/config-process.c
Normal 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
6
src/config-process.h
Normal 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
131
src/device-bridge.c
Normal 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
299
src/device-common.c
Normal 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
36
src/device-common.h
Normal 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
85
src/device-interface.c
Normal 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
15
src/device.c
Normal 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
26
src/device.h
Normal 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);
|
|
@ -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
13
src/neco.c
Normal 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
38
src/netlink.c
Normal 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
12
src/netlink.h
Normal 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);
|
|
@ -1,6 +0,0 @@
|
||||||
#include "config-load.h"
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
read_config_file(argv[1]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
38
src/types.h
38
src/types.h
|
@ -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
29
src/util.h
Normal 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
114
src/vector.c
Normal 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
113
src/vector.h
Normal 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); \
|
||||||
|
})
|
Reference in a new issue