Commit 298970c8 authored by John Fastabend's avatar John Fastabend Committed by Daniel Borkmann
Browse files

bpf, sockmap: Build helper to create connected socket pair



A common operation for testing is to spin up a pair of sockets that are
connected. Then we can use these to run specific tests that need to
send data, check BPF programs and so on.

The sockmap_listen programs already have this logic lets move it into
the new sockmap_helpers header file for general use.

Signed-off-by: default avatarJohn Fastabend <john.fastabend@gmail.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Reviewed-by: default avatarJakub Sitnicki <jakub@cloudflare.com>
Link: https://lore.kernel.org/bpf/20230523025618.113937-11-john.fastabend@gmail.com
parent 4e02588d
Loading
Loading
Loading
Loading
+118 −0
Original line number Diff line number Diff line
@@ -269,4 +269,122 @@ static inline struct sockaddr *sockaddr(struct sockaddr_storage *ss)
	return (struct sockaddr *)ss;
}

static inline int add_to_sockmap(int sock_mapfd, int fd1, int fd2)
{
	u64 value;
	u32 key;
	int err;

	key = 0;
	value = fd1;
	err = xbpf_map_update_elem(sock_mapfd, &key, &value, BPF_NOEXIST);
	if (err)
		return err;

	key = 1;
	value = fd2;
	return xbpf_map_update_elem(sock_mapfd, &key, &value, BPF_NOEXIST);
}

static inline int create_pair(int s, int family, int sotype, int *c, int *p)
{
	struct sockaddr_storage addr;
	socklen_t len;
	int err = 0;

	len = sizeof(addr);
	err = xgetsockname(s, sockaddr(&addr), &len);
	if (err)
		return err;

	*c = xsocket(family, sotype, 0);
	if (*c < 0)
		return errno;
	err = xconnect(*c, sockaddr(&addr), len);
	if (err) {
		err = errno;
		goto close_cli0;
	}

	*p = xaccept_nonblock(s, NULL, NULL);
	if (*p < 0) {
		err = errno;
		goto close_cli0;
	}
	return err;
close_cli0:
	close(*c);
	return err;
}

static inline int create_socket_pairs(int s, int family, int sotype,
				      int *c0, int *c1, int *p0, int *p1)
{
	int err;

	err = create_pair(s, family, sotype, c0, p0);
	if (err)
		return err;

	err = create_pair(s, family, sotype, c1, p1);
	if (err) {
		close(*c0);
		close(*p0);
	}
	return err;
}

static inline int enable_reuseport(int s, int progfd)
{
	int err, one = 1;

	err = xsetsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
	if (err)
		return -1;
	err = xsetsockopt(s, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &progfd,
			  sizeof(progfd));
	if (err)
		return -1;

	return 0;
}

static inline int socket_loopback_reuseport(int family, int sotype, int progfd)
{
	struct sockaddr_storage addr;
	socklen_t len;
	int err, s;

	init_addr_loopback(family, &addr, &len);

	s = xsocket(family, sotype, 0);
	if (s == -1)
		return -1;

	if (progfd >= 0)
		enable_reuseport(s, progfd);

	err = xbind(s, sockaddr(&addr), len);
	if (err)
		goto close;

	if (sotype & SOCK_DGRAM)
		return s;

	err = xlisten(s, SOMAXCONN);
	if (err)
		goto close;

	return s;
close:
	xclose(s);
	return -1;
}

static inline int socket_loopback(int family, int sotype)
{
	return socket_loopback_reuseport(family, sotype, -1);
}


#endif // __SOCKMAP_HELPERS__
+5 −102
Original line number Diff line number Diff line
@@ -29,58 +29,6 @@

#include "sockmap_helpers.h"

static int enable_reuseport(int s, int progfd)
{
	int err, one = 1;

	err = xsetsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
	if (err)
		return -1;
	err = xsetsockopt(s, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &progfd,
			  sizeof(progfd));
	if (err)
		return -1;

	return 0;
}

