From 44cb17317c40fa9d39b3402f0826006f20387be5 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 6 Jan 2019 07:20:08 +0100 Subject: 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); -- cgit v1.2.3