diff options
Diffstat (limited to 'src/device-interface.c')
-rw-r--r-- | src/device-interface.c | 147 |
1 files changed, 140 insertions, 7 deletions
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 <arpa/inet.h> -#include <netinet/ether.h> -#include <netinet/in.h> - #include <assert.h> #include <errno.h> #include <stdlib.h> #include <string.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <netinet/ether.h> +#include <netinet/in.h> + +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + 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)) |