static int socket_loopback_reuseport(int family, int sotype, int progfd)
{
	struct sockaddr_storage addr;
	socklen_t len;
	int err, s;

	init_addr_loopback(family, &addr, &len);

	s = xsocket(family, sotype, 0);
	if (s == -1)
		return -1;

	if (progfd >= 0)
		enable_reuseport(s, progfd);

	err = xbind(s, sockaddr(&addr), len);
	if (err)
		goto close;

	if (sotype & SOCK_DGRAM)
		return s;

	err = xlisten(s, SOMAXCONN);
	if (err)
		goto close;

	return s;
close:
	xclose(s);
	return -1;
}

static int socket_loopback(int family, int sotype)
{
	return socket_loopback_reuseport(family, sotype, -1);
}

static void test_insert_invalid(struct test_sockmap_listen *skel __always_unused,
				int family, int sotype, int mapfd)
{
@@ -723,31 +671,12 @@ static const char *redir_mode_str(enum redir_mode mode)
	}
}

static int add_to_sockmap(int sock_mapfd, int fd1, int fd2)
{
	u64 value;
	u32 key;
	int err;

	key = 0;
	value = fd1;
	err = xbpf_map_update_elem(sock_mapfd, &key, &value, BPF_NOEXIST);
	if (err)
		return err;

	key = 1;
	value = fd2;
	return xbpf_map_update_elem(sock_mapfd, &key, &value, BPF_NOEXIST);
}

static void redir_to_connected(int family, int sotype, int sock_mapfd,
			       int verd_mapfd, enum redir_mode mode)
{
	const char *log_prefix = redir_mode_str(mode);
	struct sockaddr_storage addr;
	int s, c0, c1, p0, p1;
	unsigned int pass;
	socklen_t len;
	int err, n;
	u32 key;
	char b;
@@ -758,36 +687,13 @@ static void redir_to_connected(int family, int sotype, int sock_mapfd,
	if (s < 0)
		return;

	len = sizeof(addr);
	err = xgetsockname(s, sockaddr(&addr), &len);
	err = create_socket_pairs(s, family, sotype, &c0, &c1, &p0, &p1);
	if (err)
		goto close_srv;

	c0 = xsocket(family, sotype, 0);
	if (c0 < 0)
		goto close_srv;
	err = xconnect(c0, sockaddr(&addr), len);
	if (err)
		goto close_cli0;

	p0 = xaccept_nonblock(s, NULL, NULL);
	if (p0 < 0)
		goto close_cli0;

	c1 = xsocket(family, sotype, 0);
	if (c1 < 0)
		goto close_peer0;
	err = xconnect(c1, sockaddr(&addr), len);
	if (err)
		goto close_cli1;

	p1 = xaccept_nonblock(s, NULL, NULL);
	if (p1 < 0)
		goto close_cli1;

	err = add_to_sockmap(sock_mapfd, p0, p1);
	if (err)
		goto close_peer1;
		goto close;

	n = write(mode == REDIR_INGRESS ? c1 : p1, "a", 1);
	if (n < 0)
@@ -795,12 +701,12 @@ static void redir_to_connected(int family, int sotype, int sock_mapfd,
	if (n == 0)
		FAIL("%s: incomplete write", log_prefix);
	if (n < 1)
		goto close_peer1;
		goto close;

	key = SK_PASS;
	err = xbpf_map_lookup_elem(verd_mapfd, &key, &pass);
	if (err)
		goto close_peer1;
		goto close;
	if (pass != 1)
		FAIL("%s: want pass count 1, have %d", log_prefix, pass);
	n = recv_timeout(c0, &b, 1, 0, IO_TIMEOUT_SEC);
@@ -809,13 +715,10 @@ static void redir_to_connected(int family, int sotype, int sock_mapfd,
	if (n == 0)
		FAIL("%s: incomplete recv", log_prefix);

close_peer1:
close:
	xclose(p1);
close_cli1:
	xclose(c1);
close_peer0:
	xclose(p0);
close_cli0:
	xclose(c0);
close_srv:
	xclose(s);