bpf: Hooks for sys_sendmsg
Subsystems (9)
v3 cover bpf: Hooks for sys_sendmsg
v2 -> v3:
* place BPF logic under static key in udp_sendmsg, udpv6_sendmsg;
* rebase.
v1 -> v2:
* return ENOTSUPP if bpf_prog rewrote IPv6-only with IPv4-mapped IPv6;
* add test for IPv4-mapped IPv6 use-case;
* fix build for CONFIG_CGROUP_BPF=n;
* rebase.
This path set adds BPF hooks for sys_sendmsg similar to existing hooks for sys_bind and sys_connect.
Hooks allow to override source IP (including the case when it's set via cmsg(3)) and destination IP:port for unconnected UDP (slow path). TCP and connected UDP (fast path) are not affected. This makes UDP support
complete: connected UDP is handled by sys_connect hooks, unconnected by sys_sendmsg ones.
Similar to sys_connect hooks, sys_sendmsg ones can be used to make system calls such as sendmsg(2) and sendto(2) return EPERM.
Please see patch 0001 for more details.
Andrey Ignatov (5):
bpf: Hooks for sys_sendmsg
bpf: Sync bpf.h to tools/
libbpf: Support guessing sendmsg{4,6} progs selftests/bpf: Prepare test_sock_addr for extension selftests/bpf: Selftest for sys_sendmsg hooks
include/linux/bpf-cgroup.h | 23 +- include/linux/filter.h | 1 + include/uapi/linux/bpf.h | 8 + kernel/bpf/cgroup.c | 11 +- kernel/bpf/syscall.c | 8 + net/core/filter.c | 39 + net/ipv4/udp.c | 20 +- net/ipv6/udp.c | 24 + tools/include/uapi/linux/bpf.h | 8 + tools/lib/bpf/libbpf.c | 2 + tools/testing/selftests/bpf/Makefile | 2 +- tools/testing/selftests/bpf/sendmsg4_prog.c | 49 ++ tools/testing/selftests/bpf/sendmsg6_prog.c | 60 ++ tools/testing/selftests/bpf/test_sock_addr.c | 1155 +++++++++++++++++++++----- 14 files changed, 1214 insertions(+), 196 deletions(-) create mode 100644 tools/testing/selftests/bpf/sendmsg4_prog.c create mode 100644 tools/testing/selftests/bpf/sendmsg6_prog.c
Signature · 1 line
2.9.5
PATCH v3 1/5 bpf: Hooks for sys_sendmsg
In addition to already existing BPF hooks for sys_bind and sys_connect, the patch provides new hooks for sys_sendmsg.
It leverages existing BPF program type BPF_PROG_TYPE_CGROUP_SOCK_ADDR that provides access to socket itlself (properties like family, type, protocol) and user-passed struct sockaddr * so that BPF program can override destination IP and port for system calls such as sendto(2) or sendmsg(2) and/or assign source IP to the socket.
The hooks are implemented as two new attach types:BPF_CGROUP_UDP4_SENDMSG and BPF_CGROUP_UDP6_SENDMSG for UDPv4 and UDPv6 correspondingly.
UDPv4 and UDPv6 separate attach types for same reason as sys_bind and sys_connect hooks, i.e. to prevent reading from / writing to e.g. user_ip6 fields when user passes sockaddr_in since it'd be out-of-bound.
The difference with already existing hooks is sys_sendmsg are implemented only for unconnected UDP.
For TCP it doesn't make sense to change user-provided struct sockaddr * at sendto(2)/sendmsg(2) time since socket either was already connected and has source/destination set or wasn't connected and call to sendto(2)/sendmsg(2) would lead to ENOTCONN anyway.
Connected UDP is already handled by sys_connect hooks that can override source/destination at connect time and use fast-path later, i.e. these hooks don't affect UDP fast-path.
Rewriting source IP is implemented differently than that in sys_connect hooks. When sys_sendmsg is used with unconnected UDP it doesn't work to just bind socket to desired local IP address since source IP can be set on per-packet basis by using ancillary data (cmsg(3)). So no matter if socket is bound or not, source IP has to be rewritten on every call to sys_sendmsg.
To do so two new fields are added to UAPI struct bpf_sock_addr;
* msg_src_ip4 to set source IPv4 for UDPv4;
* msg_src_ip6 to set source IPv6 for UDPv6.
--- include/linux/bpf-cgroup.h | 23 +++++++++++++++++------ include/linux/filter.h | 1 + include/uapi/linux/bpf.h | 8 ++++++++ kernel/bpf/cgroup.c | 11 ++++++++++- kernel/bpf/syscall.c | 8 ++++++++ net/core/filter.c | 39 +++++++++++++++++++++++++++++++++++++++ net/ipv4/udp.c | 20 ++++++++++++++++++-- net/ipv6/udp.c | 24 ++++++++++++++++++++++++ 8 files changed, 125 insertions(+), 9 deletions(-)
include/linux/bpf-cgroup.h +17 -6
@@ -66,7 +66,8 @@ int __cgroup_bpf_run_filter_sk(struct sock *sk,int __cgroup_bpf_run_filter_sock_addr (struct sock *sk,struct sockaddr *uaddr, -enum bpf_attach_type type); +enum bpf_attach_type type, +void *t_ctx); int __cgroup_bpf_run_filter_sock_ops(struct sock *sk, struct bpf_sock_ops_kern *sock_ops,
@@ -120,16 +121,18 @@ int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor, ({ \int __ret =0 ; \if (cgroup_bpf_enabled) \ - __ret =__cgroup_bpf_run_filter_sock_addr (sk, uaddr, type); \ + __ret =__cgroup_bpf_run_filter_sock_addr (sk, uaddr, type, \ +NULL ); \ __ret; \ }) -#define BPF_CGROUP_RUN_SA_PROG_LOCK (sk, uaddr, type) \ +#define BPF_CGROUP_RUN_SA_PROG_LOCK (sk, uaddr, type, t_ctx) \ ({ \ int __ret = 0; \ if (cgroup_bpf_enabled) { \ lock_sock(sk); \ - __ret = __cgroup_bpf_run_filter_sock_addr(sk, uaddr, type); \ + __ret = __cgroup_bpf_run_filter_sock_addr(sk, uaddr, type, \ + t_ctx); \ release_sock(sk); \ } \ __ret; \
@@ -151,10 +154,16 @@ int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor, BPF_CGROUP_RUN_SA_PROG(sk, uaddr, BPF_CGROUP_INET6_CONNECT)#define BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK (sk, uaddr) \ - BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, BPF_CGROUP_INET4_CONNECT) + BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, BPF_CGROUP_INET4_CONNECT, NULL)#define BPF_CGROUP_RUN_PROG_INET6_CONNECT_LOCK (sk, uaddr) \ - BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, BPF_CGROUP_INET6_CONNECT) + BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, BPF_CGROUP_INET6_CONNECT, NULL) + +#define BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK (sk, uaddr, t_ctx) \ + BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, BPF_CGROUP_UDP4_SENDMSG, t_ctx) + +#define BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK (sk, uaddr, t_ctx) \ + BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, BPF_CGROUP_UDP6_SENDMSG, t_ctx)#define BPF_CGROUP_RUN_PROG_SOCK_OPS (sock_ops) \ ({ \
@@ -197,6 +206,8 @@ static inline int cgroup_bpf_inherit(struct cgroup *cgrp) { return 0; }#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_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_SOCK_OPS (sock_ops) ({ 0; })#define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP (type,major,minor,access) ({ 0; })
include/linux/filter.h +1 -0
@@ -1010,6 +1010,7 @@ struct bpf_sock_addr_kern {* only two (src and dst) are available at convert_ctx_access time */ u64 tmp_reg; +void *t_ctx;/* Attach type specific context. */ };struct bpf_sock_ops_kern {
include/uapi/linux/bpf.h +8 -0
@@ -160,6 +160,8 @@ enum bpf_attach_type {BPF_CGROUP_INET6_CONNECT ,BPF_CGROUP_INET4_POST_BIND ,BPF_CGROUP_INET6_POST_BIND , +BPF_CGROUP_UDP4_SENDMSG , +BPF_CGROUP_UDP6_SENDMSG , __MAX_BPF_ATTACH_TYPE };
@@ -2363,6 +2365,12 @@ struct bpf_sock_addr {__u32 family;/* Allows 4-byte read, but no write */ __u32 type;/* Allows 4-byte read, but no write */ __u32 protocol;/* Allows 4-byte read, but no write */ +__u32 msg_src_ip4;/* Allows 1,2,4-byte read an 4-byte write. +* Stored in network byte order. +*/ +__u32 msg_src_ip6[4 ];/* Allows 1,2,4-byte read an 4-byte write. +* Stored in network byte order. +*/ };/* User bpf_sock_ops struct to access socket values and specify request ops
kernel/bpf/cgroup.c +10 -1
@@ -500,6 +500,7 @@ EXPORT_SYMBOL(__cgroup_bpf_run_filter_sk);* @sk: sock struct that will use sockaddr * @uaddr: sockaddr struct provided by user * @type: The type of program to be exectuted +* @t_ctx: Pointer to attach type specific context * * socket is expected to be of type INET or INET6. *
@@ -508,12 +509,15 @@ EXPORT_SYMBOL(__cgroup_bpf_run_filter_sk);*/ int __cgroup_bpf_run_filter_sock_addr (struct sock *sk,struct sockaddr *uaddr, -enum bpf_attach_type type) +enum bpf_attach_type type, +void *t_ctx) {struct bpf_sock_addr_kern ctx = { .sk = sk, .uaddr = uaddr, + .t_ctx = t_ctx, }; +struct sockaddr_storage unspec;struct cgroup *cgrp;int ret;
@@ -523,6 +527,11 @@ int __cgroup_bpf_run_filter_sock_addr(struct sock *sk,if (sk->sk_family !=AF_INET && sk->sk_family !=AF_INET6 )return 0 ; +if (!ctx.uaddr) { +memset (&unspec,0 ,sizeof (unspec)); + ctx.uaddr = (struct sockaddr *)&unspec; + } + cgrp =sock_cgroup_ptr (&sk->sk_cgrp_data); ret =BPF_PROG_RUN_ARRAY (cgrp->bpf.effective[type], &ctx,BPF_PROG_RUN );
kernel/bpf/syscall.c +8 -0
@@ -1249,6 +1249,8 @@ bpf_prog_load_check_attach_type(enum bpf_prog_type prog_type,case BPF_CGROUP_INET6_BIND :case BPF_CGROUP_INET4_CONNECT :case BPF_CGROUP_INET6_CONNECT : +case BPF_CGROUP_UDP4_SENDMSG : +case BPF_CGROUP_UDP6_SENDMSG :return 0 ;default :return -EINVAL ;
@@ -1565,6 +1567,8 @@ static int bpf_prog_attach(const union bpf_attr *attr)case BPF_CGROUP_INET6_BIND :case BPF_CGROUP_INET4_CONNECT :case BPF_CGROUP_INET6_CONNECT : +case BPF_CGROUP_UDP4_SENDMSG : +case BPF_CGROUP_UDP6_SENDMSG : ptype =BPF_PROG_TYPE_CGROUP_SOCK_ADDR ;break ;case BPF_CGROUP_SOCK_OPS :
@@ -1635,6 +1639,8 @@ static int bpf_prog_detach(const union bpf_attr *attr)case BPF_CGROUP_INET6_BIND :case BPF_CGROUP_INET4_CONNECT :case BPF_CGROUP_INET6_CONNECT : +case BPF_CGROUP_UDP4_SENDMSG : +case BPF_CGROUP_UDP6_SENDMSG : ptype =BPF_PROG_TYPE_CGROUP_SOCK_ADDR ;break ;case BPF_CGROUP_SOCK_OPS :
@@ -1692,6 +1698,8 @@ 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_UDP4_SENDMSG : +case BPF_CGROUP_UDP6_SENDMSG :case BPF_CGROUP_SOCK_OPS :case BPF_CGROUP_DEVICE :break ;
net/core/filter.c +39 -0
@@ -5299,6 +5299,7 @@ static bool sock_addr_is_valid_access(int off, int size,switch (prog->expected_attach_type) {case BPF_CGROUP_INET4_BIND :case BPF_CGROUP_INET4_CONNECT : +case BPF_CGROUP_UDP4_SENDMSG :break ;default :return false;
@@ -5308,6 +5309,24 @@ static bool sock_addr_is_valid_access(int off, int size,switch (prog->expected_attach_type) {case BPF_CGROUP_INET6_BIND :case BPF_CGROUP_INET6_CONNECT : +case BPF_CGROUP_UDP6_SENDMSG : +break ; +default : +return false; + } +break ; + case bpf_ctx_range(struct bpf_sock_addr, msg_src_ip4): +switch (prog->expected_attach_type) { +case BPF_CGROUP_UDP4_SENDMSG : +break ; +default : +return false; + } +break ; + case bpf_ctx_range_till(struct bpf_sock_addr, msg_src_ip6[0], + msg_src_ip6[3 ]): +switch (prog->expected_attach_type) { +case BPF_CGROUP_UDP6_SENDMSG :break ;default :return false;
@@ -5318,6 +5337,9 @@ static bool sock_addr_is_valid_access(int off, int size,switch (off) { case bpf_ctx_range(struct bpf_sock_addr, user_ip4): case bpf_ctx_range_till(struct bpf_sock_addr, user_ip6[0], user_ip6[3]): + case bpf_ctx_range(struct bpf_sock_addr, msg_src_ip4): + case bpf_ctx_range_till(struct bpf_sock_addr, msg_src_ip6[0], + msg_src_ip6[3 ]):/* Only narrow read access allowed for now. */ if (type ==BPF_READ ) {bpf_ctx_record_field_size (info, size_default);
@@ -6072,6 +6094,23 @@ static u32 sock_addr_convert_ctx_access(enum bpf_access_type type, *insn++ =BPF_ALU32_IMM (BPF_RSH , si->dst_reg,SK_FL_PROTO_SHIFT );break ; + +case offsetof(struct bpf_sock_addr , msg_src_ip4): +/* Treat t_ctx as struct in_addr for msg_src_ip4. */ + SOCK_ADDR_LOAD_OR_STORE_NESTED_FIELD_SIZE_OFF( + struct bpf_sock_addr_kern, struct in_addr, t_ctx, + s_addr, BPF_SIZE(si->code), 0, tmp_reg); +break ; + + case bpf_ctx_range_till(struct bpf_sock_addr, msg_src_ip6[0], + msg_src_ip6[3 ]): + off = si->off; + off -= offsetof(struct bpf_sock_addr, msg_src_ip6[0]); +/* Treat t_ctx as struct in6_addr for msg_src_ip6. */ + SOCK_ADDR_LOAD_OR_STORE_NESTED_FIELD_SIZE_OFF( + struct bpf_sock_addr_kern, struct in6_addr, t_ctx, + s6_addr32[0], BPF_SIZE(si->code), off, tmp_reg); +break ; }return insn - insn_buf;
net/ipv4/udp.c +18 -2
@@ -901,6 +901,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) {struct inet_sock *inet =inet_sk (sk);struct udp_sock *up =udp_sk (sk); + DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name);struct flowi4 fl4_stack;struct flowi4 *fl4;int ulen = len;
@@ -955,8 +956,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)/* * Get and verify the address. */ -if (msg->msg_name) { - DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name); +if (usin) {if (msg->msg_namelen <sizeof (*usin))return -EINVAL ;if (usin->sin_family !=AF_INET ) {
@@ -1010,6 +1010,22 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)rcu_read_unlock (); } +if (cgroup_bpf_enabled && !connected) { + err =BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK (sk, + (struct sockaddr *)usin, &ipc.addr); +if (err) + goto out_free; +if (usin) { +if (usin->sin_port ==0 ) { +/* BPF program set invalid port. Reject it. */ + err = -EINVAL ; + goto out_free; + } + daddr = usin->sin_addr.s_addr; + dport = usin->sin_port; + } + } + saddr = ipc.addr; ipc.addr = faddr = daddr;
net/ipv6/udp.c +24 -0
@@ -1316,6 +1316,29 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) fl6.saddr = np->saddr; fl6.fl6_sport = inet->inet_sport; +if (cgroup_bpf_enabled && !connected) { + err =BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK (sk, + (struct sockaddr *)sin6, &fl6.saddr); +if (err) + goto out_no_dst; +if (sin6) { +if (ipv6_addr_v4mapped (&sin6->sin6_addr)) { +/* BPF program rewrote IPv6-only by IPv4-mapped +* IPv6. It's currently unsupported. +*/ + err = -ENOTSUPP ; + goto out_no_dst; + } +if (sin6->sin6_port ==0 ) { +/* BPF program set invalid port. Reject it. */ + err = -EINVAL ; + goto out_no_dst; + } + fl6.fl6_dport = sin6->sin6_port; + fl6.daddr = sin6->sin6_addr; + } + } + final_p =fl6_update_dst (&fl6, opt, &final);if (final_p) connected = false;
@@ -1395,6 +1418,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) out:dst_release (dst); +out_no_dst:fl6_sock_release (flowlabel);txopt_put (opt_to_free);if (!err)
Signature · 1 line
2.9.5
PATCH v3 2/5 bpf: Sync bpf.h to tools/
In reply to Andrey IgnatovSync new BPF_CGROUP_UDP4_SENDMSG and BPF_CGROUP_UDP6_SENDMSG attach types to tools/.
--- tools/include/uapi/linux/bpf.h | 8 ++++++++ 1 file changed, 8 insertions(+)
tools/include/uapi/linux/bpf.h +8 -0
@@ -160,6 +160,8 @@ enum bpf_attach_type {BPF_CGROUP_INET6_CONNECT ,BPF_CGROUP_INET4_POST_BIND ,BPF_CGROUP_INET6_POST_BIND , +BPF_CGROUP_UDP4_SENDMSG , +BPF_CGROUP_UDP6_SENDMSG , __MAX_BPF_ATTACH_TYPE };
@@ -2363,6 +2365,12 @@ struct bpf_sock_addr {__u32 family;/* Allows 4-byte read, but no write */ __u32 type;/* Allows 4-byte read, but no write */ __u32 protocol;/* Allows 4-byte read, but no write */ +__u32 msg_src_ip4;/* Allows 1,2,4-byte read an 4-byte write. +* Stored in network byte order. +*/ +__u32 msg_src_ip6[4 ];/* Allows 1,2,4-byte read an 4-byte write. +* Stored in network byte order. +*/ };/* User bpf_sock_ops struct to access socket values and specify request ops
Signature · 1 line
2.9.5
PATCH v3 3/5 libbpf: Support guessing sendmsg{4,6} progs
In reply to Andrey Ignatovlibbpf can guess prog type and expected attach type based on section name. Add hints for "cgroup/sendmsg4" and "cgroup/sendmsg6" section names.
--- tools/lib/bpf/libbpf.c | 2 ++ 1 file changed, 2 insertions(+)
tools/lib/bpf/libbpf.c +2 -0
@@ -2043,6 +2043,8 @@ static const struct {BPF_SA_PROG_SEC ("cgroup/bind6" ,BPF_CGROUP_INET6_BIND ),BPF_SA_PROG_SEC ("cgroup/connect4" ,BPF_CGROUP_INET4_CONNECT ),BPF_SA_PROG_SEC ("cgroup/connect6" ,BPF_CGROUP_INET6_CONNECT ), +BPF_SA_PROG_SEC ("cgroup/sendmsg4" ,BPF_CGROUP_UDP4_SENDMSG ), +BPF_SA_PROG_SEC ("cgroup/sendmsg6" ,BPF_CGROUP_UDP6_SENDMSG ),BPF_S_PROG_SEC ("cgroup/post_bind4" ,BPF_CGROUP_INET4_POST_BIND ), BPF_S_PROG_SEC("cgroup/post_bind6", BPF_CGROUP_INET6_POST_BIND), };
Signature · 1 line
2.9.5
PATCH v3 4/5 selftests/bpf: Prepare test_sock_addr for extension
In reply to Andrey Ignatovtest_sock_addr was not easy to extend since it was focused on sys_bind and sys_connect quite a bit.
Reorganized it so that it'll be easier to cover new test-cases for BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
- decouple test-cases so that only one BPF prog is tested at a time;
- check programmatically that local IP:port for sys_bind, source IP and destination IP:port for sys_connect are rewritten property by tested BPF programs.
The output of new version:
# test_sock_addr.sh 2>/dev/null Wait for testing IPv4/IPv6 to become available ... OK Test case: bind4: load prog with wrong expected attach type .. [PASS] Test case: bind4: attach prog with wrong attach type .. [PASS] Test case: bind4: rewrite IP & TCP port in .. [PASS] Test case: bind4: rewrite IP & UDP port in .. [PASS] Test case: bind6: load prog with wrong expected attach type .. [PASS] Test case: bind6: attach prog with wrong attach type .. [PASS] Test case: bind6: rewrite IP & TCP port in .. [PASS] Test case: bind6: rewrite IP & UDP port in .. [PASS] Test case: connect4: load prog with wrong expected attach type .. [PASS] Test case: connect4: attach prog with wrong attach type .. [PASS] Test case: connect4: rewrite IP & TCP port .. [PASS] Test case: connect4: rewrite IP & UDP port .. [PASS] Test case: connect6: load prog with wrong expected attach type .. [PASS] Test case: connect6: attach prog with wrong attach type .. [PASS] Test case: connect6: rewrite IP & TCP port .. [PASS] Test case: connect6: rewrite IP & UDP port .. [PASS] Summary: 16 PASSED, 0 FAILED
(stderr contains errors from libbpf when testing load/attach with invalid arguments)
--- tools/testing/selftests/bpf/test_sock_addr.c | 655 +++++++++++++++++++-------- 1 file changed, 460 insertions(+), 195 deletions(-)
tools/testing/selftests/bpf/test_sock_addr.c +460 -195
@@ -17,34 +17,292 @@#include "cgroup_helpers.h" #include "bpf_rlimit.h" +#ifndef ARRAY_SIZE +# define ARRAY_SIZE (x) (sizeof(x) / sizeof((x)[0])) +#endif +#define CG_PATH "/foo"#define CONNECT4_PROG_PATH "./connect4_prog.o"#define CONNECT6_PROG_PATH "./connect6_prog.o"#define SERV4_IP "192.168.1.254"#define SERV4_REWRITE_IP "127.0.0.1" +#define SRC4_REWRITE_IP "127.0.0.4"#define SERV4_PORT 4040#define SERV4_REWRITE_PORT 4444#define SERV6_IP "face:b00c:1234:5678::abcd"#define SERV6_REWRITE_IP "::1" +#define SRC6_REWRITE_IP "::6"#define SERV6_PORT 6060#define SERV6_REWRITE_PORT 6666#define INET_NTOP_BUF 40 -typedef int (*load_fn )(enum bpf_attach_type ,const char *comment); +struct sock_addr_test ; + +typedef int (*load_fn )(const struct sock_addr_test *test);typedef int (*info_fn )(int ,struct sockaddr *,socklen_t *); -struct program { -enum bpf_attach_type type; -load_fn loadfn; -int fd; -const char *name; -enum bpf_attach_type invalid_type; +char bpf_log_buf[BPF_LOG_BUF_SIZE ]; + +struct sock_addr_test { +const char *descr; +/* BPF prog properties */ +load_fn loadfn; +enum bpf_attach_type expected_attach_type; +enum bpf_attach_type attach_type; +/* Socket properties */ +int domain; +int type; +/* IP:port pairs for BPF prog to override */ +const char *requested_ip; +unsigned short requested_port; +const char *expected_ip; +unsigned short expected_port; +const char *expected_src_ip; +/* Expected test result */ +enum { +LOAD_REJECT , +ATTACH_REJECT , +SUCCESS , + } expected_result; }; -char bpf_log_buf[BPF_LOG_BUF_SIZE ]; +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 struct sock_addr_test tests[] = { +/* bind */ + { +"bind4: load prog with wrong expected attach type" , + bind4_prog_load, +BPF_CGROUP_INET6_BIND , +BPF_CGROUP_INET4_BIND , +AF_INET , +SOCK_STREAM , +NULL , +0 , +NULL , +0 , +NULL , +LOAD_REJECT , + }, + { +"bind4: attach prog with wrong attach type" , + bind4_prog_load, +BPF_CGROUP_INET4_BIND , +BPF_CGROUP_INET6_BIND , +AF_INET , +SOCK_STREAM , +NULL , +0 , +NULL , +0 , +NULL , +ATTACH_REJECT , + }, + { +"bind4: rewrite IP & TCP port in" , + bind4_prog_load, +BPF_CGROUP_INET4_BIND , +BPF_CGROUP_INET4_BIND , +AF_INET , +SOCK_STREAM , +SERV4_IP , +SERV4_PORT , +SERV4_REWRITE_IP , +SERV4_REWRITE_PORT , +NULL , +SUCCESS , + }, + { +"bind4: rewrite IP & UDP port in" , + bind4_prog_load, +BPF_CGROUP_INET4_BIND , +BPF_CGROUP_INET4_BIND , +AF_INET , +SOCK_DGRAM , +SERV4_IP , +SERV4_PORT , +SERV4_REWRITE_IP , +SERV4_REWRITE_PORT , +NULL , +SUCCESS , + }, + { +"bind6: load prog with wrong expected attach type" , + bind6_prog_load, +BPF_CGROUP_INET4_BIND , +BPF_CGROUP_INET6_BIND , +AF_INET6 , +SOCK_STREAM , +NULL , +0 , +NULL , +0 , +NULL , +LOAD_REJECT , + }, + { +"bind6: attach prog with wrong attach type" , + bind6_prog_load, +BPF_CGROUP_INET6_BIND , +BPF_CGROUP_INET4_BIND , +AF_INET , +SOCK_STREAM , +NULL , +0 , +NULL , +0 , +NULL , +ATTACH_REJECT , + }, + { +"bind6: rewrite IP & TCP port in" , + bind6_prog_load, +BPF_CGROUP_INET6_BIND , +BPF_CGROUP_INET6_BIND , +AF_INET6 , +SOCK_STREAM , +SERV6_IP , +SERV6_PORT , +SERV6_REWRITE_IP , +SERV6_REWRITE_PORT , +NULL , +SUCCESS , + }, + { +"bind6: rewrite IP & UDP port in" , + bind6_prog_load, +BPF_CGROUP_INET6_BIND , +BPF_CGROUP_INET6_BIND , +AF_INET6 , +SOCK_DGRAM , +SERV6_IP , +SERV6_PORT , +SERV6_REWRITE_IP , +SERV6_REWRITE_PORT , +NULL , +SUCCESS , + }, + +/* connect */ + { +"connect4: load prog with wrong expected attach type" , + connect4_prog_load, +BPF_CGROUP_INET6_CONNECT , +BPF_CGROUP_INET4_CONNECT , +AF_INET , +SOCK_STREAM , +NULL , +0 , +NULL , +0 , +NULL , +LOAD_REJECT , + }, + { +"connect4: attach prog with wrong attach type" , + connect4_prog_load, +BPF_CGROUP_INET4_CONNECT , +BPF_CGROUP_INET6_CONNECT , +AF_INET , +SOCK_STREAM , +NULL , +0 , +NULL , +0 , +NULL , +ATTACH_REJECT , + }, + { +"connect4: rewrite IP & TCP port" , + connect4_prog_load, +BPF_CGROUP_INET4_CONNECT , +BPF_CGROUP_INET4_CONNECT , +AF_INET , +SOCK_STREAM , +SERV4_IP , +SERV4_PORT , +SERV4_REWRITE_IP , +SERV4_REWRITE_PORT , +SRC4_REWRITE_IP , +SUCCESS , + }, + { +"connect4: rewrite IP & UDP port" , + connect4_prog_load, +BPF_CGROUP_INET4_CONNECT , +BPF_CGROUP_INET4_CONNECT , +AF_INET , +SOCK_DGRAM , +SERV4_IP , +SERV4_PORT , +SERV4_REWRITE_IP , +SERV4_REWRITE_PORT , +SRC4_REWRITE_IP , +SUCCESS , + }, + { +"connect6: load prog with wrong expected attach type" , + connect6_prog_load, +BPF_CGROUP_INET4_CONNECT , +BPF_CGROUP_INET6_CONNECT , +AF_INET6 , +SOCK_STREAM , +NULL , +0 , +NULL , +0 , +NULL , +LOAD_REJECT , + }, + { +"connect6: attach prog with wrong attach type" , + connect6_prog_load, +BPF_CGROUP_INET6_CONNECT , +BPF_CGROUP_INET4_CONNECT , +AF_INET , +SOCK_STREAM , +NULL , +0 , +NULL , +0 , +NULL , +ATTACH_REJECT , + }, + { +"connect6: rewrite IP & TCP port" , + connect6_prog_load, +BPF_CGROUP_INET6_CONNECT , +BPF_CGROUP_INET6_CONNECT , +AF_INET6 , +SOCK_STREAM , +SERV6_IP , +SERV6_PORT , +SERV6_REWRITE_IP , +SERV6_REWRITE_PORT , +SRC6_REWRITE_IP , +SUCCESS , + }, + { +"connect6: rewrite IP & UDP port" , + connect6_prog_load, +BPF_CGROUP_INET6_CONNECT , +BPF_CGROUP_INET6_CONNECT , +AF_INET6 , +SOCK_DGRAM , +SERV6_IP , +SERV6_PORT , +SERV6_REWRITE_IP , +SERV6_REWRITE_PORT , +SRC6_REWRITE_IP , +SUCCESS , + }, +};static int mk_sockaddr (int domain,const char *ip,unsigned short port,struct sockaddr *addr,socklen_t addr_len)
@@ -84,25 +342,23 @@ static int mk_sockaddr(int domain, const char *ip, unsigned short port,return 0 ; } -static int load_insns (enum bpf_attach_type attach_type, -const struct bpf_insn *insns,size_t insns_cnt, -const char *comment) +static int load_insns (const struct sock_addr_test *test, +const struct bpf_insn *insns,size_t insns_cnt) {struct bpf_load_program_attr load_attr;int ret;memset (&load_attr,0 ,sizeof (struct bpf_load_program_attr )); load_attr.prog_type =BPF_PROG_TYPE_CGROUP_SOCK_ADDR ; - load_attr.expected_attach_type = attach_type; + load_attr.expected_attach_type = test->expected_attach_type; load_attr.insns = insns; load_attr.insns_cnt = insns_cnt; load_attr.license ="GPL" ; ret =bpf_load_program_xattr (&load_attr, bpf_log_buf,BPF_LOG_BUF_SIZE ); -if (ret <0 && comment) { -log_err (">>> Loading %s program error.\n" -">>> Output from verifier:\n%s\n-------\n" , - comment, bpf_log_buf); +if (ret <0 && test->expected_result !=LOAD_REJECT ) { +log_err (">>> Loading program error.\n" +">>> Verifier output:\n%s\n-------\n" , bpf_log_buf); }return ret;
@@ -119,8 +375,7 @@ static int load_insns(enum bpf_attach_type attach_type,* to count jumps properly. */ -static int bind4_prog_load (enum bpf_attach_type attach_type, -const char *comment) +static int bind4_prog_load (const struct sock_addr_test *test) {union {uint8_t u4_addr8[4 ];
@@ -186,12 +441,10 @@ static int bind4_prog_load(enum bpf_attach_type attach_type, BPF_EXIT_INSN(), }; -return load_insns (attach_type, insns, -sizeof (insns) /sizeof (struct bpf_insn ), comment); +return load_insns (test, insns,sizeof (insns) /sizeof (struct bpf_insn )); } -static int bind6_prog_load (enum bpf_attach_type attach_type, -const char *comment) +static int bind6_prog_load (const struct sock_addr_test *test) {struct sockaddr_in6 addr6_rw;struct in6_addr ip6;
@@ -254,13 +507,10 @@ static int bind6_prog_load(enum bpf_attach_type attach_type, BPF_EXIT_INSN(), }; -return load_insns (attach_type, insns, -sizeof (insns) /sizeof (struct bpf_insn ), comment); +return load_insns (test, insns,sizeof (insns) /sizeof (struct bpf_insn )); } -static int connect_prog_load_path (const char *path, -enum bpf_attach_type attach_type, -const char *comment) +static int load_path (const struct sock_addr_test *test,const char *path) {struct bpf_prog_load_attr attr;struct bpf_object *obj;
@@ -269,75 +519,83 @@ static int connect_prog_load_path(const char *path,memset (&attr,0 ,sizeof (struct bpf_prog_load_attr )); attr.file = path; attr.prog_type =BPF_PROG_TYPE_CGROUP_SOCK_ADDR ; - attr.expected_attach_type = attach_type; + attr.expected_attach_type = test->expected_attach_type;if (bpf_prog_load_xattr (&attr, &obj, &prog_fd)) { -if (comment) -log_err (">>> Loading %s program at %s error.\n" , - comment, path); +if (test->expected_result !=LOAD_REJECT ) +log_err (">>> Loading program (%s) error.\n" , path);return -1 ; }return prog_fd; } -static int connect4_prog_load (enum bpf_attach_type attach_type, -const char *comment) +static int connect4_prog_load (const struct sock_addr_test *test) { -return connect_prog_load_path (CONNECT4_PROG_PATH , attach_type, comment); +return load_path (test,CONNECT4_PROG_PATH ); } -static int connect6_prog_load (enum bpf_attach_type attach_type, -const char *comment) +static int connect6_prog_load (const struct sock_addr_test *test) { -return connect_prog_load_path (CONNECT6_PROG_PATH , attach_type, comment); +return load_path (test,CONNECT6_PROG_PATH ); } -static void print_ip_port (int sockfd,info_fn fn,const char *fmt) +static int cmp_addr (const struct sockaddr_storage *addr1, +const struct sockaddr_storage *addr2,int cmp_port) { -char addr_buf[INET_NTOP_BUF ]; -struct sockaddr_storage addr; -struct sockaddr_in6 *addr6; -struct sockaddr_in *addr4; -socklen_t addr_len; -unsigned short port; -void *nip; - - addr_len =sizeof (struct sockaddr_storage ); -memset (&addr,0 , addr_len); - -if (fn (sockfd, (struct sockaddr *)&addr, (socklen_t *)&addr_len) ==0 ) { -if (addr.ss_family ==AF_INET ) { - addr4 = (struct sockaddr_in *)&addr; - nip = (void *)&addr4->sin_addr; - port =ntohs (addr4->sin_port); - }else if (addr.ss_family ==AF_INET6 ) { - addr6 = (struct sockaddr_in6 *)&addr; - nip = (void *)&addr6->sin6_addr; - port =ntohs (addr6->sin6_port); - }else { -return ; - } -const char *addr_str = -inet_ntop (addr.ss_family, nip, addr_buf,INET_NTOP_BUF ); -printf (fmt, addr_str ? addr_str :"??" , port); +const struct sockaddr_in *four1, *four2; +const struct sockaddr_in6 *six1, *six2; + +if (addr1->ss_family != addr2->ss_family) +return -1 ; + +if (addr1->ss_family ==AF_INET ) { + four1 = (const struct sockaddr_in *)addr1; + four2 = (const struct sockaddr_in *)addr2; +return !((four1->sin_port == four2->sin_port || !cmp_port) && + four1->sin_addr.s_addr == four2->sin_addr.s_addr); + }else if (addr1->ss_family ==AF_INET6 ) { + six1 = (const struct sockaddr_in6 *)addr1; + six2 = (const struct sockaddr_in6 *)addr2; +return !((six1->sin6_port == six2->sin6_port || !cmp_port) && + !memcmp (&six1->sin6_addr, &six2->sin6_addr, +sizeof (struct in6_addr ))); } + +return -1 ; +} + +static int cmp_sock_addr (info_fn fn,int sock1, +const struct sockaddr_storage *addr2,int cmp_port) +{ +struct sockaddr_storage addr1; +socklen_t len1 =sizeof (addr1); + +memset (&addr1,0 , len1); +if (fn (sock1, (struct sockaddr *)&addr1, (socklen_t *)&len1) !=0 ) +return -1 ; + +return cmp_addr (&addr1, addr2, cmp_port); +} + +static int cmp_local_ip (int sock1,const struct sockaddr_storage *addr2) +{ +return cmp_sock_addr (getsockname, sock1, addr2,/*cmp_port*/ 0 ); } -static void print_local_ip_port (int sockfd,const char *fmt) +static int cmp_local_addr (int sock1,const struct sockaddr_storage *addr2) { -print_ip_port (sockfd, getsockname, fmt); +return cmp_sock_addr (getsockname, sock1, addr2,/*cmp_port*/ 1 ); } -static void print_remote_ip_port (int sockfd,const char *fmt) +static int cmp_peer_addr (int sock1,const struct sockaddr_storage *addr2) { -print_ip_port (sockfd, getpeername, fmt); +return cmp_sock_addr (getpeername, sock1, addr2,/*cmp_port*/ 1 ); }static int start_server (int type,const struct sockaddr_storage *addr,socklen_t addr_len) { -int fd; fd =socket (addr->ss_family, type,0 );
@@ -358,8 +616,6 @@ static int start_server(int type, const struct sockaddr_storage *addr, } } -print_local_ip_port (fd,"\t Actual: bind(%s, %d)\n" ); - goto out; close_out:close (fd);
@@ -372,19 +628,19 @@ static int connect_to_server(int type, const struct sockaddr_storage *addr, socklen_t addr_len) {int domain; -int fd; +int fd =-1 ; domain = addr->ss_family;if (domain !=AF_INET && domain !=AF_INET6 ) {log_err ("Unsupported address family" ); -return -1 ; + goto err; } fd =socket (domain, type,0 );if (fd ==-1 ) { -log_err ("Failed to creating client socket" ); -return -1 ; +log_err ("Failed to create client socket" ); + goto err; }if (connect (fd, (const struct sockaddr *)addr, addr_len) ==-1 ) {
@@ -392,162 +648,188 @@ static int connect_to_server(int type, const struct sockaddr_storage *addr, goto err; } -print_remote_ip_port (fd,"\t Actual: connect(%s, %d)" ); -print_local_ip_port (fd," from (%s, %d)\n" ); - -return 0 ; + goto out; err:close (fd); -return -1 ; + fd =-1 ; +out: +return fd; } -static void print_test_case_num (int domain,int type) +static int init_addrs (const struct sock_addr_test *test, +struct sockaddr_storage *requested_addr, +struct sockaddr_storage *expected_addr, +struct sockaddr_storage *expected_src_addr) { -static int test_num; - -printf ("Test case #%d (%s/%s):\n" , ++test_num, - (domain ==AF_INET ?"IPv4" : - domain ==AF_INET6 ?"IPv6" : -"unknown_domain" ), - (type ==SOCK_STREAM ?"TCP" : - type ==SOCK_DGRAM ?"UDP" : -"unknown_type" )); +socklen_t addr_len =sizeof (struct sockaddr_storage ); + +if (mk_sockaddr (test->domain, test->expected_ip, test->expected_port, + (struct sockaddr *)expected_addr, addr_len) ==-1 ) + goto err; + +if (mk_sockaddr (test->domain, test->requested_ip, test->requested_port, + (struct sockaddr *)requested_addr, addr_len) ==-1 ) + goto err; + +if (test->expected_src_ip && +mk_sockaddr (test->domain, test->expected_src_ip,0 , + (struct sockaddr *)expected_src_addr, addr_len) ==-1 ) + goto err; + +return 0 ; +err: +return -1 ; } -static int run_test_case (int domain,int type,const char *ip, -unsigned short port) +static int run_bind_test_case (const struct sock_addr_test *test) { -struct sockaddr_storage addr; -socklen_t addr_len =sizeof (addr); +socklen_t addr_len =sizeof (struct sockaddr_storage ); +struct sockaddr_storage requested_addr; +struct sockaddr_storage expected_addr; +int clientfd =-1 ;int servfd =-1 ;int err =0 ; -print_test_case_num (domain, type); - -if (mk_sockaddr (domain, ip, port, (struct sockaddr *)&addr, - addr_len) ==-1 ) -return -1 ; +if (init_addrs (test, &requested_addr, &expected_addr,NULL )) + goto err; -printf ("\tRequested: bind(%s, %d) ..\n" , ip, port); - servfd =start_server (type, &addr, addr_len); + servfd =start_server (test->type, &requested_addr, addr_len);if (servfd ==-1 ) goto err; -printf ("\tRequested: connect(%s, %d) from (*, *) ..\n" , ip, port); -if (connect_to_server (type, &addr, addr_len)) +if (cmp_local_addr (servfd, &expected_addr)) + goto err; + +/* Try to connect to server just in case */ + clientfd =connect_to_server (test->type, &expected_addr, addr_len); +if (clientfd ==-1 ) goto err; goto out; err: err =-1 ; out: +close (clientfd);close (servfd);return err; } -static void close_progs_fds (struct program *progs,size_t prog_cnt) +static int run_connect_test_case (const struct sock_addr_test *test) { -size_t i; +socklen_t addr_len =sizeof (struct sockaddr_storage ); +struct sockaddr_storage expected_src_addr; +struct sockaddr_storage requested_addr; +struct sockaddr_storage expected_addr; +int clientfd =-1 ; +int servfd =-1 ; +int err =0 ; -for (i =0 ; i < prog_cnt; ++i) { -close (progs[i].fd); - progs[i].fd =-1 ; - } -} +if (init_addrs (test, &requested_addr, &expected_addr, + &expected_src_addr)) + goto err; -static int load_and_attach_progs (int cgfd,struct program *progs, -size_t prog_cnt) -{ -size_t i; - -for (i =0 ; i < prog_cnt; ++i) { -printf ("Load %s with invalid type (can pollute stderr) " , - progs[i].name); -fflush (stdout); - progs[i].fd = progs[i].loadfn (progs[i].invalid_type,NULL ); -if (progs[i].fd !=-1 ) { -log_err ("Load with invalid type accepted for %s" , - progs[i].name); - goto err; - } -printf ("... REJECTED\n" ); +/* Prepare server to connect to */ + servfd =start_server (test->type, &expected_addr, addr_len); +if (servfd ==-1 ) + goto err; -printf ("Load %s with valid type" , progs[i].name); - progs[i].fd = progs[i].loadfn (progs[i].type, progs[i].name); -if (progs[i].fd ==-1 ) { -log_err ("Failed to load program %s" , progs[i].name); - goto err; - } -printf (" ... OK\n" ); - -printf ("Attach %s with invalid type" , progs[i].name); -if (bpf_prog_attach (progs[i].fd, cgfd, progs[i].invalid_type, -BPF_F_ALLOW_OVERRIDE ) !=-1 ) { -log_err ("Attach with invalid type accepted for %s" , - progs[i].name); - goto err; - } -printf (" ... REJECTED\n" ); + clientfd =connect_to_server (test->type, &requested_addr, addr_len); +if (clientfd ==-1 ) + goto err; -printf ("Attach %s with valid type" , progs[i].name); -if (bpf_prog_attach (progs[i].fd, cgfd, progs[i].type, -BPF_F_ALLOW_OVERRIDE ) ==-1 ) { -log_err ("Failed to attach program %s" , progs[i].name); - goto err; - } -printf (" ... OK\n" ); - } +/* Make sure src and dst addrs were overridden properly */ +if (cmp_peer_addr (clientfd, &expected_addr)) + goto err; -return 0 ; +if (cmp_local_ip (clientfd, &expected_src_addr)) + goto err; + + goto out; err: -close_progs_fds (progs, prog_cnt); -return -1 ; + err =-1 ; +out: +close (clientfd); +close (servfd); +return err; } -static int run_domain_test (int domain,int cgfd,struct program *progs, -size_t prog_cnt,const char *ip,unsigned short port) +static int run_test_case (int cgfd,const struct sock_addr_test *test) { +int progfd =-1 ;int err =0 ; -if (load_and_attach_progs (cgfd, progs, prog_cnt) ==-1 ) +printf ("Test case: %s .. " , test->descr); + + progfd = test->loadfn (test); +if (test->expected_result ==LOAD_REJECT && progfd <0 ) + goto out; +else if (test->expected_result ==LOAD_REJECT || progfd <0 ) + goto err; + + err =bpf_prog_attach (progfd, cgfd, test->attach_type, +BPF_F_ALLOW_OVERRIDE ); +if (test->expected_result ==ATTACH_REJECT && err) { + err =0 ;/* error was expected, reset it */ + goto out; + }else if (test->expected_result ==ATTACH_REJECT || err) { goto err; + } -if (run_test_case (domain,SOCK_STREAM , ip, port) ==-1 ) +switch (test->attach_type) { +case BPF_CGROUP_INET4_BIND : +case BPF_CGROUP_INET6_BIND : + err =run_bind_test_case (test); +break ; +case BPF_CGROUP_INET4_CONNECT : +case BPF_CGROUP_INET6_CONNECT : + err =run_connect_test_case (test); +break ; +default : goto err; + } -if (run_test_case (domain,SOCK_DGRAM , ip, port) ==-1 ) +if (err || test->expected_result !=SUCCESS ) goto err; goto out; err: err =-1 ; out: -close_progs_fds (progs, prog_cnt); +/* Detaching w/o checking return code: best effort attempt. */ +if (progfd !=-1 ) +bpf_prog_detach (cgfd, test->attach_type); +close (progfd); +printf ("[%s]\n" , err ?"FAIL" :"PASS" );return err; } -static int run_test (void ) +static int run_tests (int cgfd) +{ +int passes =0 ; +int fails =0 ; +int i; + +for (i =0 ; i <ARRAY_SIZE (tests); ++i) { +if (run_test_case (cgfd, &tests[i])) + ++fails; +else + ++passes; + } +printf ("Summary: %d PASSED, %d FAILED\n" , passes, fails); +return fails ?-1 :0 ; +} + +int main (int argc,char **argv) { -size_t inet6_prog_cnt; -size_t inet_prog_cnt;int cgfd =-1 ;int err =0 ; -struct program inet6_progs[] = { - {BPF_CGROUP_INET6_BIND , bind6_prog_load,-1 ,"bind6" , -BPF_CGROUP_INET4_BIND }, - {BPF_CGROUP_INET6_CONNECT , connect6_prog_load,-1 ,"connect6" , -BPF_CGROUP_INET4_CONNECT }, - }; - inet6_prog_cnt =sizeof (inet6_progs) /sizeof (struct program ); - -struct program inet_progs[] = { - {BPF_CGROUP_INET4_BIND , bind4_prog_load,-1 ,"bind4" , -BPF_CGROUP_INET6_BIND }, - {BPF_CGROUP_INET4_CONNECT , connect4_prog_load,-1 ,"connect4" , -BPF_CGROUP_INET6_CONNECT }, - }; - inet_prog_cnt =sizeof (inet_progs) /sizeof (struct program ); +if (argc <2 ) { +fprintf (stderr, +"%s has to be run via %s.sh. Skip direct run.\n" , + argv[0 ], argv[0 ]); +exit (err); + }if (setup_cgroup_environment ()) goto err;
@@ -559,12 +841,7 @@ static int run_test(void)if (join_cgroup (CG_PATH )) goto err; -if (run_domain_test (AF_INET , cgfd, inet_progs, inet_prog_cnt,SERV4_IP , -SERV4_PORT ) ==-1 ) - goto err; - -if (run_domain_test (AF_INET6 , cgfd, inet6_progs, inet6_prog_cnt, -SERV6_IP ,SERV6_PORT ) ==-1 ) +if (run_tests (cgfd)) goto err; goto out;
@@ -573,17 +850,5 @@ static int run_test(void) out:close (cgfd);cleanup_cgroup_environment (); -printf (err ?"### FAIL\n" :"### SUCCESS\n" );return err; } - -int main (int argc,char **argv) -{ -if (argc <2 ) { -fprintf (stderr, -"%s has to be run via %s.sh. Skip direct run.\n" , - argv[0 ], argv[0 ]); -exit (0 ); - } -return run_test (); -}
Signature · 1 line
2.9.5
PATCH v3 5/5 selftests/bpf: Selftest for sys_sendmsg hooks
In reply to Andrey IgnatovAdd selftest for BPF_CGROUP_UDP4_SENDMSG and BPF_CGROUP_UDP6_SENDMSG attach types.
Try to sendmsg(2) to specific IP:port and test that:
* source IP is overridden as expected.
* remote IP:port pair is overridden as expected;
Both UDPv4 and UDPv6 are tested.
Output:
# test_sock_addr.sh 2>/dev/null Wait for testing IPv4/IPv6 to become available ... OK ... pre-existing test-cases skipped ... Test case: sendmsg4: load prog with wrong expected attach type .. [PASS] Test case: sendmsg4: attach prog with wrong attach type .. [PASS] Test case: sendmsg4: rewrite IP & port (asm) .. [PASS] Test case: sendmsg4: rewrite IP & port (C) .. [PASS] Test case: sendmsg4: deny call .. [PASS] Test case: sendmsg6: load prog with wrong expected attach type .. [PASS] Test case: sendmsg6: attach prog with wrong attach type .. [PASS] Test case: sendmsg6: rewrite IP & port (asm) .. [PASS] Test case: sendmsg6: rewrite IP & port (C) .. [PASS] Test case: sendmsg6: IPv4-mapped IPv6 .. [PASS] Test case: sendmsg6: deny call .. [PASS] Summary: 27 PASSED, 0 FAILED
--- tools/testing/selftests/bpf/Makefile | 2 +- tools/testing/selftests/bpf/sendmsg4_prog.c | 49 +++ tools/testing/selftests/bpf/sendmsg6_prog.c | 60 ++++ tools/testing/selftests/bpf/test_sock_addr.c | 518 +++++++++++++++++++++++++++ 4 files changed, 628 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/sendmsg4_prog.c create mode 100644 tools/testing/selftests/bpf/sendmsg6_prog.c
tools/testing/selftests/bpf/Makefile +1 -1
@@ -34,7 +34,7 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test sockmap_tcp_msg_prog.o connect4_prog.o connect6_prog.o test_adjust_tail.o \ test_btf_haskv.o test_btf_nokv.o test_sockmap_kern.o test_tunnel_kern.o \ test_get_stack_rawtp.o test_sockmap_kern.o test_sockhash_kern.o \ - test_lwt_seg6local.o + test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o # Order correspond to 'make run_tests' order TEST_PROGS := test_kmod.sh \
tools/testing/selftests/bpf/sendmsg4_prog.c new file +49 -0
49 lines not on this page · raw message
tools/testing/selftests/bpf/sendmsg6_prog.c new file +60 -0
60 lines not on this page · raw message
tools/testing/selftests/bpf/test_sock_addr.c +518 -0
597 lines not on this page · raw message
Signature · 1 line
2.9.5
Quoted 41 lines · Andrey Ignatov
v2 -> v3:
* place BPF logic under static key inudp_sendmsg,udpv6_sendmsg;
* rebase.v1 -> v2:
* return ENOTSUPP ifbpf_progrewrote IPv6-only with IPv4-mapped IPv6;
* add test for IPv4-mapped IPv6 use-case;
* fix build forCONFIG_CGROUP_BPF=n;
* rebase.This path set adds BPF hooks for
sys_sendmsgsimilar to existing hooks forsys_bindandsys_connect.Hooks allow to override source IP (including the case when it's set via cmsg(3)) and destination IP:port for unconnected UDP (slow path). TCP and connected UDP (fast path) are not affected. This makes UDP support
complete: connected UDP is handled bysys_connecthooks, unconnected bysys_sendmsgones.Similar to
sys_connecthooks,sys_sendmsgones can be used to make system calls such as sendmsg(2) and sendto(2) return EPERM.Please see patch 0001 for more details.
Andrey Ignatov (5):
bpf: Hooks forsys_sendmsg
bpf: Syncbpf.htotools/
libbpf: Support guessing sendmsg{4,6} progs selftests/bpf: Preparetest_sock_addrfor extension selftests/bpf: Selftest forsys_sendmsghooksinclude/linux/bpf-cgroup.h | 23 +- include/linux/filter.h | 1 + include/uapi/linux/bpf.h | 8 + kernel/bpf/cgroup.c | 11 +- kernel/bpf/syscall.c | 8 + net/core/filter.c | 39 + net/ipv4/udp.c | 20 +- net/ipv6/udp.c | 24 + tools/include/uapi/linux/bpf.h | 8 +
Quoted 9 lines
tools/lib/bpf/libbpf.c | 2 + tools/testing/selftests/bpf/Makefile | 2 +- tools/testing/selftests/bpf/sendmsg4_prog.c | 49 ++ tools/testing/selftests/bpf/sendmsg6_prog.c | 60 ++ tools/testing/selftests/bpf/test_sock_addr.c | 1155 +++++++++++++++++++++----- 14 files changed, 1214 insertions(+), 196 deletions(-) create mode 100644 tools/testing/selftests/bpf/sendmsg4_prog.c create mode 100644 tools/testing/selftests/bpf/sendmsg6_prog.c
Applied to bpf-next, thanks Andrey!
Daniel Borkmann
maintainerQuoted 4 lines · Daniel Borkmann
Quoted 3 lines · Andrey Ignatov
v2 -> v3:
* place BPF logic under static key inudp_sendmsg,udpv6_sendmsg;
* rebase.
[...]
Quoted 2 lines
Applied to bpf-next, thanks Andrey!
Woops, and I meant of course v4 [0] of the series not v3. ;-)
[0] https://patchwork.ozlabs.org/project/netdev/list/?series=46691&state=*