aboutgitcodebugslistschat
path: root/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'util.c')
-rw-r--r--util.c162
1 files changed, 131 insertions, 31 deletions
diff --git a/util.c b/util.c
index 59a0cb2..1372eec 100644
--- a/util.c
+++ b/util.c
@@ -1,14 +1,19 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
/* PASST - Plug A Simple Socket Transport
+ * for qemu/UNIX domain socket mode
+ *
+ * PASTA - Pack A Subtle Tap Abstraction
+ * for network namespace/tap device mode
*
* util.c - Convenience helpers
*
* Copyright (c) 2020-2021 Red Hat GmbH
* Author: Stefano Brivio <sbrivio@redhat.com>
- *
*/
+#define _GNU_SOURCE
+#include <sched.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
@@ -20,13 +25,16 @@
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <sys/epoll.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <syslog.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
-#include "passt.h"
#include "util.h"
+#include "passt.h"
#ifdef DEBUG
#define logfn(name, level) \
@@ -183,73 +191,72 @@ char *ipv6_l4hdr(struct ipv6hdr *ip6h, uint8_t *proto)
* sock_l4() - Create and bind socket for given L4, add to epoll list
* @c: Execution context
* @af: Address family, AF_INET or AF_INET6
- * @proto: Protocol number, host order
+ * @proto: Protocol number
* @port: Port, host order
+ * @lo: Bind to loopback address only, if set
+ * @data: epoll reference portion for protocol handlers
*
* Return: newly created socket, -1 on error
*/
-int sock_l4(struct ctx *c, int af, uint16_t proto, uint16_t port)
+int sock_l4(struct ctx *c, int af, uint8_t proto, uint16_t port, int lo,
+ uint32_t data)
{
+ union epoll_ref ref = { .proto = proto, .data = data };
struct sockaddr_in addr4 = {
.sin_family = AF_INET,
.sin_port = htons(port),
- .sin_addr = { .s_addr = INADDR_ANY },
};
struct sockaddr_in6 addr6 = {
.sin6_family = AF_INET6,
.sin6_port = htons(port),
- .sin6_addr = IN6ADDR_ANY_INIT,
};
- struct epoll_event ev = { 0 };
const struct sockaddr *sa;
+ struct epoll_event ev;
int fd, sl, one = 1;
if (proto != IPPROTO_TCP && proto != IPPROTO_UDP &&
proto != IPPROTO_ICMP && proto != IPPROTO_ICMPV6)
return -1; /* Not implemented. */
- fd = socket(af, proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, proto);
+ if (proto == IPPROTO_TCP)
+ fd = socket(af, SOCK_STREAM | SOCK_NONBLOCK, proto);
+ else
+ fd = socket(af, SOCK_DGRAM | SOCK_NONBLOCK, proto);
if (fd < 0) {
perror("L4 socket");
return -1;
}
+ ref.s = fd;
if (af == AF_INET) {
+ if (lo)
+ addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ else
+ addr4.sin_addr.s_addr = htonl(INADDR_ANY);
+
sa = (const struct sockaddr *)&addr4;
sl = sizeof(addr4);
} else {
+ if (lo)
+ addr6.sin6_addr = in6addr_loopback;
+ else
+ addr6.sin6_addr = in6addr_any;
+
sa = (const struct sockaddr *)&addr6;
sl = sizeof(addr6);
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
}
- CHECK_SET_MIN_MAX_PROTO_FD(proto, IPPROTO_ICMP, icmp, fd);
- CHECK_SET_MIN_MAX_PROTO_FD(proto, IPPROTO_ICMPV6, icmp, fd);
- CHECK_SET_MIN_MAX_PROTO_FD(proto, IPPROTO_TCP, tcp, fd);
- CHECK_SET_MIN_MAX_PROTO_FD(proto, IPPROTO_UDP, udp, fd);
-
- if (proto == IPPROTO_UDP && PORT_IS_EPHEMERAL(port))
- goto epoll_add;
-
- if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6)
- goto epoll_add;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if (bind(fd, sa, sl) < 0) {
/* We'll fail to bind to low ports if we don't have enough
* capabilities, and we'll fail to bind on already bound ports,
- * this is fine. If this isn't the socket with the lowest number
- * for a given protocol, leave it open, to avoid unnecessary
- * holes in the numbering.
+ * this is fine.
*/
- if ((proto == IPPROTO_TCP && fd == c->tcp.fd_min) ||
- (proto == IPPROTO_UDP && fd == c->udp.fd_min) ||
- ((proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) &&
- fd == c->icmp.fd_min)) {
- close(fd);
- return 0;
- }
- return fd;
+ close(fd);
+ return 0;
}
if (proto == IPPROTO_TCP && listen(fd, 128) < 0) {
@@ -258,9 +265,8 @@ int sock_l4(struct ctx *c, int af, uint16_t proto, uint16_t port)
return -1;
}
-epoll_add:
ev.events = EPOLLIN;
- ev.data.fd = fd;
+ ev.data.u64 = ref.u64;
if (epoll_ctl(c->epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
perror("L4 epoll_ctl");
return -1;
@@ -286,3 +292,97 @@ int timespec_diff_ms(struct timespec *a, struct timespec *b)
return (a->tv_nsec - b->tv_nsec) / 1000000 +
(a->tv_sec - b->tv_sec) * 1000;
}
+
+/**
+ * bitmap_set() - Set single bit in bitmap
+ * @map: Pointer to bitmap
+ * @bit: Bit number to set
+ */
+void bitmap_set(uint8_t *map, int bit)
+{
+ map[bit / 8] |= 1 << (bit % 8);
+}
+
+/**
+ * bitmap_set() - Clear single bit in bitmap
+ * @map: Pointer to bitmap
+ * @bit: Bit number to clear
+ */
+void bitmap_clear(uint8_t *map, int bit)
+{
+ map[bit / 8] &= ~(1 << (bit % 8));
+}
+
+/**
+ * bitmap_isset() - Check for set bit in bitmap
+ * @map: Pointer to bitmap
+ * @bit: Bit number to check
+ *
+ * Return: non-zero if given bit is set, zero if it's not
+ */
+int bitmap_isset(uint8_t *map, int bit)
+{
+ return map[bit / 8] & (1 << bit % 8);
+}
+
+/**
+ * procfs_scan_listen() - Set bits for listening TCP or UDP sockets from procfs
+ * @name: Corresponding name of file under /proc/net/
+ * @map: Bitmap where numbers of ports in listening state will be set
+ */
+void procfs_scan_listen(char *name, uint8_t *map)
+{
+ char line[200], path[PATH_MAX];
+ unsigned long port;
+ unsigned int state;
+ FILE *fp;
+
+ snprintf(path, PATH_MAX, "/proc/net/%s", name);
+ if (!(fp = fopen(path, "r")))
+ return;
+
+ fgets(line, sizeof(line), fp);
+ while (fgets(line, sizeof(line), fp)) {
+ if (sscanf(line, "%*u: %*x:%lx %*x:%*x %x", &port, &state) != 2)
+ continue;
+
+ /* See enum in kernel's include/net/tcp_states.h */
+ if ((strstr(name, "tcp") && state != 0x0a) ||
+ (strstr(name, "udp") && state != 0x07))
+ continue;
+
+ bitmap_set(map, port);
+ }
+
+ fclose(fp);
+}
+
+/**
+ * ns_enter() - Enter user and network namespaces of process with given PID
+ * @target_pid: Process PID
+ *
+ * Return: 0 on success, -1 on failure
+ */
+int ns_enter(int target_pid)
+{
+ char ns[PATH_MAX];
+ int fd;
+
+ snprintf(ns, PATH_MAX, "/proc/%i/ns/user", target_pid);
+ if ((fd = open(ns, O_RDONLY)) < 0 || setns(fd, 0))
+ goto fail;
+ close(fd);
+
+ snprintf(ns, PATH_MAX, "/proc/%i/ns/net", target_pid);
+ if ((fd = open(ns, O_RDONLY)) < 0 || setns(fd, 0))
+ goto fail;
+ close(fd);
+
+ return 0;
+
+fail:
+ if (fd != -1)
+ close(fd);
+
+ return -1;
+}