All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] bpf: Add BPF_CGROUP_UNIX_CONNECT attach type
@ 2022-12-06 16:32 Daan De Meyer
  2022-12-07 17:37 ` Yonghong Song
  0 siblings, 1 reply; 2+ messages in thread
From: Daan De Meyer @ 2022-12-06 16:32 UTC (permalink / raw)
  To: bpf; +Cc: Daan De Meyer, martin.lau, kernel-team

This hook allows intercepting connect() calls on unix sockets. After
running the hook, we recalculate the sockaddr length as it the hook
might have replaced the sun_path with a longer/shorter one. This is
safe because all kernelspace sockaddrs are stored in instances of
sockaddr_storage which is guaranteed to larger than sockaddr_un.

This hook can be used when users want to multiplex connect()
calls to a single unix socket to multiple different processes
behind the scenes by redirecting the connect() calls to process
specific sockets.

Signed-off-by: Daan De Meyer <daan.j.demeyer@gmail.com>
---
 Documentation/bpf/libbpf/program_types.rst    |  2 +
 include/linux/bpf-cgroup-defs.h               |  1 +
 include/linux/bpf-cgroup.h                    | 12 ++-
 include/uapi/linux/bpf.h                      | 10 ++-
 kernel/bpf/cgroup.c                           |  5 +-
 kernel/bpf/syscall.c                          |  3 +
 net/core/filter.c                             | 28 +++++++
 net/unix/af_unix.c                            | 37 +++++++++
 tools/bpf/bpftool/common.c                    |  1 +
 tools/include/uapi/linux/bpf.h                | 10 ++-
 tools/lib/bpf/libbpf.c                        |  2 +
 .../selftests/bpf/prog_tests/section_names.c  |  5 ++
 .../selftests/bpf/progs/connectun_prog.c      | 27 ++++++
 tools/testing/selftests/bpf/test_sock_addr.c  | 83 +++++++++++++++++--
 14 files changed, 206 insertions(+), 20 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/progs/connectun_prog.c

diff --git a/Documentation/bpf/libbpf/program_types.rst b/Documentation/bpf/libbpf/program_types.rst
index ad4d4d5eecb0..7d0bcd883561 100644
--- a/Documentation/bpf/libbpf/program_types.rst
+++ b/Documentation/bpf/libbpf/program_types.rst
@@ -56,6 +56,8 @@ described in more detail in the footnotes.
 |                                           | ``BPF_CGROUP_UDP6_RECVMSG``            | ``cgroup/recvmsg6``              |           |
 +                                           +----------------------------------------+----------------------------------+-----------+
 |                                           | ``BPF_CGROUP_UDP6_SENDMSG``            | ``cgroup/sendmsg6``              |           |
+|                                           +----------------------------------------+----------------------------------+-----------+
+|                                           | ``BPF_CGROUP_UNIX_CONNECT``            | ``cgroup/connectun``             |           |
 +-------------------------------------------+----------------------------------------+----------------------------------+-----------+
 | ``BPF_PROG_TYPE_CGROUP_SOCK``             | ``BPF_CGROUP_INET4_POST_BIND``         | ``cgroup/post_bind4``            |           |
 +                                           +----------------------------------------+----------------------------------+-----------+
diff --git a/include/linux/bpf-cgroup-defs.h b/include/linux/bpf-cgroup-defs.h
index 7b121bd780eb..85da6dc212fb 100644
--- a/include/linux/bpf-cgroup-defs.h
+++ b/include/linux/bpf-cgroup-defs.h
@@ -28,6 +28,7 @@ enum cgroup_bpf_attach_type {
 	CGROUP_INET6_BIND,
 	CGROUP_INET4_CONNECT,
 	CGROUP_INET6_CONNECT,
+	CGROUP_UNIX_CONNECT,
 	CGROUP_INET4_POST_BIND,
 	CGROUP_INET6_POST_BIND,
 	CGROUP_UDP4_SENDMSG,
diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h
index 57e9e109257e..3e3a450e0f17 100644
--- a/include/linux/bpf-cgroup.h
+++ b/include/linux/bpf-cgroup.h
@@ -48,6 +48,7 @@ to_cgroup_bpf_attach_type(enum bpf_attach_type attach_type)
 	CGROUP_ATYPE(CGROUP_INET6_BIND);
 	CGROUP_ATYPE(CGROUP_INET4_CONNECT);
 	CGROUP_ATYPE(CGROUP_INET6_CONNECT);
+	CGROUP_ATYPE(CGROUP_UNIX_CONNECT);
 	CGROUP_ATYPE(CGROUP_INET4_POST_BIND);
 	CGROUP_ATYPE(CGROUP_INET6_POST_BIND);
 	CGROUP_ATYPE(CGROUP_UDP4_SENDMSG);
@@ -273,7 +274,8 @@ static inline bool cgroup_bpf_sock_enabled(struct sock *sk,
 
 #define BPF_CGROUP_PRE_CONNECT_ENABLED(sk)				       \
 	((cgroup_bpf_enabled(CGROUP_INET4_CONNECT) ||		       \
-	  cgroup_bpf_enabled(CGROUP_INET6_CONNECT)) &&		       \
+	  cgroup_bpf_enabled(CGROUP_INET6_CONNECT) ||		       \
+	  cgroup_bpf_enabled(CGROUP_UNIX_CONNECT)) &&		       \
 	 (sk)->sk_prot->pre_connect)
 
 #define BPF_CGROUP_RUN_PROG_INET4_CONNECT(sk, uaddr)			       \
@@ -282,12 +284,18 @@ static inline bool cgroup_bpf_sock_enabled(struct sock *sk,
 #define BPF_CGROUP_RUN_PROG_INET6_CONNECT(sk, uaddr)			       \
 	BPF_CGROUP_RUN_SA_PROG(sk, uaddr, CGROUP_INET6_CONNECT)
 
+#define BPF_CGROUP_RUN_PROG_UNIX_CONNECT(sk, uaddr)		               \
+	BPF_CGROUP_RUN_SA_PROG(sk, uaddr, CGROUP_UNIX_CONNECT)
+
 #define BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr)		       \
 	BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, CGROUP_INET4_CONNECT, NULL)
 
 #define BPF_CGROUP_RUN_PROG_INET6_CONNECT_LOCK(sk, uaddr)		       \
 	BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, CGROUP_INET6_CONNECT, NULL)
 
+#define BPF_CGROUP_RUN_PROG_UNIX_CONNECT_LOCK(sk, uaddr)		       \
+	BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, CGROUP_UNIX_CONNECT, NULL)
+
 #define BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK(sk, uaddr, t_ctx)		       \
 	BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, CGROUP_UDP4_SENDMSG, t_ctx)
 
@@ -491,6 +499,8 @@ static inline int bpf_percpu_cgroup_storage_update(struct bpf_map *map,
 #define BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_INET6_CONNECT(sk, uaddr) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_INET6_CONNECT_LOCK(sk, uaddr) ({ 0; })
+#define BPF_CGROUP_RUN_PROG_UNIX_CONNECT(sk, uaddr) ({ 0; })
+#define BPF_CGROUP_RUN_PROG_UNIX_CONNECT_LOCK(sk, uaddr) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK(sk, uaddr, t_ctx) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK(sk, uaddr, t_ctx) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_UDP4_RECVMSG_LOCK(sk, uaddr) ({ 0; })
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index f89de51a45db..8acfaddca772 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1001,6 +1001,7 @@ enum bpf_attach_type {
 	BPF_CGROUP_INET6_BIND,
 	BPF_CGROUP_INET4_CONNECT,
 	BPF_CGROUP_INET6_CONNECT,
+	BPF_CGROUP_UNIX_CONNECT,
 	BPF_CGROUP_INET4_POST_BIND,
 	BPF_CGROUP_INET6_POST_BIND,
 	BPF_CGROUP_UDP4_SENDMSG,
@@ -2575,8 +2576,8 @@ union bpf_attr {
  * 		*bpf_socket* should be one of the following:
  *
  * 		* **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.
- * 		* **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**
- * 		  and **BPF_CGROUP_INET6_CONNECT**.
+ * 		* **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**,
+ * 		  **BPF_CGROUP_INET6_CONNECT** and **BPF_CGROUP_UNIX_CONNECT**.
  *
  * 		This helper actually implements a subset of **setsockopt()**.
  * 		It supports the following *level*\ s:
@@ -2809,8 +2810,8 @@ union bpf_attr {
  * 		*bpf_socket* should be one of the following:
  *
  * 		* **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.
- * 		* **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**
- * 		  and **BPF_CGROUP_INET6_CONNECT**.
+ * 		* **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**,
+ * 		  **BPF_CGROUP_INET6_CONNECT** and **BPF_CGROUP_UNIX_CONNECT**.
  *
  * 		This helper actually implements a subset of **getsockopt()**.
  * 		It supports the same set of *optname*\ s that is supported by
@@ -6353,6 +6354,7 @@ struct bpf_sock_addr {
 	__u32 user_ip6[4];	/* Allows 1,2,4,8-byte read and 4,8-byte write.
 				 * Stored in network byte order.
 				 */
+	char user_path[108];    /* Allows 1 byte read and write. */
 	__u32 user_port;	/* Allows 1,2,4-byte read and 4-byte write.
 				 * Stored in network byte order
 				 */
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index bf2fdb33fb31..f1d20f69b260 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -1454,7 +1454,7 @@ EXPORT_SYMBOL(__cgroup_bpf_run_filter_sk);
  * @flags: Pointer to u32 which contains higher bits of BPF program
  *         return value (OR'ed together).
  *
- * socket is expected to be of type INET or INET6.
+ * socket is expected to be of type INET, INET6 or UNIX.
  *
  * This function will return %-EPERM if an attached program is found and
  * returned value != 1 during execution. In all other cases, 0 is returned.
@@ -1476,7 +1476,8 @@ int __cgroup_bpf_run_filter_sock_addr(struct sock *sk,
 	/* Check socket family since not all sockets represent network
 	 * endpoint (e.g. AF_UNIX).
 	 */
-	if (sk->sk_family != AF_INET && sk->sk_family != AF_INET6)
+	if (sk->sk_family != AF_INET && sk->sk_family != AF_INET6 &&
+		sk->sk_family != AF_UNIX)
 		return 0;
 
 	if (!ctx.uaddr) {
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 35972afb6850..bbfc918b7541 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2372,6 +2372,7 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
 		case BPF_CGROUP_INET6_BIND:
 		case BPF_CGROUP_INET4_CONNECT:
 		case BPF_CGROUP_INET6_CONNECT:
+		case BPF_CGROUP_UNIX_CONNECT:
 		case BPF_CGROUP_INET4_GETPEERNAME:
 		case BPF_CGROUP_INET6_GETPEERNAME:
 		case BPF_CGROUP_INET4_GETSOCKNAME:
@@ -3420,6 +3421,7 @@ attach_type_to_prog_type(enum bpf_attach_type attach_type)
 	case BPF_CGROUP_INET6_BIND:
 	case BPF_CGROUP_INET4_CONNECT:
 	case BPF_CGROUP_INET6_CONNECT:
+	case BPF_CGROUP_UNIX_CONNECT:
 	case BPF_CGROUP_INET4_GETPEERNAME:
 	case BPF_CGROUP_INET6_GETPEERNAME:
 	case BPF_CGROUP_INET4_GETSOCKNAME:
@@ -3587,6 +3589,7 @@ static int bpf_prog_query(const union bpf_attr *attr,
 	case BPF_CGROUP_INET6_POST_BIND:
 	case BPF_CGROUP_INET4_CONNECT:
 	case BPF_CGROUP_INET6_CONNECT:
+	case BPF_CGROUP_UNIX_CONNECT:
 	case BPF_CGROUP_INET4_GETPEERNAME:
 	case BPF_CGROUP_INET6_GETPEERNAME:
 	case BPF_CGROUP_INET4_GETSOCKNAME:
diff --git a/net/core/filter.c b/net/core/filter.c
index 8607136b6e2c..5dccd74afd77 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -26,6 +26,7 @@
 #include <linux/socket.h>
 #include <linux/sock_diag.h>
 #include <linux/in.h>
+#include <linux/un.h>
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/if_packet.h>
@@ -7665,6 +7666,7 @@ sock_addr_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 		switch (prog->expected_attach_type) {
 		case BPF_CGROUP_INET4_CONNECT:
 		case BPF_CGROUP_INET6_CONNECT:
+		case BPF_CGROUP_UNIX_CONNECT:
 			return &bpf_bind_proto;
 		default:
 			return NULL;
@@ -7695,6 +7697,7 @@ sock_addr_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 		case BPF_CGROUP_INET6_BIND:
 		case BPF_CGROUP_INET4_CONNECT:
 		case BPF_CGROUP_INET6_CONNECT:
+		case BPF_CGROUP_UNIX_CONNECT:
 		case BPF_CGROUP_UDP4_RECVMSG:
 		case BPF_CGROUP_UDP6_RECVMSG:
 		case BPF_CGROUP_UDP4_SENDMSG:
@@ -7713,6 +7716,7 @@ sock_addr_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 		case BPF_CGROUP_INET6_BIND:
 		case BPF_CGROUP_INET4_CONNECT:
 		case BPF_CGROUP_INET6_CONNECT:
+		case BPF_CGROUP_UNIX_CONNECT:
 		case BPF_CGROUP_UDP4_RECVMSG:
 		case BPF_CGROUP_UDP6_RECVMSG:
 		case BPF_CGROUP_UDP4_SENDMSG:
@@ -8830,6 +8834,14 @@ static bool sock_addr_is_valid_access(int off, int size,
 			return false;
 		}
 		break;
+	case bpf_ctx_range_till(struct bpf_sock_addr, user_path[0], user_path[107]):
+		switch (prog->expected_attach_type) {
+		case BPF_CGROUP_UNIX_CONNECT:
+			break;
+		default:
+			return false;
+		}
+		break;
 	}
 
 	switch (off) {
@@ -8869,6 +8881,10 @@ static bool sock_addr_is_valid_access(int off, int size,
 				return false;
 		}
 		break;
+	case bpf_ctx_range_till(struct bpf_sock_addr, user_path[0], user_path[107]):
+		if (size != sizeof(char))
+			return false;
+		break;
 	case offsetof(struct bpf_sock_addr, sk):
 		if (type != BPF_READ)
 			return false;
@@ -9931,6 +9947,18 @@ static u32 sock_addr_convert_ctx_access(enum bpf_access_type type,
 			tmp_reg);
 		break;
 
+	case bpf_ctx_range_till(struct bpf_sock_addr, user_path[0], user_path[107]):
+		/* In kernelspace, addresses are always stored in
+		 * sockaddr_storage so any access in the full range of
+		 * sockaddr_un.sun_path is safe.
+		 */
+		off = si->off;
+		off -= offsetof(struct bpf_sock_addr, user_path[0]);
+		SOCK_ADDR_LOAD_OR_STORE_NESTED_FIELD_SIZE_OFF(
+			struct bpf_sock_addr_kern, struct sockaddr_un, uaddr,
+			sun_path, BPF_SIZE(si->code), off, tmp_reg);
+		break;
+
 	case offsetof(struct bpf_sock_addr, user_port):
 		/* To get port we need to know sa_family first and then treat
 		 * sockaddr as either sockaddr_in or sockaddr_in6.
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index b3545fc68097..afd77908edba 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -115,6 +115,7 @@
 #include <linux/freezer.h>
 #include <linux/file.h>
 #include <linux/btf_ids.h>
+#include <linux/bpf-cgroup.h>
 
 #include "scm.h"
 
@@ -920,11 +921,18 @@ static void unix_unhash(struct sock *sk)
 	 */
 }
 
+static int unix_pre_connect(struct sock *sk, struct sockaddr *uaddr,
+			    int addr_len)
+{
+	return BPF_CGROUP_RUN_PROG_UNIX_CONNECT(sk, uaddr);
+}
+
 struct proto unix_dgram_proto = {
 	.name			= "UNIX",
 	.owner			= THIS_MODULE,
 	.obj_size		= sizeof(struct unix_sock),
 	.close			= unix_close,
+	.pre_connect            = unix_pre_connect,
 #ifdef CONFIG_BPF_SYSCALL
 	.psock_update_sk_prot	= unix_dgram_bpf_update_proto,
 #endif
@@ -936,6 +944,7 @@ struct proto unix_stream_proto = {
 	.obj_size		= sizeof(struct unix_sock),
 	.close			= unix_close,
 	.unhash			= unix_unhash,
+	.pre_connect            = unix_pre_connect,
 #ifdef CONFIG_BPF_SYSCALL
 	.psock_update_sk_prot	= unix_stream_bpf_update_proto,
 #endif
@@ -1367,6 +1376,20 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
 				goto out;
 		}
 
+		if (BPF_CGROUP_PRE_CONNECT_ENABLED(sk)) {
+			err = sk->sk_prot->pre_connect(sk, addr, alen);
+			if (err)
+				goto out;
+
+			alen = offsetof(struct sockaddr_un, sun_path) +
+				strnlen(sunaddr->sun_path,
+				sizeof(sunaddr->sun_path)) + 1;
+
+			err = unix_validate_addr(sunaddr, alen);
+			if (err)
+				goto out;
+		}
+
 restart:
 		other = unix_find_other(sock_net(sk), sunaddr, alen, sock->type);
 		if (IS_ERR(other)) {
@@ -1496,6 +1519,20 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
 	if (skb == NULL)
 		goto out;
 
+	if (BPF_CGROUP_PRE_CONNECT_ENABLED(sk)) {
+		err = sk->sk_prot->pre_connect(sk, uaddr, addr_len);
+		if (err)
+			goto out;
+
+		addr_len = offsetof(struct sockaddr_un, sun_path) +
+			   strnlen(sunaddr->sun_path,
+				   sizeof(sunaddr->sun_path)) + 1;
+
+		err = unix_validate_addr(sunaddr, addr_len);
+		if (err)
+			goto out;
+	}
+
 restart:
 	/*  Find listening sock. */
 	other = unix_find_other(net, sunaddr, addr_len, sk->sk_type);
diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c
index c90b756945e3..3fd69d448225 100644
--- a/tools/bpf/bpftool/common.c
+++ b/tools/bpf/bpftool/common.c
@@ -1067,6 +1067,7 @@ const char *bpf_attach_type_input_str(enum bpf_attach_type t)
 	case BPF_CGROUP_INET6_BIND:		return "bind6";
 	case BPF_CGROUP_INET4_CONNECT:		return "connect4";
 	case BPF_CGROUP_INET6_CONNECT:		return "connect6";
+	case BPF_CGROUP_UNIX_CONNECT:		return "connectun";
 	case BPF_CGROUP_INET4_POST_BIND:	return "post_bind4";
 	case BPF_CGROUP_INET6_POST_BIND:	return "post_bind6";
 	case BPF_CGROUP_INET4_GETPEERNAME:	return "getpeername4";
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index f89de51a45db..8acfaddca772 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1001,6 +1001,7 @@ enum bpf_attach_type {
 	BPF_CGROUP_INET6_BIND,
 	BPF_CGROUP_INET4_CONNECT,
 	BPF_CGROUP_INET6_CONNECT,
+	BPF_CGROUP_UNIX_CONNECT,
 	BPF_CGROUP_INET4_POST_BIND,
 	BPF_CGROUP_INET6_POST_BIND,
 	BPF_CGROUP_UDP4_SENDMSG,
@@ -2575,8 +2576,8 @@ union bpf_attr {
  * 		*bpf_socket* should be one of the following:
  *
  * 		* **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.
- * 		* **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**
- * 		  and **BPF_CGROUP_INET6_CONNECT**.
+ * 		* **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**,
+ * 		  **BPF_CGROUP_INET6_CONNECT** and **BPF_CGROUP_UNIX_CONNECT**.
  *
  * 		This helper actually implements a subset of **setsockopt()**.
  * 		It supports the following *level*\ s:
@@ -2809,8 +2810,8 @@ union bpf_attr {
  * 		*bpf_socket* should be one of the following:
  *
  * 		* **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.
- * 		* **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**
- * 		  and **BPF_CGROUP_INET6_CONNECT**.
+ * 		* **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**,
+ * 		  **BPF_CGROUP_INET6_CONNECT** and **BPF_CGROUP_UNIX_CONNECT**.
  *
  * 		This helper actually implements a subset of **getsockopt()**.
  * 		It supports the same set of *optname*\ s that is supported by
@@ -6353,6 +6354,7 @@ struct bpf_sock_addr {
 	__u32 user_ip6[4];	/* Allows 1,2,4,8-byte read and 4,8-byte write.
 				 * Stored in network byte order.
 				 */
+	char user_path[108];    /* Allows 1 byte read and write. */
 	__u32 user_port;	/* Allows 1,2,4-byte read and 4-byte write.
 				 * Stored in network byte order
 				 */
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 2a82f49ce16f..ae00819b86f6 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -82,6 +82,7 @@ static const char * const attach_type_name[] = {
 	[BPF_CGROUP_INET6_BIND]		= "cgroup_inet6_bind",
 	[BPF_CGROUP_INET4_CONNECT]	= "cgroup_inet4_connect",
 	[BPF_CGROUP_INET6_CONNECT]	= "cgroup_inet6_connect",
+	[BPF_CGROUP_UNIX_CONNECT]       = "cgroup_unix_connect",
 	[BPF_CGROUP_INET4_POST_BIND]	= "cgroup_inet4_post_bind",
 	[BPF_CGROUP_INET6_POST_BIND]	= "cgroup_inet6_post_bind",
 	[BPF_CGROUP_INET4_GETPEERNAME]	= "cgroup_inet4_getpeername",
@@ -8592,6 +8593,7 @@ static const struct bpf_sec_def section_defs[] = {
 	SEC_DEF("cgroup/bind6",		CGROUP_SOCK_ADDR, BPF_CGROUP_INET6_BIND, SEC_ATTACHABLE),
 	SEC_DEF("cgroup/connect4",	CGROUP_SOCK_ADDR, BPF_CGROUP_INET4_CONNECT, SEC_ATTACHABLE),
 	SEC_DEF("cgroup/connect6",	CGROUP_SOCK_ADDR, BPF_CGROUP_INET6_CONNECT, SEC_ATTACHABLE),
+	SEC_DEF("cgroup/connectun",	CGROUP_SOCK_ADDR, BPF_CGROUP_UNIX_CONNECT, SEC_ATTACHABLE),
 	SEC_DEF("cgroup/sendmsg4",	CGROUP_SOCK_ADDR, BPF_CGROUP_UDP4_SENDMSG, SEC_ATTACHABLE),
 	SEC_DEF("cgroup/sendmsg6",	CGROUP_SOCK_ADDR, BPF_CGROUP_UDP6_SENDMSG, SEC_ATTACHABLE),
 	SEC_DEF("cgroup/recvmsg4",	CGROUP_SOCK_ADDR, BPF_CGROUP_UDP4_RECVMSG, SEC_ATTACHABLE),
diff --git a/tools/testing/selftests/bpf/prog_tests/section_names.c b/tools/testing/selftests/bpf/prog_tests/section_names.c
index 8b571890c57e..1b5723106c71 100644
--- a/tools/testing/selftests/bpf/prog_tests/section_names.c
+++ b/tools/testing/selftests/bpf/prog_tests/section_names.c
@@ -123,6 +123,11 @@ static struct sec_name_test tests[] = {
 		{0, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_INET6_CONNECT},
 		{0, BPF_CGROUP_INET6_CONNECT},
 	},
+	{
+		"cgroup/connectun",
+		{0, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_UNIX_CONNECT},
+		{0, BPF_CGROUP_UNIX_CONNECT},
+	},
 	{
 		"cgroup/sendmsg4",
 		{0, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_UDP4_SENDMSG},
diff --git a/tools/testing/selftests/bpf/progs/connectun_prog.c b/tools/testing/selftests/bpf/progs/connectun_prog.c
new file mode 100644
index 000000000000..91097bd8df3c
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/connectun_prog.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Facebook
+
+#include <string.h>
+
+#include <linux/stddef.h>
+#include <linux/bpf.h>
+#include <linux/if.h>
+#include <errno.h>
+
+#include <bpf/bpf_helpers.h>
+
+#define DST_REWRITE_PATH	"/tmp/bpf_cgroup_unix_test_rewrite"
+
+SEC("cgroup/connectun")
+int connect_un_prog(struct bpf_sock_addr *ctx)
+{
+	if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
+		return 0;
+
+	/* Rewrite destination. */
+	memcpy(ctx->user_path, DST_REWRITE_PATH, sizeof(DST_REWRITE_PATH));
+
+	return 1;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_sock_addr.c b/tools/testing/selftests/bpf/test_sock_addr.c
index 2c89674fc62c..3e747b7b4f94 100644
--- a/tools/testing/selftests/bpf/test_sock_addr.c
+++ b/tools/testing/selftests/bpf/test_sock_addr.c
@@ -12,6 +12,7 @@
 #include <sys/types.h>
 #include <sys/select.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 
 #include <linux/filter.h>
 
@@ -28,6 +29,7 @@
 #define CG_PATH	"/foo"
 #define CONNECT4_PROG_PATH	"./connect4_prog.bpf.o"
 #define CONNECT6_PROG_PATH	"./connect6_prog.bpf.o"
+#define CONNECTUN_PROG_PATH	"./connectun_prog.bpf.o"
 #define SENDMSG4_PROG_PATH	"./sendmsg4_prog.bpf.o"
 #define SENDMSG6_PROG_PATH	"./sendmsg6_prog.bpf.o"
 #define RECVMSG4_PROG_PATH	"./recvmsg4_prog.bpf.o"
@@ -51,6 +53,9 @@
 #define SERV6_PORT		6060
 #define SERV6_REWRITE_PORT	6666
 
+#define SERV_UNIX_PATH		"/tmp/bpf_cgroup_unix_test"
+#define SERV_UNIX_REWRITE_PATH	"/tmp/bpf_cgroup_unix_test_rewrite"
+
 #define INET_NTOP_BUF	40
 
 struct sock_addr_test;
@@ -90,6 +95,7 @@ static int bind4_prog_load(const struct sock_addr_test *test);
 static int bind6_prog_load(const struct sock_addr_test *test);
 static int connect4_prog_load(const struct sock_addr_test *test);
 static int connect6_prog_load(const struct sock_addr_test *test);
+static int connectun_prog_load(const struct sock_addr_test *test);
 static int sendmsg_allow_prog_load(const struct sock_addr_test *test);
 static int sendmsg_deny_prog_load(const struct sock_addr_test *test);
 static int recvmsg_allow_prog_load(const struct sock_addr_test *test);
@@ -331,6 +337,34 @@ static struct sock_addr_test tests[] = {
 		SRC6_REWRITE_IP,
 		SUCCESS,
 	},
+	{
+		"connectun: rewrite SOCK_STREAM path",
+		connectun_prog_load,
+		BPF_CGROUP_UNIX_CONNECT,
+		BPF_CGROUP_UNIX_CONNECT,
+		AF_UNIX,
+		SOCK_STREAM,
+		SERV_UNIX_PATH,
+		0,
+		SERV_UNIX_REWRITE_PATH,
+		0,
+		NULL,
+		SUCCESS,
+	},
+	{
+		"connectun: rewrite SOCK_DGRAM path",
+		connectun_prog_load,
+		BPF_CGROUP_UNIX_CONNECT,
+		BPF_CGROUP_UNIX_CONNECT,
+		AF_UNIX,
+		SOCK_DGRAM,
+		SERV_UNIX_PATH,
+		0,
+		SERV_UNIX_REWRITE_PATH,
+		0,
+		NULL,
+		SUCCESS,
+	},
 
 	/* sendmsg */
 	{
@@ -603,13 +637,14 @@ static struct sock_addr_test tests[] = {
 	},
 };
 
-static int mk_sockaddr(int domain, const char *ip, unsigned short port,
+static int mk_sockaddr(int domain, const char *address, unsigned short port,
 		       struct sockaddr *addr, socklen_t addr_len)
 {
 	struct sockaddr_in6 *addr6;
 	struct sockaddr_in *addr4;
+	struct sockaddr_un *addrun;
 
-	if (domain != AF_INET && domain != AF_INET6) {
+	if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNIX) {
 		log_err("Unsupported address family");
 		return -1;
 	}
@@ -622,8 +657,8 @@ static int mk_sockaddr(int domain, const char *ip, unsigned short port,
 		addr4 = (struct sockaddr_in *)addr;
 		addr4->sin_family = domain;
 		addr4->sin_port = htons(port);
-		if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1) {
-			log_err("Invalid IPv4: %s", ip);
+		if (inet_pton(domain, address, (void *)&addr4->sin_addr) != 1) {
+			log_err("Invalid IPv4: %s", address);
 			return -1;
 		}
 	} else if (domain == AF_INET6) {
@@ -632,10 +667,16 @@ static int mk_sockaddr(int domain, const char *ip, unsigned short port,
 		addr6 = (struct sockaddr_in6 *)addr;
 		addr6->sin6_family = domain;
 		addr6->sin6_port = htons(port);
-		if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1) {
-			log_err("Invalid IPv6: %s", ip);
+		if (inet_pton(domain, address, (void *)&addr6->sin6_addr) != 1) {
+			log_err("Invalid IPv6: %s", address);
 			return -1;
 		}
+	} else if (domain == AF_UNIX) {
+		if (addr_len < sizeof(struct sockaddr_un))
+			return -1;
+		addrun = (struct sockaddr_un *)addr;
+		addrun->sun_family = domain;
+		strcpy(addrun->sun_path, address);
 	}
 
 	return 0;
@@ -714,6 +755,11 @@ static int connect6_prog_load(const struct sock_addr_test *test)
 	return load_path(test, CONNECT6_PROG_PATH);
 }
 
+static int connectun_prog_load(const struct sock_addr_test *test)
+{
+	return load_path(test, CONNECTUN_PROG_PATH);
+}
+
 static int xmsg_ret_only_prog_load(const struct sock_addr_test *test,
 				   int32_t rc)
 {
@@ -890,6 +936,7 @@ static int cmp_addr(const struct sockaddr_storage *addr1,
 {
 	const struct sockaddr_in *four1, *four2;
 	const struct sockaddr_in6 *six1, *six2;
+	const struct sockaddr_un *un1, *un2;
 
 	if (addr1->ss_family != addr2->ss_family)
 		return -1;
@@ -905,6 +952,10 @@ static int cmp_addr(const struct sockaddr_storage *addr1,
 		return !((six1->sin6_port == six2->sin6_port || !cmp_port) &&
 			 !memcmp(&six1->sin6_addr, &six2->sin6_addr,
 				 sizeof(struct in6_addr)));
+	} else if (addr1->ss_family == AF_UNIX) {
+		un1 = (const struct sockaddr_un *)addr1;
+		un2 = (const struct sockaddr_un *)addr2;
+		return memcmp(un1->sun_path, un2->sun_path, sizeof(un1->sun_path));
 	}
 
 	return -1;
@@ -949,6 +1000,13 @@ static int start_server(int type, const struct sockaddr_storage *addr,
 		goto out;
 	}
 
+	if (addr->ss_family == AF_UNIX) {
+		struct sockaddr_un *un = (struct sockaddr_un *) addr;
+
+		addr_len = strlen(un->sun_path) + sizeof(un->sun_family);
+		unlink(un->sun_path);
+	}
+
 	if (bind(fd, (const struct sockaddr *)addr, addr_len) == -1) {
 		log_err("Failed to bind server socket");
 		goto close_out;
@@ -977,7 +1035,7 @@ static int connect_to_server(int type, const struct sockaddr_storage *addr,
 
 	domain = addr->ss_family;
 
-	if (domain != AF_INET && domain != AF_INET6) {
+	if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNIX) {
 		log_err("Unsupported address family");
 		goto err;
 	}
@@ -988,6 +1046,12 @@ static int connect_to_server(int type, const struct sockaddr_storage *addr,
 		goto err;
 	}
 
+	if (addr->ss_family == AF_UNIX) {
+		struct sockaddr_un *un = (struct sockaddr_un *) addr;
+
+		addr_len = strlen(un->sun_path) + sizeof(un->sun_family);
+	}
+
 	if (connect(fd, (const struct sockaddr *)addr, addr_len) == -1) {
 		log_err("Fail to connect to server");
 		goto err;
@@ -1225,10 +1289,10 @@ static int run_connect_test_case(const struct sock_addr_test *test)
 	if (cmp_peer_addr(clientfd, &expected_addr))
 		goto err;
 
-	if (cmp_local_ip(clientfd, &expected_src_addr))
+	if (test->domain != AF_UNIX && cmp_local_ip(clientfd, &expected_src_addr))
 		goto err;
 
-	if (test->type == SOCK_STREAM) {
+	if (test->domain != AF_UNIX && test->type == SOCK_STREAM) {
 		/* Test TCP Fast Open scenario */
 		clientfd = fastconnect_to_server(&requested_addr, addr_len);
 		if (clientfd == -1)
@@ -1346,6 +1410,7 @@ static int run_test_case(int cgfd, const struct sock_addr_test *test)
 		break;
 	case BPF_CGROUP_INET4_CONNECT:
 	case BPF_CGROUP_INET6_CONNECT:
+	case BPF_CGROUP_UNIX_CONNECT:
 		err = run_connect_test_case(test);
 		break;
 	case BPF_CGROUP_UDP4_SENDMSG:
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] bpf: Add BPF_CGROUP_UNIX_CONNECT attach type
  2022-12-06 16:32 [PATCH] bpf: Add BPF_CGROUP_UNIX_CONNECT attach type Daan De Meyer
@ 2022-12-07 17:37 ` Yonghong Song
  0 siblings, 0 replies; 2+ messages in thread
