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. diff --git a/meson.build b/meson.build index aaa8cf6..0cbd657 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,9 @@ -project('pnc', 'c') +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') + +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 7cfa1ff..0000000 --- a/src/config-ini.c +++ /dev/null @@ -1,147 +0,0 @@ -#include "config-ini.h" -#include "types.h" - -#include -#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(const char *filename) { - FILE *f = fopen(filename, "r"); - if (!f) - return NULL; - - ini_file_t *file = calloc(1, sizeof(*file)); - if (!file) - goto error; - - INIT_LIST_HEAD(&file->sections); - - ini_section_t *section = NULL; - int err = 0; - - char *line = NULL; - size_t n = 0; - - while (getline(&line, &n, f) >= 0) { - char *input = line; - - while (isspace(input[0])) - input++; - - if (input[0] == '#' || input[0] == '\0') - continue; - - size_t len = strlen(input); - - while (isspace(input[len-1])) - len--; - - if (input[0] == '[') { - if (input[len-1] != ']') { - err = EINVAL; - goto error; - } - - input[len-1] = '\0'; - - section = add_section(file, input+1); - if (!section) - goto error; - } else { - if (!section) { - err = EINVAL; - goto error; - } - - input[len] = '\0'; - - char *delim = strchr(input, '='); - if (!delim) { - err = EINVAL; - goto error; - } - - *delim = '\0'; - - if (!add_field(section, input, delim+1)) - goto error; - } - } - - if (ferror(f)) - err = EIO; - -error: - free(line); - fclose(f); - - if (err) { - free_ini_file(file); - errno = err; - return NULL; - } - - return file; -} diff --git a/src/config-ini.h b/src/config-ini.h deleted file mode 100644 index 802195d..0000000 --- a/src/config-ini.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -typedef struct { - struct list_head node; - char *key; - char *value; -} ini_field_t; - -typedef struct { - struct list_head node; - struct list_head fields; - char *name; -} ini_section_t; - -typedef struct { - struct list_head sections; -} ini_file_t; - -ini_file_t * read_ini_file(const char *filename); -void free_ini_file(ini_file_t *file); diff --git a/src/config-load.c b/src/config-load.c index 9335813..19b9d3a 100644 --- a/src/config-load.c +++ b/src/config-load.c @@ -1,55 +1,41 @@ #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 +#include #include #include +#include -static const char * simple_basename(const char *path) { - const char *slash = strrchr(path, '/'); - return slash ? (slash+1) : path; -} - -static const char * extname(const char *filename) { - const char *dot = strrchr(filename, '.'); - return dot ? (dot+1) : NULL; -} - -static interface_config_t * read_interface_config(const char *path) { - ini_file_t *file = read_ini_file(path); - ini_section_t *section; - ini_field_t *field; - - list_for_each_entry(section, &file->sections, node) { - printf("%s\n", section->name); - - list_for_each_entry(field, §ion->fields, node) - printf("%s.%s=%s\n", section->name, field->key, field->value); - } - - free_ini_file(file); - - return NULL; -} - -bool read_config_file(const char *path) { - const char *filename = simple_basename(path); - const char *ext = extname(filename); - if (!ext) { - errno = EINVAL; +bool read_config(const char *path) { + struct json_object *config = json_object_from_file(path); + if (!config) return false; - } - char *name = strndup(filename, (ext - filename) - 1); + struct avl_tree *devices = config_process(config); + json_object_put(config); - if (strcmp(ext, "interface") == 0) { - interface_config_t *iface = read_interface_config(path); - free(iface); - } + if (!devices) + return false; - free(name); + device_t *dev, *tmp; + avl_for_each_element(devices, dev, node) + dev->type->init(dev); + + //avl_for_each_element(devices, dev, node) + // dev->type->release(dev); + + avl_remove_all_elements(devices, dev, node, tmp) + dev->type->free(dev); + + free(devices); return true; } 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/config-process.c b/src/config-process.c new file mode 100644 index 0000000..c1634d5 --- /dev/null +++ b/src/config-process.c @@ -0,0 +1,58 @@ +#include "config-process.h" +#include "device.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; + + +static device_t * config_process_device(const char *name, struct json_object *obj) { + const char *typename = NULL; + struct json_object *device = neco_json_get_value(obj, "device", json_type_object); + if (device) + typename = neco_json_get_string(device, "type"); + + const device_type_t *type = get_device_type(typename ?: "interface"); + if (!type) + return NULL; + + return type->process_config(name, obj); +} + +struct avl_tree * config_process(struct json_object *config) { + if (!json_object_is_type(config, json_type_object)) + return NULL; + + struct json_object *devices = neco_json_get_value(config, "devices", json_type_object); + if (!devices) + return NULL; + + process_ctx_t ctx; + ctx.ret = calloc(1, sizeof(*ctx.ret)); + if (!ctx.ret) + return NULL; + + avl_init(ctx.ret, avl_strcmp, false, NULL); + + json_object_object_foreach(devices, name, obj) { + device_t *device = config_process_device(name, obj); + if (!device) + continue; + + avl_insert(ctx.ret, &device->node); + } + + return ctx.ret; +} diff --git a/src/config-process.h b/src/config-process.h new file mode 100644 index 0000000..3903d78 --- /dev/null +++ b/src/config-process.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +struct avl_tree * config_process(struct json_object *config); diff --git a/src/device-bridge.c b/src/device-bridge.c new file mode 100644 index 0000000..11b24b0 --- /dev/null +++ b/src/device-bridge.c @@ -0,0 +1,131 @@ +#include "device-common.h" +#include "netlink.h" +#include "util.h" + +#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) { + device_bridge_t *iface = calloc(1, sizeof(*iface)); + if (!iface) + return NULL; + + device_t *dev = &iface->device; + dev->type = &device_type_bridge; + + NODE_NAME(dev) = strdup(name); + if (!NODE_NAME(dev)) { + free(iface); + return NULL; + } + + if (!device_common_process_config(&iface->common, config)) + goto err; + + return dev; + +err: + bridge_free(dev); + return NULL; +} + +static bool bridge_nl_create(device_bridge_t *bridge) { + char buf[MNL_SOCKET_BUFFER_SIZE]; + + struct mnl_socket *nl = nl_socket(); + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_NEWLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE; + int seq = nlh->nlmsg_seq = nl_seq(); + + struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi)); + ifi->ifi_family = AF_UNSPEC; + + mnl_attr_put_str(nlh, IFLA_IFNAME, NODE_NAME(&bridge->device)); + + struct nlattr *linkinfo = mnl_attr_nest_start(nlh, IFLA_LINKINFO); + mnl_attr_put_str(nlh, IFLA_INFO_KIND, "bridge"); + mnl_attr_nest_end(nlh, linkinfo); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + return false; + } + + int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + return false; + } + + ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + return false; + } + + return true; +} + +static void bridge_init(device_t *dev) { + device_bridge_t *bridge = container_of(dev, device_bridge_t, device); + + if (!bridge_nl_create(bridge)) + return; + + unsigned ifindex = if_nametoindex(NODE_NAME(dev)); + if (!ifindex) + return; + + device_common_init(&bridge->common, ifindex); +} + +static void bridge_update(device_t *dev) { +} + +static void bridge_release(device_t *dev) { + /* + device_bridge_t *bridge = container_of(dev, device_bridge_t, device); + + unsigned ifindex = if_nametoindex(NODE_NAME(dev)); + if (!ifindex) + return; + + device_common_release(&bridge->common, ifindex); + */ +} + +static device_type_t device_type_bridge = { + .process_config = bridge_process_config, + .free = bridge_free, + + .init = bridge_init, + .update = bridge_update, + .release = bridge_release, +}; + +__attribute__((constructor)) +static void bridge_constructor(void) { + register_device_type("bridge", &device_type_bridge); +} diff --git a/src/device-common.c b/src/device-common.c new file mode 100644 index 0000000..1dc4490 --- /dev/null +++ b/src/device-common.c @@ -0,0 +1,299 @@ +#include "device-common.h" +#include "netlink.h" +#include "util.h" + +#include +#include +#include +#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; +} + +static bool process_section_bridge(device_common_t *device, struct json_object *section) { + const char *master = neco_json_get_string(section, "master"); + if (master) + strncpy(device->master, master, sizeof(device->master)-1); + + return true; +} + +bool device_common_process_config(device_common_t *device, struct json_object *config) { + struct json_object *sec_device = neco_json_get_value(config, "device", json_type_object); + if (sec_device) { + if (!process_section_device(device, sec_device)) + return false; + } + + struct json_object *sec_static = neco_json_get_value(config, "static", json_type_object); + if (sec_static) { + if (!process_section_static(device, sec_static)) + return false; + } + + struct json_object *sec_bridge = neco_json_get_value(config, "bridge", json_type_object); + if (sec_bridge) { + if (!process_section_bridge(device, sec_bridge)) + return false; + } + + return true; +} + +static bool device_common_set_link_flags(unsigned ifindex, unsigned change, unsigned flags) { + char buf[MNL_SOCKET_BUFFER_SIZE]; + + struct mnl_socket *nl = nl_socket(); + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_SETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + int seq = nlh->nlmsg_seq = nl_seq(); + + struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi)); + ifi->ifi_index = ifindex; + ifi->ifi_family = AF_UNSPEC; + + ifi->ifi_change = change; + ifi->ifi_flags = flags; + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + return false; + } + + int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + return false; + } + + ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + return false; + } + + return true; +} + +static bool device_common_set_link_state(unsigned ifindex, bool up) { + return device_common_set_link_flags(ifindex, IFF_UP, up ? IFF_UP : 0); +} + +static bool device_common_set_ipaddr(unsigned ifindex, ipaddr_prefix_t *addr, bool add) { + char buf[MNL_SOCKET_BUFFER_SIZE]; + + struct mnl_socket *nl = nl_socket(); + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = add ? RTM_NEWADDR : RTM_DELADDR; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + if (add) + nlh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; + + int seq = nlh->nlmsg_seq = nl_seq(); + + struct ifaddrmsg *ifa = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifa)); + ifa->ifa_index = ifindex; + ifa->ifa_family = addr->addr.af; + + ifa->ifa_prefixlen = addr->plen; + + switch (addr->addr.af) { + case AF_INET: + mnl_attr_put(nlh, IFA_LOCAL, 4, &addr->addr.addr4); + break; + + case AF_INET6: + mnl_attr_put(nlh, IFA_LOCAL, 16, &addr->addr.addr6); + break; + + default: + errno = EINVAL; + return false; + } + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + return false; + } + + int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + return false; + } + + ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + return false; + } + + return true; +} + +static bool device_common_set_master(unsigned ifindex, unsigned master) { + char buf[MNL_SOCKET_BUFFER_SIZE]; + + struct mnl_socket *nl = nl_socket(); + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_SETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + int seq = nlh->nlmsg_seq = nl_seq(); + + struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi)); + ifi->ifi_index = ifindex; + ifi->ifi_family = AF_UNSPEC; + + mnl_attr_put_u32(nlh, IFLA_MASTER, master); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + return false; + } + + int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + return false; + } + + ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + return false; + } + + return true; +} + +void device_common_init(device_common_t *device, int ifindex) { + if (*device->master) { + unsigned master = if_nametoindex(device->master); + if (!master) + return; + + device_common_set_master(ifindex, master); + } + + device_common_set_link_state(ifindex, true); + + if (!*device->master) { + for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++) + device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), true); + } +} + +void device_common_release(device_common_t *device, int ifindex) { + if (!*device->master) { + for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++) + device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), false); + } + + device_common_set_link_state(ifindex, false); + + if (*device->master) + device_common_set_master(ifindex, 0); +} diff --git a/src/device-common.h b/src/device-common.h new file mode 100644 index 0000000..dc0fe28 --- /dev/null +++ b/src/device-common.h @@ -0,0 +1,36 @@ +#pragma once + +#include "device.h" +#include "vector.h" + +#include +#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; + char master[IF_NAMESIZE]; +} device_common_t; + +bool device_common_process_config(device_common_t *device, struct json_object *config); + +void device_common_init(device_common_t *device, int ifindex); +void device_common_release(device_common_t *device, int ifindex); diff --git a/src/device-interface.c b/src/device-interface.c new file mode 100644 index 0000000..1add2b2 --- /dev/null +++ b/src/device-interface.c @@ -0,0 +1,85 @@ +#include "device-common.h" +#include "util.h" +#include "vector.h" + +#include +#include +#include + +#include + +static device_type_t device_type_interface; + +typedef struct _device_interface { + device_t device; + device_common_t common; +} device_interface_t; + +static void interface_free(device_t *dev) { + device_interface_t *iface = container_of(dev, device_interface_t, device); + + VECTOR_FREE(iface->common.addrs); + free(NODE_NAME(dev)); + free(iface); +} + +static device_t * interface_process_config(const char *name, struct json_object *config) { + device_interface_t *iface = calloc(1, sizeof(*iface)); + if (!iface) + return NULL; + + device_t *dev = &iface->device; + dev->type = &device_type_interface; + + NODE_NAME(dev) = strdup(name); + if (!NODE_NAME(dev)) { + free(iface); + return NULL; + } + + if (!device_common_process_config(&iface->common, config)) + goto err; + + return dev; + +err: + interface_free(dev); + return NULL; +} + +static void interface_init(device_t *dev) { + device_interface_t *iface = container_of(dev, device_interface_t, device); + + unsigned ifindex = if_nametoindex(NODE_NAME(dev)); + if (!ifindex) + return; + + device_common_init(&iface->common, ifindex); +} + +static void interface_update(device_t *dev) { +} + +static void interface_release(device_t *dev) { + device_interface_t *iface = container_of(dev, device_interface_t, device); + + unsigned ifindex = if_nametoindex(NODE_NAME(dev)); + if (!ifindex) + return; + + device_common_release(&iface->common, ifindex); +} + +static device_type_t device_type_interface = { + .process_config = interface_process_config, + .free = interface_free, + + .init = interface_init, + .update = interface_update, + .release = interface_release, +}; + +__attribute__((constructor)) +static void interface_constructor(void) { + register_device_type("interface", &device_type_interface); +} 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..e44c695 --- /dev/null +++ b/src/device.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#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, struct json_object *config); + void (*free)(device_t *device); + + void (*init)(device_t *device); + void (*update)(device_t *device); + void (*release)(device_t *device); +}; + +void register_device_type(const char *name, device_type_t *device_type); +const device_type_t * get_device_type(const char *name); diff --git a/src/meson.build b/src/meson.build index c24ca21..3cc830c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,7 +1,19 @@ src = [ - 'pnc.c', - 'config-ini.c', + 'neco.c', 'config-load.c', + 'config-process.c', + 'device.c', + 'device-bridge.c', + 'device-common.c', + 'device-interface.c', + 'netlink.c', + 'vector.c', ] -executable('pnc', sources: src, dependencies: [ubox_dep]) +dep = [ + libjson_c_dep, + libmnl_dep, + libubox_dep, +] + +executable('neco', sources: src, dependencies: dep) diff --git a/src/neco.c b/src/neco.c new file mode 100644 index 0000000..4e4d297 --- /dev/null +++ b/src/neco.c @@ -0,0 +1,13 @@ +#include "config-load.h" +#include "netlink.h" + +int main(int argc, char *argv[]) { + if (!nl_init()) + return 1; + + read_config(argv[1]); + + nl_deinit(); + + return 0; +} 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); diff --git a/src/pnc.c b/src/pnc.c deleted file mode 100644 index 651d140..0000000 --- a/src/pnc.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "config-load.h" - -int main(int argc, char *argv[]) { - read_config_file(argv[1]); - return 0; -} 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..2e60467 --- /dev/null +++ b/src/util.h @@ -0,0 +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); +} 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); \ +})