diff options
Diffstat (limited to 'src/device-interface.c')
-rw-r--r-- | src/device-interface.c | 202 |
1 files changed, 202 insertions, 0 deletions
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 <arpa/inet.h> +#include <netinet/ether.h> +#include <netinet/in.h> + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +static device_type_t device_type_interface; + +typedef struct _ipaddr { + int af; + union { + struct in_addr addr4; + struct in6_addr addr6; + }; +} ipaddr_t; + +typedef struct _ipprefix { + ipaddr_t addr; + uint8_t plen; +} ipaddr_prefix_t; + +typedef VECTOR(ipaddr_prefix_t) ipaddr_prefix_vector_t; + +typedef struct _device_interface { + device_t device; + + struct ether_addr macaddr; + uint16_t mtu; + + ipaddr_prefix_vector_t addrs; +} device_interface_t; + +static unsigned long strtoul_safe(const char *str) { + char *endptr; + + errno = 0; + unsigned long val = strtoul(str, &endptr, 10); + if (endptr == str || *endptr) + errno = EINVAL; + + return val; +} + +static bool parse_address(ipaddr_t *addr, const char *str) { + if (inet_pton(AF_INET, str, &addr->addr4)) { + addr->af = AF_INET; + return true; + } + + if (inet_pton(AF_INET6, str, &addr->addr6)) { + addr->af = AF_INET6; + return true; + } + + return false; +} + +static bool parse_prefix(ipaddr_prefix_t *prefix, const char *str, bool allow_host) { + const char *slash = strrchr(str, '/'); + if (!slash) + return false; + + size_t len = slash - str; + char buf[len+1]; + memcpy(buf, str, len); + buf[len] = 0; + + if (!parse_address(&prefix->addr, buf)) + return false; + + long plen = strtoul_safe(slash + 1); + if (errno) + return false; + + switch (prefix->addr.af) { + case AF_INET: + if (plen > 32) + return false; + break; + + case AF_INET6: + if (plen > 128) + return false; + break; + + default: + assert(false); + __builtin_unreachable(); + } + + prefix->plen = plen; + + // TODO: Implement allow_host + + return true; +} + +static bool process_section_device(device_interface_t *iface, const ini_section_t *section) { + ini_field_t *field; + + list_for_each_entry(field, §ion->fields, node) { + switch (lookup_keyword(field->key)) { + default: + fprintf(stderr, "interface: [Device]: unknown field %s\n", field->key); + } + } + + return true; +} + +static bool process_section_static(device_interface_t *iface, const ini_section_t *section) { + ini_field_t *field; + + list_for_each_entry(field, §ion->fields, node) { + switch (lookup_keyword(field->key)) { + case KW_Address: { + ipaddr_prefix_t p; + if (!parse_prefix(&p, field->value, true)) { + fprintf(stderr, "interface: [Static]: unable to parse Address %s\n", field->value); + break; + } + + if (!VECTOR_ADD(iface->addrs, p)) { + fprintf(stderr, "interface: [Static]: adding address failed\n"); + return false; + } + + break; + } + + default: + fprintf(stderr, "interface: [Static]: unknown field %s\n", field->key); + } + } + + return true; +} + +static void interface_free_device(device_t *dev) { + device_interface_t *iface = container_of(dev, device_interface_t, device); + + VECTOR_FREE(iface->addrs); + free(NODE_NAME(dev)); + free(iface); +} + +static device_t * interface_process_config(const char *name, const ini_file_t *config) { + device_interface_t *iface = calloc(1, sizeof(*iface)); + if (!iface) + return NULL; + + device_t *dev = &iface->device; + dev->type = &device_type_interface; + + NODE_NAME(dev) = strdup(name); + if (!NODE_NAME(dev)) { + free(iface); + return NULL; + } + + const ini_section_t *section; + list_for_each_entry(section, &config->sections, node) { + switch (lookup_keyword(section->name)) { + case KW_Device: + if (!process_section_device(iface, section)) + goto err; + break; + + case KW_Static: + if (!process_section_static(iface, section)) + goto err; + break; + + default: + fprintf(stderr, "interface: unknown section %s\n", section->name); + } + } + + return dev; + +err: + interface_free_device(dev); + return NULL; +} + +static device_type_t device_type_interface = { + .process_config = interface_process_config, + .free_device = interface_free_device, +}; + +__attribute__((constructor)) +static void interface_constructor(void) { + register_device_type("interface", &device_type_interface); +} |