From: Yonghong Song @ 2022-12-07 17:37 UTC (permalink / raw)
  To: Daan De Meyer, bpf; +Cc: martin.lau, kernel-team



On 12/6/22 8:32 AM, Daan De Meyer wrote:
> This hook allows intercepting connect() calls on unix sockets. After
> running the hook, we recalculate the sockaddr length as it the hook

as the hook

> might have replaced the sun_path with a longer/shorter one. This is
> safe because all kernelspace sockaddrs are stored in instances of
> sockaddr_storage which is guaranteed to larger than sockaddr_un.
> 
> This hook can be used when users want to multiplex connect()
> calls to a single unix socket to multiple different processes
> behind the scenes by redirecting the connect() calls to process
> specific sockets.
> 
> Signed-off-by: Daan De Meyer <daan.j.demeyer@gmail.com>
> ---
>   Documentation/bpf/libbpf/program_types.rst    |  2 +
>   include/linux/bpf-cgroup-defs.h               |  1 +
>   include/linux/bpf-cgroup.h                    | 12 ++-
>   include/uapi/linux/bpf.h                      | 10 ++-
>   kernel/bpf/cgroup.c                           |  5 +-
>   kernel/bpf/syscall.c                          |  3 +
>   net/core/filter.c                             | 28 +++++++
>   net/unix/af_unix.c                            | 37 +++++++++
>   tools/bpf/bpftool/common.c                    |  1 +
>   tools/include/uapi/linux/bpf.h                | 10 ++-
>   tools/lib/bpf/libbpf.c                        |  2 +
>   .../selftests/bpf/prog_tests/section_names.c  |  5 ++
>   .../selftests/bpf/progs/connectun_prog.c      | 27 ++++++
>   tools/testing/selftests/bpf/test_sock_addr.c  | 83 +++++++++++++++++--
>   14 files changed, 206 insertions(+), 20 deletions(-)
>   create mode 100644 tools/testing/selftests/bpf/progs/connectun_prog.c

