aboutgitcodebugslistschat
diff options
context:
space:
mode:
-rw-r--r--passt.c34
-rw-r--r--passt.h6
-rw-r--r--pasta.c101
-rw-r--r--pasta.h5
4 files changed, 107 insertions, 39 deletions
diff --git a/passt.c b/passt.c
index aaa8e58..a90568f 100644
--- a/passt.c
+++ b/passt.c
@@ -61,17 +61,18 @@
char pkt_buf[PKT_BUF_BYTES] __attribute__ ((aligned(PAGE_SIZE)));
char *epoll_type_str[] = {
- [EPOLL_TYPE_TCP] = "connected TCP socket",
- [EPOLL_TYPE_TCP_SPLICE] = "connected spliced TCP socket",
- [EPOLL_TYPE_TCP_LISTEN] = "listening TCP socket",
- [EPOLL_TYPE_TCP_TIMER] = "TCP timer",
- [EPOLL_TYPE_UDP] = "UDP socket",
- [EPOLL_TYPE_ICMP] = "ICMP socket",
- [EPOLL_TYPE_ICMPV6] = "ICMPv6 socket",
- [EPOLL_TYPE_NSQUIT] = "namespace inotify",
- [EPOLL_TYPE_TAP_PASTA] = "/dev/net/tun device",
- [EPOLL_TYPE_TAP_PASST] = "connected qemu socket",
- [EPOLL_TYPE_TAP_LISTEN] = "listening qemu socket",
+ [EPOLL_TYPE_TCP] = "connected TCP socket",
+ [EPOLL_TYPE_TCP_SPLICE] = "connected spliced TCP socket",
+ [EPOLL_TYPE_TCP_LISTEN] = "listening TCP socket",
+ [EPOLL_TYPE_TCP_TIMER] = "TCP timer",
+ [EPOLL_TYPE_UDP] = "UDP socket",
+ [EPOLL_TYPE_ICMP] = "ICMP socket",
+ [EPOLL_TYPE_ICMPV6] = "ICMPv6 socket",
+ [EPOLL_TYPE_NSQUIT_INOTIFY] = "namespace inotify watch",
+ [EPOLL_TYPE_NSQUIT_TIMER] = "namespace timer watch",
+ [EPOLL_TYPE_TAP_PASTA] = "/dev/net/tun device",
+ [EPOLL_TYPE_TAP_PASST] = "connected qemu socket",
+ [EPOLL_TYPE_TAP_LISTEN] = "listening qemu socket",
};
static_assert(ARRAY_SIZE(epoll_type_str) == EPOLL_NUM_TYPES,
"epoll_type_str[] doesn't match enum epoll_type");
@@ -201,7 +202,7 @@ void exit_handler(int signal)
*/
int main(int argc, char **argv)
{
- int nfds, i, devnull_fd = -1, pidfile_fd = -1, quit_fd;
+ int nfds, i, devnull_fd = -1, pidfile_fd = -1;
struct epoll_event events[EPOLL_EVENTS];
char *log_name, argv0[PATH_MAX], *name;
struct ctx c = { 0 };
@@ -274,7 +275,7 @@ int main(int argc, char **argv)
if (c.force_stderr || isatty(fileno(stdout)))
__openlog(log_name, LOG_PERROR, LOG_DAEMON);
- quit_fd = pasta_netns_quit_init(&c);
+ pasta_netns_quit_init(&c);
tap_sock_init(&c);
@@ -370,8 +371,11 @@ loop:
case EPOLL_TYPE_TAP_LISTEN:
tap_listen_handler(&c, eventmask);
break;
- case EPOLL_TYPE_NSQUIT:
- pasta_netns_quit_handler(&c, quit_fd);
+ case EPOLL_TYPE_NSQUIT_INOTIFY:
+ pasta_netns_quit_inotify_handler(&c, ref.fd);
+ break;
+ case EPOLL_TYPE_NSQUIT_TIMER:
+ pasta_netns_quit_timer_handler(&c, ref);
break;
case EPOLL_TYPE_TCP:
tcp_sock_handler(&c, ref, eventmask);
diff --git a/passt.h b/passt.h
index a9e8f15..fb729b6 100644
--- a/passt.h
+++ b/passt.h
@@ -64,7 +64,9 @@ enum epoll_type {
/* ICMPv6 sockets */
EPOLL_TYPE_ICMPV6,
/* inotify fd watching for end of netns (pasta) */
- EPOLL_TYPE_NSQUIT,
+ EPOLL_TYPE_NSQUIT_INOTIFY,
+ /* timer fd watching for end of netns, fallback for inotify (pasta) */
+ EPOLL_TYPE_NSQUIT_TIMER,
/* tuntap character device */
EPOLL_TYPE_TAP_PASTA,
/* socket connected to qemu */
@@ -84,6 +86,7 @@ enum epoll_type {
* @udp: UDP-specific reference part
* @icmp: ICMP-specific reference part
* @data: Data handled by protocol handlers
+ * @nsdir_fd: netns dirfd for fallback timer checking if namespace is gone
* @u64: Opaque reference for epoll_ctl() and epoll_wait()
*/
union epoll_ref {
@@ -99,6 +102,7 @@ union epoll_ref {
union udp_epoll_ref udp;
union icmp_epoll_ref icmp;
uint32_t data;
+ int nsdir_fd;
};
};
uint64_t u64;
diff --git a/pasta.c b/pasta.c
index 94807a3..01d1511 100644
--- a/pasta.c
+++ b/pasta.c
@@ -30,6 +30,7 @@
#include <sys/epoll.h>
#include <sys/inotify.h>
#include <sys/mount.h>
+#include <sys/timerfd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -357,46 +358,78 @@ void pasta_ns_conf(struct ctx *c)
}
/**
+ * pasta_netns_quit_timer() - Set up fallback timer to monitor namespace
+ *
+ * Return: timerfd file descriptor, negative error code on failure
+ */
+static int pasta_netns_quit_timer(void)
+{
+ int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+ struct itimerspec it = { { 1, 0 }, { 1, 0 } }; /* one-second interval */
+
+ if (fd == -1) {
+ err("timerfd_create(): %s", strerror(errno));
+ return -errno;
+ }
+
+ if (timerfd_settime(fd, 0, &it, NULL) < 0) {
+ err("timerfd_settime(): %s", strerror(errno));
+ close(fd);
+ return -errno;
+ }
+
+ return fd;
+}
+
+/**
* pasta_netns_quit_init() - Watch network namespace to quit once it's gone
* @c: Execution context
- *
- * Return: inotify file descriptor, -1 on failure or if not needed/applicable
*/
-int pasta_netns_quit_init(const struct ctx *c)
+void pasta_netns_quit_init(const struct ctx *c)
{
+ union epoll_ref ref = { .type = EPOLL_TYPE_NSQUIT_INOTIFY };
+ struct epoll_event ev = { .events = EPOLLIN };
int flags = O_NONBLOCK | O_CLOEXEC;
- union epoll_ref ref = { .type = EPOLL_TYPE_NSQUIT };
- struct epoll_event ev = {
- .events = EPOLLIN
- };
- int inotify_fd;
+ int fd;
if (c->mode != MODE_PASTA || c->no_netns_quit || !*c->netns_base)
- return -1;
+ return;
- if ((inotify_fd = inotify_init1(flags)) < 0) {
- perror("inotify_init(): won't quit once netns is gone");
- return -1;
+ if ((fd = inotify_init1(flags)) < 0)
+ warn("inotify_init1(): %s, use a timer", strerror(errno));
+
+ if (fd >= 0 && inotify_add_watch(fd, c->netns_dir, IN_DELETE) < 0) {
+ warn("inotify_add_watch(): %s, use a timer",
+ strerror(errno));
+ close(fd);
+ fd = -1;
}
- if (inotify_add_watch(inotify_fd, c->netns_dir, IN_DELETE) < 0) {
- perror("inotify_add_watch(): won't quit once netns is gone");
- return -1;
+ if (fd < 0) {
+ if ((fd = pasta_netns_quit_timer()) < 0)
+ die("Failed to set up fallback netns timer, exiting");
+
+ ref.nsdir_fd = open(c->netns_dir, O_CLOEXEC | O_RDONLY);
+ if (ref.nsdir_fd < 0)
+ die("netns dir open: %s, exiting", strerror(errno));
+
+ ref.type = EPOLL_TYPE_NSQUIT_TIMER;
}
- ref.fd = inotify_fd;
- ev.data.u64 = ref.u64;
- epoll_ctl(c->epollfd, EPOLL_CTL_ADD, inotify_fd, &ev);
+ if (fd > FD_REF_MAX)
+ die("netns monitor file number %i too big, exiting", fd);
- return inotify_fd;
+ ref.fd = fd;
+ ev.data.u64 = ref.u64;
+ epoll_ctl(c->epollfd, EPOLL_CTL_ADD, fd, &ev);
}
/**
- * pasta_netns_quit_handler() - Handle ns directory events, exit if ns is gone
+ * pasta_netns_quit_inotify_handler() - Handle inotify watch, exit if ns is gone
* @c: Execution context
* @inotify_fd: inotify file descriptor with watch on namespace directory
*/
-void pasta_netns_quit_handler(struct ctx *c, int inotify_fd)
+void pasta_netns_quit_inotify_handler(struct ctx *c, int inotify_fd)
{
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
const struct inotify_event *in_ev = (struct inotify_event *)buf;
@@ -410,3 +443,29 @@ void pasta_netns_quit_handler(struct ctx *c, int inotify_fd)
info("Namespace %s is gone, exiting", c->netns_base);
exit(EXIT_SUCCESS);
}
+
+/**
+ * pasta_netns_quit_timer_handler() - Handle timer, exit if ns is gone
+ * @c: Execution context
+ * @ref: epoll reference for timer descriptor
+ */
+void pasta_netns_quit_timer_handler(struct ctx *c, union epoll_ref ref)
+{
+ uint64_t expirations;
+ ssize_t n;
+ int fd;
+
+ n = read(ref.fd, &expirations, sizeof(expirations));
+ if (n < 0)
+ die("Namespace watch timer read() error: %s", strerror(errno));
+ if ((size_t)n < sizeof(expirations))
+ warn("Namespace watch timer: short read(): %zi", n);
+
+ fd = openat(ref.nsdir_fd, c->netns_base, O_PATH | O_CLOEXEC);
+ if (fd < 0) {
+ info("Namespace %s is gone, exiting", c->netns_base);
+ exit(EXIT_SUCCESS);
+ }
+
+ close(fd);
+}
diff --git a/pasta.h b/pasta.h
index 5d20063..4b063d1 100644
--- a/pasta.h
+++ b/pasta.h
@@ -13,7 +13,8 @@ void pasta_start_ns(struct ctx *c, uid_t uid, gid_t gid,
int argc, char *argv[]);
void pasta_ns_conf(struct ctx *c);
void pasta_child_handler(int signal);
-int pasta_netns_quit_init(const struct ctx *c);
-void pasta_netns_quit_handler(struct ctx *c, int inotify_fd);
+void pasta_netns_quit_init(const struct ctx *c);
+void pasta_netns_quit_inotify_handler(struct ctx *c, int inotify_fd);
+void pasta_netns_quit_timer_handler(struct ctx *c, union epoll_ref ref);
#endif /* PASTA_H */