diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/include/bcnm/bcnm.h | 16 | ||||
-rw-r--r-- | src/include/bcnm/if.h | 22 | ||||
-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 | ||||
-rw-r--r-- | src/utils/bcnm-waitif.c | 91 |
7 files changed, 277 insertions, 0 deletions
diff --git a/src/include/bcnm/bcnm.h b/src/include/bcnm/bcnm.h new file mode 100644 index 0000000..f3d5bf2 --- /dev/null +++ b/src/include/bcnm/bcnm.h @@ -0,0 +1,16 @@ +/* ISC license. */ + +#ifndef BCNM_BCNM_H +#define BCNM_BCNM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <bcnm/if.h> + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/bcnm/if.h b/src/include/bcnm/if.h new file mode 100644 index 0000000..c4217ab --- /dev/null +++ b/src/include/bcnm/if.h @@ -0,0 +1,22 @@ +/* ISC license. */ + +#ifndef BCNM_IF_H +#define BCNM_IF_H + +#include <sys/types.h> +#include <net/if.h> + +typedef struct bcnm_if_link_state_s bcnm_if_link_state_t, *bcnm_if_link_state_t_ref ; +struct bcnm_if_link_state_s +{ + char ifname[IF_NAMESIZE] ; + unsigned int state : 15 ; + unsigned int changed : 1 ; +} ; +#define BCNM_IF_LINK_STATE_ZERO { "", 0, 0 } + +extern int bcnm_if_getstate (char const *) ; +extern int bcnm_if_link_init (unsigned int) ; +extern ssize_t bcnm_if_link_event (int, bcnm_if_link_state_t *, size_t) ; + +#endif 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 diff --git a/src/utils/bcnm-waitif.c b/src/utils/bcnm-waitif.c new file mode 100644 index 0000000..c5fd556 --- /dev/null +++ b/src/utils/bcnm-waitif.c @@ -0,0 +1,91 @@ +/* ISC license. */ + +#include <string.h> +#include <net/if.h> + +#include <skalibs/strerr2.h> +#include <skalibs/sgetopt.h> +#include <skalibs/types.h> +#include <skalibs/tai.h> +#include <skalibs/iopause.h> + +#include <bcnm/if.h> + +#define USAGE "bcnm-waitif [ -u | -d ] [ -t timeout ] [ -k kbufsize ] mask interface..." +#define dieusage() strerr_dieusage(100, USAGE) + +static inline int check (bcnm_if_link_state_t *tab, size_t n, unsigned int mask, int not) +{ + size_t i = 0 ; + for (; i < n ; i++) + if ((tab[i].state & mask) != (not ? 0 : mask)) return 0 ; + return 1 ; +} + +int main (int argc, char const *const *argv) +{ + iopause_fd x = { .events = IOPAUSE_READ } ; + tain_t deadline, tto ; + int not = 0 ; + unsigned int mask = 1 ; + unsigned int kbufsize = 131072 ; + PROG = "bcnm-waitif" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + unsigned int t = 0 ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "udt:k:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'u' : not = 0 ; break ; + case 'd' : not = 1 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + case 'k' : if (!uint0_scan(l.arg, &kbufsize)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (t) tain_from_millisecs(&tto, t) ; + else tto = tain_infinite_relative ; + } + if (argc < 2) dieusage() ; + if (!uint0_scan(*argv++, &mask)) + strerr_dief2x(100, "invalid mask: ", argv[-1]) ; + + x.fd = bcnm_if_link_init(kbufsize) ; + if (x.fd < 0) strerr_diefu1sys(111, "bcnm_if_link_init") ; + + tain_now_set_stopwatch_g() ; + tain_add_g(&deadline, &tto) ; + + { + size_t n = --argc ; + bcnm_if_link_state_t tab[n] ; + for (size_t i = 0 ; i < n ; i++) + { + int r ; + size_t len = strlen(argv[i]) ; + if (len >= IF_NAMESIZE - 1) + strerr_dief2x(100, "interface name too long: ", argv[i]) ; + memcpy(tab[i].ifname, argv[i], len+1) ; + tab[i].changed = 0 ; + r = bcnm_if_getstate(argv[i]) ; + if (r < 0) strerr_diefu2sys(111, "get state for interface ", argv[i]) ; + tab[i].state = r ; + } + + for (;;) + { + int r ; + if (check(tab, n, mask, not)) return 0 ; + r = iopause_g(&x, 1, &deadline) ; + if (r < 0) strerr_diefu1sys(111, "iopause") ; + if (!r) strerr_dief1x(99, "timed out") ; + if (bcnm_if_link_event(x.fd, tab, n) < 0) + strerr_diefu1sys(111, "read rtnetlink event") ; + } + } + return 0 ; +} |