Add first parts of interface config handling
This commit is contained in:
parent
9e059f898a
commit
daa68c4b2d
19 changed files with 741 additions and 104 deletions
|
@ -1,5 +1,4 @@
|
||||||
project('pnc', 'c', default_options : ['c_std=c11'])
|
project('neco', 'c', default_options : ['c_std=gnu11'])
|
||||||
add_project_arguments('-D_DEFAULT_SOURCE', language : 'c')
|
|
||||||
|
|
||||||
cc = meson.get_compiler('c')
|
cc = meson.get_compiler('c')
|
||||||
ubox_dep = cc.find_library('ubox')
|
ubox_dep = cc.find_library('ubox')
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include "config-ini.h"
|
#include "config-ini.h"
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
|
@ -4,19 +4,19 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct _ini_field {
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
char *key;
|
char *key;
|
||||||
char *value;
|
char *value;
|
||||||
} ini_field_t;
|
} ini_field_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct _ini_section {
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
struct list_head fields;
|
struct list_head fields;
|
||||||
char *name;
|
char *name;
|
||||||
} ini_section_t;
|
} ini_section_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct _ini_file {
|
||||||
struct list_head sections;
|
struct list_head sections;
|
||||||
} ini_file_t;
|
} ini_file_t;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#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.h>
|
|
||||||
#include <libubox/avl-cmp.h>
|
#include <libubox/avl-cmp.h>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
@ -13,22 +14,9 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef struct _load_ctx {
|
||||||
LOAD_TYPE_INTERFACE,
|
struct avl_tree subtypes;
|
||||||
LOAD_TYPE_SUBCONFIG,
|
struct avl_tree devices;
|
||||||
LOAD_TYPE_GENERATOR,
|
|
||||||
LOAD_TYPE_DEVICE,
|
|
||||||
N_LOAD_TYPES,
|
|
||||||
} load_type_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
struct avl_node node;
|
|
||||||
char *type;
|
|
||||||
ini_file_t *data;
|
|
||||||
} load_object_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
struct avl_tree objects[N_LOAD_TYPES];
|
|
||||||
} load_ctx_t;
|
} load_ctx_t;
|
||||||
|
|
||||||
static const char * extname(const char *filename) {
|
static const char * extname(const char *filename) {
|
||||||
|
@ -36,29 +24,24 @@ static const char * extname(const char *filename) {
|
||||||
return dot ? (dot+1) : NULL;
|
return dot ? (dot+1) : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_object(load_object_t *obj) {
|
static bool isfile(int fd) {
|
||||||
free_ini_file(obj->data);
|
struct stat buf;
|
||||||
free(obj->type);
|
if (fstat(fd, &buf) < 0)
|
||||||
free(NODE_NAME(obj));
|
return false;
|
||||||
free(obj);
|
|
||||||
|
return (buf.st_mode & S_IFMT) == S_IFREG;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) {
|
static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) {
|
||||||
const char *ext = extname(filename);
|
const char *ext = extname(filename);
|
||||||
if (!ext) {
|
if (!ext)
|
||||||
errno = EINVAL;
|
return true;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
load_type_t type;
|
struct avl_tree *target_tree;
|
||||||
if (strcmp(ext, "interface") == 0)
|
if (strcmp(ext, "sub") == 0 || strcmp(ext, "gen") == 0)
|
||||||
type = LOAD_TYPE_INTERFACE;
|
target_tree = &ctx->subtypes;
|
||||||
else if (strcmp(ext, "sub") == 0)
|
else if (get_device_type(ext))
|
||||||
type = LOAD_TYPE_SUBCONFIG;
|
target_tree = &ctx->devices;
|
||||||
else if (strcmp(ext, "gen") == 0)
|
|
||||||
type = LOAD_TYPE_GENERATOR;
|
|
||||||
else if (strcmp(ext, "bridge") == 0)
|
|
||||||
type = LOAD_TYPE_DEVICE;
|
|
||||||
else
|
else
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -66,6 +49,11 @@ static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) {
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (!isfile(fd)) {
|
||||||
|
close(fd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
FILE *f = fdopen(fd, "r");
|
FILE *f = fdopen(fd, "r");
|
||||||
if (!f) {
|
if (!f) {
|
||||||
close(fd);
|
close(fd);
|
||||||
|
@ -78,7 +66,7 @@ static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) {
|
||||||
if (!data)
|
if (!data)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
load_object_t *obj = calloc(1, sizeof(*obj));
|
config_object_t *obj = calloc(1, sizeof(*obj));
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
free_ini_file(data);
|
free_ini_file(data);
|
||||||
return false;
|
return false;
|
||||||
|
@ -87,18 +75,18 @@ static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) {
|
||||||
|
|
||||||
char *name = strndup(filename, (ext - filename) - 1);
|
char *name = strndup(filename, (ext - filename) - 1);
|
||||||
if (!name) {
|
if (!name) {
|
||||||
free_object(obj);
|
config_object_free(obj);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
NODE_NAME(obj) = name;
|
NODE_NAME(obj) = name;
|
||||||
|
|
||||||
obj->type = strdup(ext);
|
obj->type = strdup(ext);
|
||||||
if (!obj->type) {
|
if (!obj->type) {
|
||||||
free_object(obj);
|
config_object_free(obj);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
avl_insert(&ctx->objects[type], &obj->node);
|
avl_insert(target_tree, &obj->node);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -111,10 +99,8 @@ static bool read_config_dir(load_ctx_t *ctx, const char *path) {
|
||||||
int fd = dirfd(dir);
|
int fd = dirfd(dir);
|
||||||
|
|
||||||
struct dirent *ent;
|
struct dirent *ent;
|
||||||
while ((ent = readdir(dir)) != NULL) {
|
while ((ent = readdir(dir)) != NULL)
|
||||||
if (ent->d_type == DT_REG)
|
read_config_file(ctx, fd, ent->d_name);
|
||||||
read_config_file(ctx, fd, ent->d_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
|
|
||||||
|
@ -123,23 +109,21 @@ static bool read_config_dir(load_ctx_t *ctx, const char *path) {
|
||||||
|
|
||||||
bool read_config(const char *path) {
|
bool read_config(const char *path) {
|
||||||
load_ctx_t ctx;
|
load_ctx_t ctx;
|
||||||
for (size_t i = 0; i < N_LOAD_TYPES; i++)
|
avl_init(&ctx.subtypes, avl_strcmp, true, NULL);
|
||||||
avl_init(&ctx.objects[i], avl_strcmp, true, NULL);
|
avl_init(&ctx.devices, avl_strcmp, true, NULL);
|
||||||
|
|
||||||
bool ret = read_config_dir(&ctx, path);
|
bool ret = read_config_dir(&ctx, path);
|
||||||
|
|
||||||
for (size_t i = 0; i < N_LOAD_TYPES; i++) {
|
struct avl_tree *subtypes = config_process_subtypes(&ctx.subtypes);
|
||||||
load_object_t *obj;
|
struct avl_tree *devices = config_process_devices(&ctx.devices);
|
||||||
avl_for_each_element(&ctx.objects[i], obj, node) {
|
|
||||||
printf("%s(%u): %s\n", obj->type, (unsigned)i, NODE_NAME(obj));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < N_LOAD_TYPES; i++) {
|
free(subtypes);
|
||||||
load_object_t *obj, *tmp;
|
|
||||||
avl_for_each_element_safe(&ctx.objects[i], obj, node, tmp)
|
device_t *dev, *tmp;
|
||||||
free_object(obj);
|
avl_remove_all_elements(devices, dev, node, tmp)
|
||||||
}
|
dev->type->free_device(dev);
|
||||||
|
|
||||||
|
free(devices);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
135
src/config-process.c
Normal file
135
src/config-process.c
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
#include "config-process.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "keywords.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;
|
||||||
|
|
||||||
|
void config_object_free(config_object_t *obj) {
|
||||||
|
free_ini_file(obj->data);
|
||||||
|
free(obj->type);
|
||||||
|
free(NODE_NAME(obj));
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool subtype_supported(const process_ctx_t *ctx, const char *type) {
|
||||||
|
switch (lookup_keyword(type)) {
|
||||||
|
case KW_Properties:
|
||||||
|
case KW_Generate:
|
||||||
|
case KW_Static:
|
||||||
|
case KW_DHCP:
|
||||||
|
case KW_DHCPv6:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return avl_find(ctx->ret, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static config_subtype_t * config_process_subtype(const process_ctx_t *ctx, config_object_t *obj) {
|
||||||
|
ini_section_t *section;
|
||||||
|
list_for_each_entry(section, &obj->data->sections, node) {
|
||||||
|
printf("%s %s %i\n", NODE_NAME(obj), section->name, subtype_supported(ctx, section->name));
|
||||||
|
|
||||||
|
if (!subtype_supported(ctx, section->name))
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
config_subtype_t *ret = calloc(1, sizeof(*ret));
|
||||||
|
if (!ret)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char *name = strdup(NODE_NAME(obj));
|
||||||
|
if (!name) {
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_NAME(ret) = name;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct avl_tree * config_process_subtypes(struct avl_tree *sub) {
|
||||||
|
process_ctx_t ctx;
|
||||||
|
ctx.ret = calloc(1, sizeof(*ctx.ret));
|
||||||
|
if (!ctx.ret)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
avl_init(ctx.ret, avl_strcmp, false, NULL);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
size_t processed = 0;
|
||||||
|
|
||||||
|
config_object_t *obj, *tmp;
|
||||||
|
avl_for_each_element_safe(sub, obj, node, tmp) {
|
||||||
|
config_subtype_t *t = config_process_subtype(&ctx, obj);
|
||||||
|
if (!t)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
avl_delete(sub, &obj->node);
|
||||||
|
config_object_free(obj);
|
||||||
|
|
||||||
|
avl_insert(ctx.ret, &t->node);
|
||||||
|
|
||||||
|
processed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!processed)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static device_t * config_process_device(config_object_t *obj) {
|
||||||
|
const device_type_t *type = get_device_type(obj->type);
|
||||||
|
|
||||||
|
assert(type != NULL);
|
||||||
|
|
||||||
|
return type->process_config(NODE_NAME(obj), obj->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct avl_tree * config_process_devices(struct avl_tree *devices) {
|
||||||
|
process_ctx_t ctx;
|
||||||
|
ctx.ret = calloc(1, sizeof(*ctx.ret));
|
||||||
|
if (!ctx.ret)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
avl_init(ctx.ret, avl_strcmp, false, NULL);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
size_t processed = 0;
|
||||||
|
|
||||||
|
config_object_t *obj, *tmp;
|
||||||
|
avl_for_each_element_safe(devices, obj, node, tmp) {
|
||||||
|
device_t *device = config_process_device(obj);
|
||||||
|
if (!device)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
avl_delete(devices, &obj->node);
|
||||||
|
config_object_free(obj);
|
||||||
|
|
||||||
|
avl_insert(ctx.ret, &device->node);
|
||||||
|
|
||||||
|
processed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!processed)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.ret;
|
||||||
|
}
|
15
src/config-process.h
Normal file
15
src/config-process.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config-ini.h"
|
||||||
|
|
||||||
|
#include <libubox/avl.h>
|
||||||
|
|
||||||
|
typedef struct _config_object {
|
||||||
|
struct avl_node node;
|
||||||
|
char *type;
|
||||||
|
ini_file_t *data;
|
||||||
|
} config_object_t;
|
||||||
|
|
||||||
|
void config_object_free(config_object_t *obj);
|
||||||
|
struct avl_tree * config_process_subtypes(struct avl_tree *sub);
|
||||||
|
struct avl_tree * config_process_devices(struct avl_tree *devices);
|
15
src/device-bridge.c
Normal file
15
src/device-bridge.c
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#include "device.h"
|
||||||
|
|
||||||
|
static device_t * bridge_process_config(const char *name, const ini_file_t *config) {
|
||||||
|
printf("Bridge: %s\n", name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_type_t device_type_bridge = {
|
||||||
|
.process_config = bridge_process_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((constructor))
|
||||||
|
static void bridge_constructor(void) {
|
||||||
|
register_device_type("bridge", &device_type_bridge);
|
||||||
|
}
|
202
src/device-interface.c
Normal file
202
src/device-interface.c
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
#include "device.h"
|
||||||
|
#include "keywords.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "vector.h"
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/ether.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static device_type_t device_type_interface;
|
||||||
|
|
||||||
|
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_interface {
|
||||||
|
device_t device;
|
||||||
|
|
||||||
|
struct ether_addr macaddr;
|
||||||
|
uint16_t mtu;
|
||||||
|
|
||||||
|
ipaddr_prefix_vector_t addrs;
|
||||||
|
} device_interface_t;
|
||||||
|
|
||||||
|
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_interface_t *iface, const ini_section_t *section) {
|
||||||
|
ini_field_t *field;
|
||||||
|
|
||||||
|
list_for_each_entry(field, §ion->fields, node) {
|
||||||
|
switch (lookup_keyword(field->key)) {
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "interface: [Device]: unknown field %s\n", field->key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool process_section_static(device_interface_t *iface, const ini_section_t *section) {
|
||||||
|
ini_field_t *field;
|
||||||
|
|
||||||
|
list_for_each_entry(field, §ion->fields, node) {
|
||||||
|
switch (lookup_keyword(field->key)) {
|
||||||
|
case KW_Address: {
|
||||||
|
ipaddr_prefix_t p;
|
||||||
|
if (!parse_prefix(&p, field->value, true)) {
|
||||||
|
fprintf(stderr, "interface: [Static]: unable to parse Address %s\n", field->value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!VECTOR_ADD(iface->addrs, p)) {
|
||||||
|
fprintf(stderr, "interface: [Static]: adding address failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "interface: [Static]: unknown field %s\n", field->key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void interface_free_device(device_t *dev) {
|
||||||
|
device_interface_t *iface = container_of(dev, device_interface_t, device);
|
||||||
|
|
||||||
|
VECTOR_FREE(iface->addrs);
|
||||||
|
free(NODE_NAME(dev));
|
||||||
|
free(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static device_t * interface_process_config(const char *name, const ini_file_t *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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ini_section_t *section;
|
||||||
|
list_for_each_entry(section, &config->sections, node) {
|
||||||
|
switch (lookup_keyword(section->name)) {
|
||||||
|
case KW_Device:
|
||||||
|
if (!process_section_device(iface, section))
|
||||||
|
goto err;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KW_Static:
|
||||||
|
if (!process_section_static(iface, section))
|
||||||
|
goto err;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "interface: unknown section %s\n", section->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dev;
|
||||||
|
|
||||||
|
err:
|
||||||
|
interface_free_device(dev);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static device_type_t device_type_interface = {
|
||||||
|
.process_config = interface_process_config,
|
||||||
|
.free_device = interface_free_device,
|
||||||
|
};
|
||||||
|
|
||||||
|
__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);
|
||||||
|
}
|
23
src/device.h
Normal file
23
src/device.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config-ini.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, const ini_file_t *config);
|
||||||
|
void (*free_device)(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);
|
31
src/keywords.c
Normal file
31
src/keywords.c
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#include "keywords.h"
|
||||||
|
|
||||||
|
#include <libubox/utils.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static const char *const keywords[] = {
|
||||||
|
|
||||||
|
#define KW(kw) #kw,
|
||||||
|
#include "keywords.def"
|
||||||
|
#undef KW
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static int compare_keywords(const void *a, const void *b) {
|
||||||
|
const char *const *ea = a, *const *eb = b;
|
||||||
|
return strcmp(*ea, *eb);
|
||||||
|
}
|
||||||
|
|
||||||
|
keyword_t lookup_keyword(const char *keyword) {
|
||||||
|
const char *const *entry = bsearch(
|
||||||
|
&keyword, keywords, ARRAY_SIZE(keywords), sizeof(const char *),
|
||||||
|
compare_keywords
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!entry)
|
||||||
|
return UNKNOWN_KEYWORD;
|
||||||
|
|
||||||
|
return (keyword_t) (entry - keywords + 1);
|
||||||
|
}
|
9
src/keywords.def
Normal file
9
src/keywords.def
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/* Config section and field names; keep sorted! */
|
||||||
|
|
||||||
|
KW(Address)
|
||||||
|
KW(DHCP)
|
||||||
|
KW(DHCPv6)
|
||||||
|
KW(Device)
|
||||||
|
KW(Generate)
|
||||||
|
KW(Properties)
|
||||||
|
KW(Static)
|
12
src/keywords.h
Normal file
12
src/keywords.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum _keyword {
|
||||||
|
UNKNOWN_KEYWORD = 0,
|
||||||
|
|
||||||
|
#define KW(kw) KW_##kw,
|
||||||
|
#include "keywords.def"
|
||||||
|
#undef KW
|
||||||
|
|
||||||
|
} keyword_t;
|
||||||
|
|
||||||
|
keyword_t lookup_keyword(const char *keyword);
|
|
@ -1,7 +1,13 @@
|
||||||
src = [
|
src = [
|
||||||
'pnc.c',
|
'neco.c',
|
||||||
'config-ini.c',
|
'config-ini.c',
|
||||||
'config-load.c',
|
'config-load.c',
|
||||||
|
'config-process.c',
|
||||||
|
'device.c',
|
||||||
|
'device-bridge.c',
|
||||||
|
'device-interface.c',
|
||||||
|
'keywords.c',
|
||||||
|
'vector.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
executable('pnc', sources: src, dependencies: [ubox_dep])
|
executable('neco', sources: src, dependencies: [ubox_dep])
|
||||||
|
|
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;
|
|
3
src/util.h
Normal file
3
src/util.h
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define NODE_NAME(c) (*(char **)&(c)->node.key)
|
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