summaryrefslogtreecommitdiffstats
path: root/src/device-interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/device-interface.c')
-rw-r--r--src/device-interface.c202
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, &section->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, &section->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);
+}