Please break this into multiple patches for easier review:
patch 1: core change
 >   include/linux/bpf-cgroup-defs.h               |  1 +
 >   include/linux/bpf-cgroup.h                    | 12 ++-
 >   include/uapi/linux/bpf.h                      | 10 ++-
 >   kernel/bpf/cgroup.c                           |  5 +-
 >   kernel/bpf/syscall.c                          |  3 +
 >   net/core/filter.c                             | 28 +++++++
 >   net/unix/af_unix.c
 >   tools/include/uapi/linux/bpf.h                | 10 ++-
patch 2: libbpf change
 >   tools/lib/bpf/libbpf.c                        |  2 +
patch 3: bpftool change
 >   tools/bpf/bpftool/common.c                    |  1 +
patch 4: selftest change:
 >   .../selftests/bpf/prog_tests/section_names.c  |  5 ++
 >   .../selftests/bpf/progs/connectun_prog.c      | 27 ++++++
 >   tools/testing/selftests/bpf/test_sock_addr.c  | 83 +++++++++++++++++--
patch 5: doc change:
 >   Documentation/bpf/libbpf/program_types.rst    |  2 +

> 
> diff --git a/Documentation/bpf/libbpf/program_types.rst b/Documentation/bpf/libbpf/program_types.rst
> index ad4d4d5eecb0..7d0bcd883561 100644
> --- a/Documentation/bpf/libbpf/program_types.rst
> +++ b/Documentation/bpf/libbpf/program_types.rst
> @@ -56,6 +56,8 @@ described in more detail in the footnotes.
>   |                                           | ``BPF_CGROUP_UDP6_RECVMSG``            | ``cgroup/recvmsg6``              |           |
>   +                                           +----------------------------------------+----------------------------------+-----------+
>   |                                           | ``BPF_CGROUP_UDP6_SENDMSG``            | ``cgroup/sendmsg6``              |           |
> +|                                           +----------------------------------------+----------------------------------+-----------+
> +|                                           | ``BPF_CGROUP_UNIX_CONNECT``            | ``cgroup/connectun``             |           |
>   +-------------------------------------------+----------------------------------------+----------------------------------+-----------+
>   | ``BPF_PROG_TYPE_CGROUP_SOCK``             | ``BPF_CGROUP_INET4_POST_BIND``         | ``cgroup/post_bind4``            |           |
>   +                                           +----------------------------------------+----------------------------------+-----------+
[...]
> @@ -6353,6 +6354,7 @@ struct bpf_sock_addr {
>   	__u32 user_ip6[4];	/* Allows 1,2,4,8-byte read and 4,8-byte write.
>   				 * Stored in network byte order.
>   				 */
> +	char user_path[108];    /* Allows 1 byte read and write. */
>   	__u32 user_port;	/* Allows 1,2,4-byte read and 4-byte write.
>   				 * Stored in network byte order
>   				 */

Please put the new field at the end of struct. Otherwise, we have
uapi compatibility issue.

> diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
> index bf2fdb33fb31..f1d20f69b260 100644
> --- a/kernel/bpf/cgroup.c
> +++ b/kernel/bpf/cgroup.c
> @@ -1454,7 +1454,7 @@ EXPORT_SYMBOL(__cgroup_bpf_run_filter_sk);
>    * @flags: Pointer to u32 which contains higher bits of BPF program
>    *         return value (OR'ed together).
>    *
> - * socket is expected to be of type INET or INET6.
> + * socket is expected to be of type INET, INET6 or UNIX.
>    *
>    * This function will return %-EPERM if an attached program is found and
>    * returned value != 1 during execution. In all other cases, 0 is returned.
> @@ -1476,7 +1476,8 @@ int __cgroup_bpf_run_filter_sock_addr(struct sock *sk,
>   	/* Check socket family since not all sockets represent network
>   	 * endpoint (e.g. AF_UNIX).
>   	 */
> -	if (sk->sk_family != AF_INET && sk->sk_family != AF_INET6)
> +	if (sk->sk_family != AF_INET && sk->sk_family != AF_INET6 &&
> +		sk->sk_family != AF_UNIX)
>   		return 0;
>   
>   	if (!ctx.uaddr) {
[...]
> diff --git a/tools/testing/selftests/bpf/progs/connectun_prog.c b/tools/testing/selftests/bpf/progs/connectun_prog.c
> new file mode 100644
> index 000000000000..91097bd8df3c
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/connectun_prog.c
> @@ -0,0 +1,27 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2018 Facebook

/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */

> +
> +#include <string.h>
> +
> +#include <linux/stddef.h>
> +#include <linux/bpf.h>
> +#include <linux/if.h>
> +#include <errno.h>
> +
> +#include <bpf/bpf_helpers.h>
> +
> +#define DST_REWRITE_PATH	"/tmp/bpf_cgroup_unix_test_rewrite"
> +
> +SEC("cgroup/connectun")
> +int connect_un_prog(struct bpf_sock_addr *ctx)
> +{
> +	if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
> +		return 0;
> +
> +	/* Rewrite destination. */
> +	memcpy(ctx->user_path, DST_REWRITE_PATH, sizeof(DST_REWRITE_PATH));
> +
> +	return 1;
> +}
> +
[...]

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2022-12-07 17:37 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-12-06 16:32 [PATCH] bpf: Add BPF_CGROUP_UNIX_CONNECT attach type Daan De Meyer
2022-12-07 17:37 ` Yonghong Song

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.