diff options
Diffstat (limited to 'src/libbcnm')
-rw-r--r-- | src/libbcnm/bcnm_if_getstate.c | 30 | ||||
-rw-r--r-- | src/libbcnm/bcnm_if_link_event.c | 77 | ||||
-rw-r--r-- | src/libbcnm/bcnm_if_link_init.c | 38 | ||||
-rw-r--r-- | src/libbcnm/deps-lib/bcnm | 3 |
4 files changed, 148 insertions, 0 deletions
diff --git a/src/libbcnm/bcnm_if_getstate.c b/src/libbcnm/bcnm_if_getstate.c new file mode 100644 index 0000000..af50268 --- /dev/null +++ b/src/libbcnm/bcnm_if_getstate.c @@ -0,0 +1,30 @@ +/* ISC license. */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <net/if.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <bcnm/if.h> + +int bcnm_if_getstate (char const *ifname) +{ + struct ifreq blah ; + int sfd ; + int e = errno ; + size_t len = strlen(ifname) ; + if (len >= IFNAMSIZ) return (errno = ENAMETOOLONG, -1) ; + memcpy(blah.ifr_name, ifname, len+1) ; + sfd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0) ; + if (sfd < 0) return -1 ; + if (ioctl(sfd, SIOCGIFFLAGS, &blah) < 0) + return errno == ENODEV ? (errno = e, 0) : -1 ; + close(sfd) ; + return 1 | !!(blah.ifr_flags & IFF_UP) << 1 | !!(blah.ifr_flags & IFF_RUNNING) << 2 ; +} diff --git a/src/libbcnm/bcnm_if_link_event.c b/src/libbcnm/bcnm_if_link_event.c new file mode 100644 index 0000000..fd7157a --- /dev/null +++ b/src/libbcnm/bcnm_if_link_event.c @@ -0,0 +1,77 @@ +/* ISC license. */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <string.h> +#include <errno.h> +#include <net/if.h> +#include <sys/socket.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +#include <bcnm/if.h> + +static inline ssize_t fd_recvmsg_nb (int fd, struct msghdr *hdr) +{ + int e = errno ; + ssize_t r ; + do r = recvmsg(fd, hdr, MSG_DONTWAIT) ; + while (r == -1 && errno == EINTR) ; + if (r == -1 && errno == EWOULDBLOCK) return (errno = e, 0) ; + if (!r) return (errno = EPIPE, -1) ; + return r ; +} + +static int answer (bcnm_if_link_state_t *tab, size_t n, char const *ifname, unsigned int state) +{ + size_t i = 0 ; + for (; i < n ; i++) if (!strcmp(ifname, tab[i].ifname)) break ; + if (i >= n) return 0 ; + tab[i].state = state ; + tab[i].changed = 1 ; + return 1 ; +} + +ssize_t bcnm_if_link_event (int fd, bcnm_if_link_state_t *tab, size_t n) +{ + ssize_t e = 0 ; + struct sockaddr_nl nl ; + struct nlmsghdr buf[8192 / sizeof(struct nlmsghdr)] ; + struct iovec v = { .iov_base = buf, .iov_len = sizeof(struct nlmsghdr) * (8192 / sizeof(struct nlmsghdr)) } ; + struct msghdr msg = + { + .msg_name = &nl, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &v, + .msg_iovlen = 1, + .msg_control = 0, + .msg_controllen = 0, + .msg_flags = 0 + } ; + ssize_t r = fd_recvmsg_nb(fd, &msg) ; + if (r <= 0) return r ; + if (msg.msg_flags & MSG_TRUNC) return (errno = ENOBUFS, -1) ; + if (nl.nl_pid) return (errno = EPROTO, -1) ; + for (struct nlmsghdr *hdr = buf ; NLMSG_OK(hdr, r) ; hdr = NLMSG_NEXT(hdr, r)) + { + struct ifinfomsg *ifi = NLMSG_DATA(hdr) ; + char ifname[IF_NAMESIZE] ; + switch (hdr->nlmsg_type) + { + case NLMSG_DONE : goto out ; + case NLMSG_ERROR : return (errno = EPROTO, -1) ; + case RTM_DELLINK : + if_indextoname(ifi->ifi_index, ifname) ; + e += answer(tab, n, ifname, 0) ; + break ; + case RTM_NEWLINK : + if_indextoname(ifi->ifi_index, ifname) ; + e += answer(tab, n, ifname, 1 | !!(ifi->ifi_flags & IFF_UP) << 1 | !!(ifi->ifi_flags & IFF_RUNNING) << 2) ; + break ; + } + } + out: + return e ; +} diff --git a/src/libbcnm/bcnm_if_link_init.c b/src/libbcnm/bcnm_if_link_init.c new file mode 100644 index 0000000..dad93fd --- /dev/null +++ b/src/libbcnm/bcnm_if_link_init.c @@ -0,0 +1,38 @@ +/* ISC license. */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <unistd.h> +#include <errno.h> +#include <sys/socket.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +#include <bcnm/if.h> + +int bcnm_if_link_init (unsigned int kbufsz) +{ + struct sockaddr_nl nl = + { + .nl_family = AF_NETLINK, + .nl_pad = 0, + .nl_groups = RTNLGRP_LINK, + .nl_pid = 0 + } ; + int fd = socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE) ; + if (fd < 0) return -1 ; + if (bind(fd, (struct sockaddr *)&nl, sizeof(struct sockaddr_nl)) < 0) goto err ; + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &kbufsz, sizeof(unsigned int)) < 0) + { + if (errno != EPERM + || setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &kbufsz, sizeof(unsigned int)) < 0) goto err ; + } + return fd ; + + err: + close(fd) ; + return -1 ; +} + diff --git a/src/libbcnm/deps-lib/bcnm b/src/libbcnm/deps-lib/bcnm new file mode 100644 index 0000000..3e3bada --- /dev/null +++ b/src/libbcnm/deps-lib/bcnm @@ -0,0 +1,3 @@ +bcnm_if_getstate.o +bcnm_if_link_init.o +bcnm_if_link_event.o |