summaryrefslogtreecommitdiffstats
path: root/src/device-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/device-common.c')
-rw-r--r--src/device-common.c112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/device-common.c b/src/device-common.c
index 4a10bf5..9e7605e 100644
--- a/src/device-common.c
+++ b/src/device-common.c
@@ -1,4 +1,5 @@
#include "device-common.h"
+#include "netlink.h"
#include "util.h"
#include <assert.h>
@@ -9,6 +10,10 @@
#include <arpa/inet.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
static unsigned long strtoul_safe(const char *str) {
char *endptr;
@@ -119,3 +124,110 @@ bool device_common_process_config(device_common_t *device, struct json_object *c
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;
+}
+void device_common_init(device_common_t *device, int ifindex) {
+ device_common_set_link_state(ifindex, true);
+
+ 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) {
+ 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);
+}