From 9e059f898ae1b0528cdde553fd51c661901e283a Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 29 Dec 2018 17:00:31 +0100 Subject: [PATCH 01/10] Load config dirs --- meson.build | 3 +- src/config-ini.c | 8 +-- src/config-ini.h | 4 +- src/config-load.c | 144 +++++++++++++++++++++++++++++++++++++--------- src/config-load.h | 2 +- src/pnc.c | 2 +- 6 files changed, 125 insertions(+), 38 deletions(-) diff --git a/meson.build b/meson.build index aaa8cf6..f8089e4 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,5 @@ -project('pnc', 'c') +project('pnc', 'c', default_options : ['c_std=c11']) +add_project_arguments('-D_DEFAULT_SOURCE', language : 'c') cc = meson.get_compiler('c') ubox_dep = cc.find_library('ubox') diff --git a/src/config-ini.c b/src/config-ini.c index 7cfa1ff..30275fa 100644 --- a/src/config-ini.c +++ b/src/config-ini.c @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -67,11 +66,7 @@ static ini_section_t * add_section(ini_file_t *file, const char *name) { return section; } -ini_file_t * read_ini_file(const char *filename) { - FILE *f = fopen(filename, "r"); - if (!f) - return NULL; - +ini_file_t * read_ini_file(FILE *f) { ini_file_t *file = calloc(1, sizeof(*file)); if (!file) goto error; @@ -135,7 +130,6 @@ ini_file_t * read_ini_file(const char *filename) { error: free(line); - fclose(f); if (err) { free_ini_file(file); diff --git a/src/config-ini.h b/src/config-ini.h index 802195d..58f6608 100644 --- a/src/config-ini.h +++ b/src/config-ini.h @@ -2,6 +2,8 @@ #include +#include + typedef struct { struct list_head node; char *key; @@ -18,5 +20,5 @@ typedef struct { struct list_head sections; } ini_file_t; -ini_file_t * read_ini_file(const char *filename); +ini_file_t * read_ini_file(FILE *f); void free_ini_file(ini_file_t *file); diff --git a/src/config-load.c b/src/config-load.c index 9335813..28b5953 100644 --- a/src/config-load.c +++ b/src/config-load.c @@ -2,54 +2,144 @@ #include "config-ini.h" #include "types.h" +#include +#include + +#include +#include #include -#include +#include #include #include +#include -static const char * simple_basename(const char *path) { - const char *slash = strrchr(path, '/'); - return slash ? (slash+1) : path; -} +typedef enum { + LOAD_TYPE_INTERFACE, + LOAD_TYPE_SUBCONFIG, + 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; 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; +static void free_object(load_object_t *obj) { + free_ini_file(obj->data); + free(obj->type); + free(NODE_NAME(obj)); + free(obj); } -bool read_config_file(const char *path) { - const char *filename = simple_basename(path); +static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) { const char *ext = extname(filename); if (!ext) { errno = EINVAL; return false; } - char *name = strndup(filename, (ext - filename) - 1); + load_type_t type; + if (strcmp(ext, "interface") == 0) + type = LOAD_TYPE_INTERFACE; + else if (strcmp(ext, "sub") == 0) + type = LOAD_TYPE_SUBCONFIG; + else if (strcmp(ext, "gen") == 0) + type = LOAD_TYPE_GENERATOR; + else if (strcmp(ext, "bridge") == 0) + type = LOAD_TYPE_DEVICE; + else + return true; + + int fd = openat(dirfd, filename, O_RDONLY); + if (fd < 0) + return false; + + FILE *f = fdopen(fd, "r"); + if (!f) { + close(fd); + return false; + } + + ini_file_t *data = read_ini_file(f); + fclose(f); - if (strcmp(ext, "interface") == 0) { - interface_config_t *iface = read_interface_config(path); - free(iface); + if (!data) + return false; + + load_object_t *obj = calloc(1, sizeof(*obj)); + if (!obj) { + free_ini_file(data); + return false; + } + obj->data = data; + + char *name = strndup(filename, (ext - filename) - 1); + if (!name) { + free_object(obj); + return false; + } + NODE_NAME(obj) = name; + + obj->type = strdup(ext); + if (!obj->type) { + free_object(obj); + return false; } - free(name); + avl_insert(&ctx->objects[type], &obj->node); return true; } + +static bool read_config_dir(load_ctx_t *ctx, const char *path) { + DIR *dir = opendir(path); + if (!dir) + return false; + + int fd = dirfd(dir); + + struct dirent *ent; + while ((ent = readdir(dir)) != NULL) { + if (ent->d_type == DT_REG) + read_config_file(ctx, fd, ent->d_name); + } + + closedir(dir); + + return true; +} + +bool read_config(const char *path) { + load_ctx_t ctx; + for (size_t i = 0; i < N_LOAD_TYPES; i++) + avl_init(&ctx.objects[i], avl_strcmp, true, NULL); + + bool ret = read_config_dir(&ctx, path); + + for (size_t i = 0; i < N_LOAD_TYPES; i++) { + load_object_t *obj; + 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++) { + load_object_t *obj, *tmp; + avl_for_each_element_safe(&ctx.objects[i], obj, node, tmp) + free_object(obj); + } + + return ret; +} diff --git a/src/config-load.h b/src/config-load.h index 0c278ee..e2b4736 100644 --- a/src/config-load.h +++ b/src/config-load.h @@ -2,4 +2,4 @@ #include -bool read_config_file(const char *path); +bool read_config(const char *path); diff --git a/src/pnc.c b/src/pnc.c index 651d140..942ba2f 100644 --- a/src/pnc.c +++ b/src/pnc.c @@ -1,6 +1,6 @@ #include "config-load.h" int main(int argc, char *argv[]) { - read_config_file(argv[1]); + read_config(argv[1]); return 0; } From daa68c4b2de9814b8331e7b9079d5fc49bee35c1 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 5 Jan 2019 22:06:45 +0100 Subject: [PATCH 02/10] Add first parts of interface config handling --- meson.build | 3 +- src/config-ini.c | 1 - src/config-ini.h | 6 +- src/config-load.c | 100 +++++++++----------- src/config-process.c | 135 +++++++++++++++++++++++++++ src/config-process.h | 15 +++ src/device-bridge.c | 15 +++ src/device-interface.c | 202 +++++++++++++++++++++++++++++++++++++++++ src/device.c | 15 +++ src/device.h | 23 +++++ src/keywords.c | 31 +++++++ src/keywords.def | 9 ++ src/keywords.h | 12 +++ src/meson.build | 10 +- src/{pnc.c => neco.c} | 0 src/types.h | 38 -------- src/util.h | 3 + src/vector.c | 114 +++++++++++++++++++++++ src/vector.h | 113 +++++++++++++++++++++++ 19 files changed, 741 insertions(+), 104 deletions(-) create mode 100644 src/config-process.c create mode 100644 src/config-process.h create mode 100644 src/device-bridge.c create mode 100644 src/device-interface.c create mode 100644 src/device.c create mode 100644 src/device.h create mode 100644 src/keywords.c create mode 100644 src/keywords.def create mode 100644 src/keywords.h rename src/{pnc.c => neco.c} (100%) delete mode 100644 src/types.h create mode 100644 src/util.h create mode 100644 src/vector.c create mode 100644 src/vector.h diff --git a/meson.build b/meson.build index f8089e4..81cf8ee 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,4 @@ -project('pnc', 'c', default_options : ['c_std=c11']) -add_project_arguments('-D_DEFAULT_SOURCE', language : 'c') +project('neco', 'c', default_options : ['c_std=gnu11']) cc = meson.get_compiler('c') ubox_dep = cc.find_library('ubox') diff --git a/src/config-ini.c b/src/config-ini.c index 30275fa..0461e13 100644 --- a/src/config-ini.c +++ b/src/config-ini.c @@ -1,5 +1,4 @@ #include "config-ini.h" -#include "types.h" #include #include diff --git a/src/config-ini.h b/src/config-ini.h index 58f6608..48400fd 100644 --- a/src/config-ini.h +++ b/src/config-ini.h @@ -4,19 +4,19 @@ #include -typedef struct { +typedef struct _ini_field { struct list_head node; char *key; char *value; } ini_field_t; -typedef struct { +typedef struct _ini_section { struct list_head node; struct list_head fields; char *name; } ini_section_t; -typedef struct { +typedef struct _ini_file { struct list_head sections; } ini_file_t; diff --git a/src/config-load.c b/src/config-load.c index 28b5953..92abca0 100644 --- a/src/config-load.c +++ b/src/config-load.c @@ -1,10 +1,11 @@ #include "config-load.h" -#include "config-ini.h" -#include "types.h" +#include "config-process.h" +#include "device.h" +#include "util.h" -#include #include +#include #include #include #include @@ -13,22 +14,9 @@ #include #include -typedef enum { - LOAD_TYPE_INTERFACE, - LOAD_TYPE_SUBCONFIG, - 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]; +typedef struct _load_ctx { + struct avl_tree subtypes; + struct avl_tree devices; } load_ctx_t; static const char * extname(const char *filename) { @@ -36,29 +24,24 @@ static const char * extname(const char *filename) { return dot ? (dot+1) : NULL; } -static void free_object(load_object_t *obj) { - free_ini_file(obj->data); - free(obj->type); - free(NODE_NAME(obj)); - free(obj); +static bool isfile(int fd) { + struct stat buf; + if (fstat(fd, &buf) < 0) + return false; + + return (buf.st_mode & S_IFMT) == S_IFREG; } static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) { const char *ext = extname(filename); - if (!ext) { - errno = EINVAL; - return false; - } + if (!ext) + return true; - load_type_t type; - if (strcmp(ext, "interface") == 0) - type = LOAD_TYPE_INTERFACE; - else if (strcmp(ext, "sub") == 0) - type = LOAD_TYPE_SUBCONFIG; - else if (strcmp(ext, "gen") == 0) - type = LOAD_TYPE_GENERATOR; - else if (strcmp(ext, "bridge") == 0) - type = LOAD_TYPE_DEVICE; + struct avl_tree *target_tree; + if (strcmp(ext, "sub") == 0 || strcmp(ext, "gen") == 0) + target_tree = &ctx->subtypes; + else if (get_device_type(ext)) + target_tree = &ctx->devices; else return true; @@ -66,6 +49,11 @@ static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) { if (fd < 0) return false; + if (!isfile(fd)) { + close(fd); + return true; + } + FILE *f = fdopen(fd, "r"); if (!f) { close(fd); @@ -78,7 +66,7 @@ static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) { if (!data) return false; - load_object_t *obj = calloc(1, sizeof(*obj)); + config_object_t *obj = calloc(1, sizeof(*obj)); if (!obj) { free_ini_file(data); 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); if (!name) { - free_object(obj); + config_object_free(obj); return false; } NODE_NAME(obj) = name; obj->type = strdup(ext); if (!obj->type) { - free_object(obj); + config_object_free(obj); return false; } - avl_insert(&ctx->objects[type], &obj->node); + avl_insert(target_tree, &obj->node); return true; } @@ -111,10 +99,8 @@ static bool read_config_dir(load_ctx_t *ctx, const char *path) { int fd = dirfd(dir); struct dirent *ent; - while ((ent = readdir(dir)) != NULL) { - if (ent->d_type == DT_REG) - read_config_file(ctx, fd, ent->d_name); - } + while ((ent = readdir(dir)) != NULL) + read_config_file(ctx, fd, ent->d_name); closedir(dir); @@ -123,23 +109,21 @@ static bool read_config_dir(load_ctx_t *ctx, const char *path) { bool read_config(const char *path) { load_ctx_t ctx; - for (size_t i = 0; i < N_LOAD_TYPES; i++) - avl_init(&ctx.objects[i], avl_strcmp, true, NULL); + avl_init(&ctx.subtypes, avl_strcmp, true, NULL); + avl_init(&ctx.devices, avl_strcmp, true, NULL); bool ret = read_config_dir(&ctx, path); - for (size_t i = 0; i < N_LOAD_TYPES; i++) { - load_object_t *obj; - avl_for_each_element(&ctx.objects[i], obj, node) { - printf("%s(%u): %s\n", obj->type, (unsigned)i, NODE_NAME(obj)); - } - } + struct avl_tree *subtypes = config_process_subtypes(&ctx.subtypes); + struct avl_tree *devices = config_process_devices(&ctx.devices); - for (size_t i = 0; i < N_LOAD_TYPES; i++) { - load_object_t *obj, *tmp; - avl_for_each_element_safe(&ctx.objects[i], obj, node, tmp) - free_object(obj); - } + free(subtypes); + + device_t *dev, *tmp; + avl_remove_all_elements(devices, dev, node, tmp) + dev->type->free_device(dev); + + free(devices); return ret; } diff --git a/src/config-process.c b/src/config-process.c new file mode 100644 index 0000000..e71b228 --- /dev/null +++ b/src/config-process.c @@ -0,0 +1,135 @@ +#include "config-process.h" +#include "device.h" +#include "keywords.h" +#include "util.h" + +#include + +#include +#include +#include + +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; +} diff --git a/src/config-process.h b/src/config-process.h new file mode 100644 index 0000000..501e765 --- /dev/null +++ b/src/config-process.h @@ -0,0 +1,15 @@ +#pragma once + +#include "config-ini.h" + +#include + +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); diff --git a/src/device-bridge.c b/src/device-bridge.c new file mode 100644 index 0000000..f973dbf --- /dev/null +++ b/src/device-bridge.c @@ -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); +} diff --git a/src/device-interface.c b/src/device-interface.c new file mode 100644 index 0000000..5ac7cee --- /dev/null +++ b/src/device-interface.c @@ -0,0 +1,202 @@ +#include "device.h" +#include "keywords.h" +#include "util.h" +#include "vector.h" + +#include +#include +#include + +#include +#include +#include +#include + +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); +} diff --git a/src/device.c b/src/device.c new file mode 100644 index 0000000..a773036 --- /dev/null +++ b/src/device.c @@ -0,0 +1,15 @@ +#include "device.h" +#include "util.h" + +#include + +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); +} diff --git a/src/device.h b/src/device.h new file mode 100644 index 0000000..f437021 --- /dev/null +++ b/src/device.h @@ -0,0 +1,23 @@ +#pragma once + +#include "config-ini.h" + +#include + +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); diff --git a/src/keywords.c b/src/keywords.c new file mode 100644 index 0000000..12393bd --- /dev/null +++ b/src/keywords.c @@ -0,0 +1,31 @@ +#include "keywords.h" + +#include + +#include +#include + +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); +} diff --git a/src/keywords.def b/src/keywords.def new file mode 100644 index 0000000..0dbd866 --- /dev/null +++ b/src/keywords.def @@ -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) diff --git a/src/keywords.h b/src/keywords.h new file mode 100644 index 0000000..05d809e --- /dev/null +++ b/src/keywords.h @@ -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); diff --git a/src/meson.build b/src/meson.build index c24ca21..b088e39 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,7 +1,13 @@ src = [ - 'pnc.c', + 'neco.c', 'config-ini.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]) diff --git a/src/pnc.c b/src/neco.c similarity index 100% rename from src/pnc.c rename to src/neco.c diff --git a/src/types.h b/src/types.h deleted file mode 100644 index cabaac6..0000000 --- a/src/types.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include - -#include -#include - -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; diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..31b13b6 --- /dev/null +++ b/src/util.h @@ -0,0 +1,3 @@ +#pragma once + +#define NODE_NAME(c) (*(char **)&(c)->node.key) diff --git a/src/vector.c b/src/vector.c new file mode 100644 index 0000000..41cd0cc --- /dev/null +++ b/src/vector.c @@ -0,0 +1,114 @@ +/** + \file + + Typesafe dynamically sized arrays +*/ + + +#include "vector.h" + +#include +#include +#include + +/** 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--; +} diff --git a/src/vector.h b/src/vector.h new file mode 100644 index 0000000..02ea804 --- /dev/null +++ b/src/vector.h @@ -0,0 +1,113 @@ +/** + \file + + Typesafe dynamically sized arrays +*/ + + +#pragma once + +#include +#include + +/** 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); \ +}) From b4bdc797028a3789bb5e0c68bc428bfd54bd200e Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 5 Jan 2019 22:12:18 +0100 Subject: [PATCH 03/10] Add LICENSE --- LICENSE | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4bad81e --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012-2019, Matthias Schiffer +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. From 44cb17317c40fa9d39b3402f0826006f20387be5 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 6 Jan 2019 07:20:08 +0100 Subject: [PATCH 04/10] Implement simple setting of link state and IP addresses --- meson.build | 4 +- src/config-load.c | 8 ++- src/device-interface.c | 147 +++++++++++++++++++++++++++++++++++++++-- src/device.h | 6 +- src/meson.build | 8 ++- src/neco.c | 7 ++ src/netlink.c | 38 +++++++++++ src/netlink.h | 12 ++++ 8 files changed, 219 insertions(+), 11 deletions(-) create mode 100644 src/netlink.c create mode 100644 src/netlink.h diff --git a/meson.build b/meson.build index 81cf8ee..d7a554a 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,8 @@ project('neco', 'c', default_options : ['c_std=gnu11']) cc = meson.get_compiler('c') -ubox_dep = cc.find_library('ubox') +libubox_dep = cc.find_library('ubox') + +libmnl_dep = dependency('libmnl', method : 'pkg-config') subdir('src') diff --git a/src/config-load.c b/src/config-load.c index 92abca0..63a87b4 100644 --- a/src/config-load.c +++ b/src/config-load.c @@ -120,8 +120,14 @@ bool read_config(const char *path) { free(subtypes); 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_device(dev); + dev->type->free(dev); free(devices); diff --git a/src/device-interface.c b/src/device-interface.c index 5ac7cee..a2aae14 100644 --- a/src/device-interface.c +++ b/src/device-interface.c @@ -1,17 +1,23 @@ #include "device.h" #include "keywords.h" +#include "netlink.h" #include "util.h" #include "vector.h" -#include -#include -#include - #include #include #include #include +#include +#include +#include +#include + +#include +#include +#include + static device_type_t device_type_interface; typedef struct _ipaddr { @@ -144,7 +150,7 @@ static bool process_section_static(device_interface_t *iface, const ini_section_ return true; } -static void interface_free_device(device_t *dev) { +static void interface_free(device_t *dev) { device_interface_t *iface = container_of(dev, device_interface_t, device); VECTOR_FREE(iface->addrs); @@ -152,6 +158,129 @@ static void interface_free_device(device_t *dev) { free(iface); } +static bool interface_set_link_flags(unsigned index, 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 = index; + 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 interface_set_link_state(unsigned index, bool up) { + return interface_set_link_flags(index, IFF_UP, up ? IFF_UP : 0); +} + +static bool interface_set_ipaddr(unsigned index, 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 = index; + 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 void interface_init(device_t *dev) { + device_interface_t *iface = container_of(dev, device_interface_t, device); + + unsigned index = if_nametoindex(NODE_NAME(dev)); + if (!index) + return; + + interface_set_link_state(index, true); + + for (size_t i = 0; i < VECTOR_LEN(iface->addrs); i++) + interface_set_ipaddr(index, &VECTOR_INDEX(iface->addrs, i), true); +} + +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 index = if_nametoindex(NODE_NAME(dev)); + if (!index) + return; + + for (size_t i = 0; i < VECTOR_LEN(iface->addrs); i++) + interface_set_ipaddr(index, &VECTOR_INDEX(iface->addrs, i), false); + + interface_set_link_state(index, false); +} + static device_t * interface_process_config(const char *name, const ini_file_t *config) { device_interface_t *iface = calloc(1, sizeof(*iface)); if (!iface) @@ -187,13 +316,17 @@ static device_t * interface_process_config(const char *name, const ini_file_t *c return dev; err: - interface_free_device(dev); + interface_free(dev); return NULL; } static device_type_t device_type_interface = { .process_config = interface_process_config, - .free_device = interface_free_device, + .free = interface_free, + + .init = interface_init, + .update = interface_update, + .release = interface_release, }; __attribute__((constructor)) diff --git a/src/device.h b/src/device.h index f437021..33acfbb 100644 --- a/src/device.h +++ b/src/device.h @@ -16,7 +16,11 @@ typedef struct _device { 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 (*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); diff --git a/src/meson.build b/src/meson.build index b088e39..9056cf3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,7 +7,13 @@ src = [ 'device-bridge.c', 'device-interface.c', 'keywords.c', + 'netlink.c', 'vector.c', ] -executable('neco', sources: src, dependencies: [ubox_dep]) +dep = [ + libubox_dep, + libmnl_dep, +] + +executable('neco', sources: src, dependencies: dep) diff --git a/src/neco.c b/src/neco.c index 942ba2f..4e4d297 100644 --- a/src/neco.c +++ b/src/neco.c @@ -1,6 +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; } diff --git a/src/netlink.c b/src/netlink.c new file mode 100644 index 0000000..ec9c935 --- /dev/null +++ b/src/netlink.c @@ -0,0 +1,38 @@ +#include "netlink.h" + +#include + +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++; +} diff --git a/src/netlink.h b/src/netlink.h new file mode 100644 index 0000000..6529e92 --- /dev/null +++ b/src/netlink.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include +#include + +bool nl_init(void); +void nl_deinit(void); + +struct mnl_socket * nl_socket(void); +uint32_t nl_seq(void); From bcef77a7fb3b73d2a2fbcea51012014b62755bb5 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 31 May 2019 18:42:01 +0200 Subject: [PATCH 05/10] 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. --- meson.build | 1 + src/config-ini.c | 140 ----------------------------------------- src/config-ini.h | 24 ------- src/config-load.c | 110 +++----------------------------- src/config-process.c | 121 +++++++---------------------------- src/config-process.h | 13 +--- src/device-bridge.c | 4 +- src/device-interface.c | 117 ++++++++++++++++++---------------- src/device.h | 5 +- src/keywords.c | 31 --------- src/keywords.def | 9 --- src/keywords.h | 12 ---- src/meson.build | 5 +- src/util.h | 26 ++++++++ 14 files changed, 128 insertions(+), 490 deletions(-) delete mode 100644 src/config-ini.c delete mode 100644 src/config-ini.h delete mode 100644 src/keywords.c delete mode 100644 src/keywords.def delete mode 100644 src/keywords.h diff --git a/meson.build b/meson.build index d7a554a..0cbd657 100644 --- a/meson.build +++ b/meson.build @@ -3,6 +3,7 @@ project('neco', 'c', default_options : ['c_std=gnu11']) cc = meson.get_compiler('c') libubox_dep = cc.find_library('ubox') +libjson_c_dep = dependency('json-c', method : 'pkg-config') libmnl_dep = dependency('libmnl', method : 'pkg-config') subdir('src') diff --git a/src/config-ini.c b/src/config-ini.c deleted file mode 100644 index 0461e13..0000000 --- a/src/config-ini.c +++ /dev/null @@ -1,140 +0,0 @@ -#include "config-ini.h" - -#include -#include -#include -#include - -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(FILE *f) { - 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); - - if (err) { - free_ini_file(file); - errno = err; - return NULL; - } - - return file; -} diff --git a/src/config-ini.h b/src/config-ini.h deleted file mode 100644 index 48400fd..0000000 --- a/src/config-ini.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -#include - -typedef struct _ini_field { - struct list_head node; - char *key; - char *value; -} ini_field_t; - -typedef struct _ini_section { - struct list_head node; - struct list_head fields; - char *name; -} ini_section_t; - -typedef struct _ini_file { - struct list_head sections; -} ini_file_t; - -ini_file_t * read_ini_file(FILE *f); -void free_ini_file(ini_file_t *file); diff --git a/src/config-load.c b/src/config-load.c index 63a87b4..19b9d3a 100644 --- a/src/config-load.c +++ b/src/config-load.c @@ -14,110 +14,16 @@ #include #include -typedef struct _load_ctx { - struct avl_tree subtypes; - struct avl_tree devices; -} load_ctx_t; - -static const char * extname(const char *filename) { - const char *dot = strrchr(filename, '.'); - return dot ? (dot+1) : NULL; -} - -static bool isfile(int fd) { - struct stat buf; - if (fstat(fd, &buf) < 0) - return false; - - return (buf.st_mode & S_IFMT) == S_IFREG; -} - -static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) { - const char *ext = extname(filename); - if (!ext) - return true; - - struct avl_tree *target_tree; - if (strcmp(ext, "sub") == 0 || strcmp(ext, "gen") == 0) - target_tree = &ctx->subtypes; - else if (get_device_type(ext)) - target_tree = &ctx->devices; - else - return true; - - int fd = openat(dirfd, filename, O_RDONLY); - if (fd < 0) - return false; - - if (!isfile(fd)) { - close(fd); - return true; - } - - FILE *f = fdopen(fd, "r"); - if (!f) { - close(fd); - return false; - } - - ini_file_t *data = read_ini_file(f); - fclose(f); - - if (!data) - return false; - - config_object_t *obj = calloc(1, sizeof(*obj)); - if (!obj) { - free_ini_file(data); - return false; - } - obj->data = data; - - char *name = strndup(filename, (ext - filename) - 1); - if (!name) { - config_object_free(obj); - return false; - } - NODE_NAME(obj) = name; - - obj->type = strdup(ext); - if (!obj->type) { - config_object_free(obj); - return false; - } - - avl_insert(target_tree, &obj->node); - - return true; -} - -static bool read_config_dir(load_ctx_t *ctx, const char *path) { - DIR *dir = opendir(path); - if (!dir) - return false; - - int fd = dirfd(dir); - - struct dirent *ent; - while ((ent = readdir(dir)) != NULL) - read_config_file(ctx, fd, ent->d_name); - - closedir(dir); - - return true; -} - bool read_config(const char *path) { - load_ctx_t ctx; - avl_init(&ctx.subtypes, avl_strcmp, true, NULL); - avl_init(&ctx.devices, avl_strcmp, true, NULL); - - bool ret = read_config_dir(&ctx, path); + struct json_object *config = json_object_from_file(path); + if (!config) + return false; - struct avl_tree *subtypes = config_process_subtypes(&ctx.subtypes); - struct avl_tree *devices = config_process_devices(&ctx.devices); + struct avl_tree *devices = config_process(config); + json_object_put(config); - free(subtypes); + if (!devices) + return false; device_t *dev, *tmp; avl_for_each_element(devices, dev, node) @@ -131,5 +37,5 @@ bool read_config(const char *path) { free(devices); - return ret; + return true; } diff --git a/src/config-process.c b/src/config-process.c index e71b228..a0671cb 100644 --- a/src/config-process.c +++ b/src/config-process.c @@ -1,6 +1,5 @@ #include "config-process.h" #include "device.h" -#include "keywords.h" #include "util.h" #include @@ -9,6 +8,7 @@ #include #include + typedef struct _process_ctx { struct avl_tree *ret; } process_ctx_t; @@ -17,52 +17,28 @@ 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; +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(obj, "type"); - 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) + const device_type_t *type = get_device_type(typename ?: "interface"); + if (!type) return NULL; - char *name = strdup(NODE_NAME(obj)); - if (!name) { - free(ret); - return NULL; - } - - NODE_NAME(ret) = name; - - return ret; + return type->process_config(name, obj); } -struct avl_tree * config_process_subtypes(struct avl_tree *sub) { +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) @@ -70,65 +46,12 @@ struct avl_tree * config_process_subtypes(struct avl_tree *sub) { avl_init(ctx.ret, avl_strcmp, false, NULL); - while (true) { - size_t processed = 0; + json_object_object_foreach(devices, name, obj) { + device_t *device = config_process_device(name, obj); + if (!device) + continue; - 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; + avl_insert(ctx.ret, &device->node); } return ctx.ret; diff --git a/src/config-process.h b/src/config-process.h index 501e765..3903d78 100644 --- a/src/config-process.h +++ b/src/config-process.h @@ -1,15 +1,6 @@ #pragma once -#include "config-ini.h" - +#include #include -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); +struct avl_tree * config_process(struct json_object *config); diff --git a/src/device-bridge.c b/src/device-bridge.c index f973dbf..4a8d24c 100644 --- a/src/device-bridge.c +++ b/src/device-bridge.c @@ -1,6 +1,8 @@ #include "device.h" -static device_t * bridge_process_config(const char *name, const ini_file_t *config) { +#include + +static device_t * bridge_process_config(const char *name, struct json_object *config) { printf("Bridge: %s\n", name); return NULL; } diff --git a/src/device-interface.c b/src/device-interface.c index a2aae14..ef85089 100644 --- a/src/device-interface.c +++ b/src/device-interface.c @@ -1,5 +1,4 @@ #include "device.h" -#include "keywords.h" #include "netlink.h" #include "util.h" #include "vector.h" @@ -109,22 +108,43 @@ static bool parse_prefix(ipaddr_prefix_t *prefix, const char *str, bool allow_ho return true; } -static bool process_section_device(device_interface_t *iface, const ini_section_t *section) { - ini_field_t *field; +static void interface_free(device_t *dev) { + device_interface_t *iface = container_of(dev, device_interface_t, device); - list_for_each_entry(field, §ion->fields, node) { - switch (lookup_keyword(field->key)) { - default: - fprintf(stderr, "interface: [Device]: unknown field %s\n", field->key); - } - } + VECTOR_FREE(iface->addrs); + free(NODE_NAME(dev)); + free(iface); +} +static bool process_section_device(device_interface_t *iface, struct json_object *section) { return true; } -static bool process_section_static(device_interface_t *iface, const ini_section_t *section) { - ini_field_t *field; +static bool process_section_static(device_interface_t *iface, 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(iface->addrs, p)) { + fprintf(stderr, "interface: static: adding address failed\n"); + return false; + } + } + } + + + /* list_for_each_entry(field, §ion->fields, node) { switch (lookup_keyword(field->key)) { case KW_Address: { @@ -146,16 +166,42 @@ static bool process_section_static(device_interface_t *iface, const ini_section_ fprintf(stderr, "interface: [Static]: unknown field %s\n", field->key); } } + */ return true; } -static void interface_free(device_t *dev) { - device_interface_t *iface = container_of(dev, device_interface_t, device); +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; - VECTOR_FREE(iface->addrs); - free(NODE_NAME(dev)); - free(iface); + device_t *dev = &iface->device; + dev->type = &device_type_interface; + + NODE_NAME(dev) = strdup(name); + if (!NODE_NAME(dev)) { + free(iface); + return NULL; + } + + struct json_object *sec_device = neco_json_get_value(config, "device", json_type_object); + if (sec_device) { + if (!process_section_device(iface, sec_device)) + goto err; + } + + struct json_object *sec_static = neco_json_get_value(config, "static", json_type_object); + if (sec_static) { + if (!process_section_static(iface, sec_static)) + goto err; + } + + return dev; + +err: + interface_free(dev); + return NULL; } static bool interface_set_link_flags(unsigned index, unsigned change, unsigned flags) { @@ -281,45 +327,6 @@ static void interface_release(device_t *dev) { interface_set_link_state(index, false); } -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(dev); - return NULL; -} - static device_type_t device_type_interface = { .process_config = interface_process_config, .free = interface_free, diff --git a/src/device.h b/src/device.h index 33acfbb..e44c695 100644 --- a/src/device.h +++ b/src/device.h @@ -1,7 +1,6 @@ #pragma once -#include "config-ini.h" - +#include #include extern struct avl_tree device_types; @@ -15,7 +14,7 @@ typedef struct _device { struct _device_type { struct avl_node node; - device_t * (*process_config)(const char *name, const ini_file_t *config); + device_t * (*process_config)(const char *name, struct json_object *config); void (*free)(device_t *device); void (*init)(device_t *device); diff --git a/src/keywords.c b/src/keywords.c deleted file mode 100644 index 12393bd..0000000 --- a/src/keywords.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "keywords.h" - -#include - -#include -#include - -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); -} diff --git a/src/keywords.def b/src/keywords.def deleted file mode 100644 index 0dbd866..0000000 --- a/src/keywords.def +++ /dev/null @@ -1,9 +0,0 @@ -/* Config section and field names; keep sorted! */ - -KW(Address) -KW(DHCP) -KW(DHCPv6) -KW(Device) -KW(Generate) -KW(Properties) -KW(Static) diff --git a/src/keywords.h b/src/keywords.h deleted file mode 100644 index 05d809e..0000000 --- a/src/keywords.h +++ /dev/null @@ -1,12 +0,0 @@ -#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); diff --git a/src/meson.build b/src/meson.build index 9056cf3..b359ab4 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,19 +1,18 @@ src = [ 'neco.c', - 'config-ini.c', 'config-load.c', 'config-process.c', 'device.c', 'device-bridge.c', 'device-interface.c', - 'keywords.c', 'netlink.c', 'vector.c', ] dep = [ - libubox_dep, + libjson_c_dep, libmnl_dep, + libubox_dep, ] executable('neco', sources: src, dependencies: dep) diff --git a/src/util.h b/src/util.h index 31b13b6..2e60467 100644 --- a/src/util.h +++ b/src/util.h @@ -1,3 +1,29 @@ #pragma once +#include + + #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); +} From 7745aa34ad3fd114a0e3d0f89eef145c4736c5f3 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 31 May 2019 20:40:13 +0200 Subject: [PATCH 06/10] config: fix resolve of device type --- src/config-process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config-process.c b/src/config-process.c index a0671cb..c1634d5 100644 --- a/src/config-process.c +++ b/src/config-process.c @@ -22,7 +22,7 @@ static device_t * config_process_device(const char *name, struct json_object *ob const char *typename = NULL; struct json_object *device = neco_json_get_value(obj, "device", json_type_object); if (device) - typename = neco_json_get_string(obj, "type"); + typename = neco_json_get_string(device, "type"); const device_type_t *type = get_device_type(typename ?: "interface"); if (!type) From f43788c9473480e127118ec0e614b9faa7eea2e2 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 31 May 2019 21:56:08 +0200 Subject: [PATCH 07/10] device: split out common device settings --- src/device-common.c | 121 +++++++++++++++++++++++++++++ src/device-common.h | 31 ++++++++ src/device-interface.c | 170 +++-------------------------------------- src/meson.build | 1 + 4 files changed, 162 insertions(+), 161 deletions(-) create mode 100644 src/device-common.c create mode 100644 src/device-common.h diff --git a/src/device-common.c b/src/device-common.c new file mode 100644 index 0000000..4a10bf5 --- /dev/null +++ b/src/device-common.c @@ -0,0 +1,121 @@ +#include "device-common.h" +#include "util.h" + +#include +#include +#include +#include +#include + +#include + +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; +} + +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; + } + + return true; +} diff --git a/src/device-common.h b/src/device-common.h new file mode 100644 index 0000000..ff27961 --- /dev/null +++ b/src/device-common.h @@ -0,0 +1,31 @@ +#pragma once + +#include "device.h" +#include "vector.h" + +#include +#include + +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; +} device_common_t; + +bool device_common_process_config(device_common_t *device, struct json_object *config); diff --git a/src/device-interface.c b/src/device-interface.c index ef85089..aa37940 100644 --- a/src/device-interface.c +++ b/src/device-interface.c @@ -1,17 +1,13 @@ -#include "device.h" +#include "device-common.h" #include "netlink.h" #include "util.h" #include "vector.h" -#include #include #include #include -#include #include -#include -#include #include #include @@ -19,158 +15,19 @@ 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_common_t common; } 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 void interface_free(device_t *dev) { device_interface_t *iface = container_of(dev, device_interface_t, device); - VECTOR_FREE(iface->addrs); + VECTOR_FREE(iface->common.addrs); free(NODE_NAME(dev)); free(iface); } -static bool process_section_device(device_interface_t *iface, struct json_object *section) { - return true; -} - -static bool process_section_static(device_interface_t *iface, 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(iface->addrs, p)) { - fprintf(stderr, "interface: static: adding address failed\n"); - return false; - } - } - } - - - /* - 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 device_t * interface_process_config(const char *name, struct json_object *config) { device_interface_t *iface = calloc(1, sizeof(*iface)); if (!iface) @@ -185,17 +42,8 @@ static device_t * interface_process_config(const char *name, struct json_object return NULL; } - struct json_object *sec_device = neco_json_get_value(config, "device", json_type_object); - if (sec_device) { - if (!process_section_device(iface, sec_device)) - goto err; - } - - struct json_object *sec_static = neco_json_get_value(config, "static", json_type_object); - if (sec_static) { - if (!process_section_static(iface, sec_static)) - goto err; - } + if (!device_common_process_config(&iface->common, config)) + goto err; return dev; @@ -307,8 +155,8 @@ static void interface_init(device_t *dev) { interface_set_link_state(index, true); - for (size_t i = 0; i < VECTOR_LEN(iface->addrs); i++) - interface_set_ipaddr(index, &VECTOR_INDEX(iface->addrs, i), true); + for (size_t i = 0; i < VECTOR_LEN(iface->common.addrs); i++) + interface_set_ipaddr(index, &VECTOR_INDEX(iface->common.addrs, i), true); } static void interface_update(device_t *dev) { @@ -321,8 +169,8 @@ static void interface_release(device_t *dev) { if (!index) return; - for (size_t i = 0; i < VECTOR_LEN(iface->addrs); i++) - interface_set_ipaddr(index, &VECTOR_INDEX(iface->addrs, i), false); + for (size_t i = 0; i < VECTOR_LEN(iface->common.addrs); i++) + interface_set_ipaddr(index, &VECTOR_INDEX(iface->common.addrs, i), false); interface_set_link_state(index, false); } diff --git a/src/meson.build b/src/meson.build index b359ab4..3cc830c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -4,6 +4,7 @@ src = [ 'config-process.c', 'device.c', 'device-bridge.c', + 'device-common.c', 'device-interface.c', 'netlink.c', 'vector.c', From 8b5af11ebe1211861b422cc6737219c28594eecd Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 1 Jun 2019 12:30:14 +0200 Subject: [PATCH 08/10] device: move more common functions --- src/device-common.c | 112 +++++++++++++++++++++++++++++++++++++++ src/device-common.h | 3 ++ src/device-interface.c | 117 +++-------------------------------------- 3 files changed, 121 insertions(+), 111 deletions(-) diff --git a/src/device-common.c b/src/device-common.c index 4a10bf5..9e7605e 100644 --- a/src/device-common.c +++ b/src/device-common.c @@ -1,4 +1,5 @@ #include "device-common.h" +#include "netlink.h" #include "util.h" #include @@ -9,6 +10,10 @@ #include +#include +#include +#include + static unsigned long strtoul_safe(const char *str) { char *endptr; @@ -119,3 +124,110 @@ bool device_common_process_config(device_common_t *device, struct json_object *c 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; +} +void device_common_init(device_common_t *device, int ifindex) { + device_common_set_link_state(ifindex, true); + + 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) { + 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); +} diff --git a/src/device-common.h b/src/device-common.h index ff27961..398c5a1 100644 --- a/src/device-common.h +++ b/src/device-common.h @@ -29,3 +29,6 @@ typedef struct _device_common { } 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); diff --git a/src/device-interface.c b/src/device-interface.c index aa37940..1add2b2 100644 --- a/src/device-interface.c +++ b/src/device-interface.c @@ -1,5 +1,4 @@ #include "device-common.h" -#include "netlink.h" #include "util.h" #include "vector.h" @@ -9,10 +8,6 @@ #include -#include -#include -#include - static device_type_t device_type_interface; typedef struct _device_interface { @@ -52,111 +47,14 @@ err: return NULL; } -static bool interface_set_link_flags(unsigned index, 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 = index; - 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 interface_set_link_state(unsigned index, bool up) { - return interface_set_link_flags(index, IFF_UP, up ? IFF_UP : 0); -} - -static bool interface_set_ipaddr(unsigned index, 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 = index; - 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 void interface_init(device_t *dev) { device_interface_t *iface = container_of(dev, device_interface_t, device); - unsigned index = if_nametoindex(NODE_NAME(dev)); - if (!index) + unsigned ifindex = if_nametoindex(NODE_NAME(dev)); + if (!ifindex) return; - interface_set_link_state(index, true); - - for (size_t i = 0; i < VECTOR_LEN(iface->common.addrs); i++) - interface_set_ipaddr(index, &VECTOR_INDEX(iface->common.addrs, i), true); + device_common_init(&iface->common, ifindex); } static void interface_update(device_t *dev) { @@ -165,14 +63,11 @@ 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 index = if_nametoindex(NODE_NAME(dev)); - if (!index) + unsigned ifindex = if_nametoindex(NODE_NAME(dev)); + if (!ifindex) return; - for (size_t i = 0; i < VECTOR_LEN(iface->common.addrs); i++) - interface_set_ipaddr(index, &VECTOR_INDEX(iface->common.addrs, i), false); - - interface_set_link_state(index, false); + device_common_release(&iface->common, ifindex); } static device_type_t device_type_interface = { From 8d15854eb57c8a16523a986aa72e3a2c993a8c50 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 1 Jun 2019 14:28:03 +0200 Subject: [PATCH 09/10] device: bridge: implement initial bridge setup --- src/device-bridge.c | 122 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 4 deletions(-) diff --git a/src/device-bridge.c b/src/device-bridge.c index 4a8d24c..11b24b0 100644 --- a/src/device-bridge.c +++ b/src/device-bridge.c @@ -1,14 +1,128 @@ -#include "device.h" +#include "device-common.h" +#include "netlink.h" +#include "util.h" -#include +#include + +#include + +#include +#include +#include + +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) { - printf("Bridge: %s\n", name); + 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; } -device_type_t device_type_bridge = { +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)) From 29f3ef36af7bb387a51fe8ad968d9770d3b54a6d Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 1 Jun 2019 19:10:17 +0200 Subject: [PATCH 10/10] device: add bridge port configuration --- src/device-common.c | 74 ++++++++++++++++++++++++++++++++++++++++++--- src/device-common.h | 2 ++ 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/device-common.c b/src/device-common.c index 9e7605e..1dc4490 100644 --- a/src/device-common.c +++ b/src/device-common.c @@ -109,6 +109,14 @@ static bool process_section_static(device_common_t *device, struct json_object * 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) { @@ -122,6 +130,12 @@ bool device_common_process_config(device_common_t *device, struct json_object *c 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; } @@ -218,16 +232,68 @@ static bool device_common_set_ipaddr(unsigned ifindex, ipaddr_prefix_t *addr, bo 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); - for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++) - device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), 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) { - for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++) - device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), false); + 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); } diff --git a/src/device-common.h b/src/device-common.h index 398c5a1..dc0fe28 100644 --- a/src/device-common.h +++ b/src/device-common.h @@ -3,6 +3,7 @@ #include "device.h" #include "vector.h" +#include #include #include @@ -26,6 +27,7 @@ typedef struct _device_common { 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);