bpf: Hooks for sys_sendmsg
Subsystems (9)
cover bpf: Hooks for sys_sendmsg
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 | 17 + tools/include/uapi/linux/bpf.h | 8 + tools/lib/bpf/libbpf.c | 2 + tools/testing/selftests/bpf/Makefile | 3 +- tools/testing/selftests/bpf/sendmsg4_prog.c | 49 ++ tools/testing/selftests/bpf/sendmsg6_prog.c | 60 ++ tools/testing/selftests/bpf/test_sock_addr.c | 1118 +++++++++++++++++++++----- 14 files changed, 1171 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 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 | 17 +++++++++++++++++ 8 files changed, 118 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) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK (sk, uaddr) ({ 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
@@ -158,6 +158,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 };
@@ -2247,6 +2249,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
@@ -1247,6 +1247,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 ;
@@ -1563,6 +1565,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 :
@@ -1633,6 +1637,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 :
@@ -1690,6 +1696,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
@@ -5010,6 +5010,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;
@@ -5019,6 +5020,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;
@@ -5029,6 +5048,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);
@@ -5783,6 +5805,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
@@ -900,6 +900,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;
@@ -954,8 +955,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 ) {
@@ -1009,6 +1009,22 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)rcu_read_unlock (); } +if (!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 +17 -0
@@ -1315,6 +1315,22 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) fl6.saddr = np->saddr; fl6.fl6_sport = inet->inet_sport; +if (!connected) { + err =BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK (sk, + (struct sockaddr *)sin6, &fl6.saddr); +if (err) + goto out_no_dst; +if (sin6) { +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;
@@ -1394,6 +1410,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
Quoted 363 lines · Andrey Ignatov
In addition to already existing BPF hooks for
sys_bindandsys_connect, the patch provides new hooks forsys_sendmsg.It leverages existing BPF program type
BPF_PROG_TYPE_CGROUP_SOCK_ADDRthat provides access to socket itlself (properties like family, type, protocol) and user-passedstruct 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_SENDMSGandBPF_CGROUP_UDP6_SENDMSGfor UDPv4 and UDPv6 correspondingly.UDPv4 and UDPv6 separate attach types for same reason as
sys_bindandsys_connecthooks, i.e. to prevent reading from / writing to e.g.user_ip6fields when user passessockaddr_insince it'd be out-of-bound.The difference with already existing hooks is
sys_sendmsgare 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_connecthooks 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_connecthooks. Whensys_sendmsgis 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 tosys_sendmsg.To do so two new fields are added to UAPI
struct bpf_sock_addr;
*msg_src_ip4to set source IPv4 for UDPv4;
*msg_src_ip6to set source IPv6 for UDPv6.Signed-off-by Andrey Ignatov <[email protected]>--- 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 | 17 +++++++++++++++++ 8 files changed, 118 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) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK (sk, uaddr) ({ 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@@ -158,6 +158,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 };@@ -2247,6 +2249,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@@ -1247,6 +1247,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 ;@@ -1563,6 +1565,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 :@@ -1633,6 +1637,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 :@@ -1690,6 +1696,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@@ -5010,6 +5010,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;@@ -5019,6 +5020,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;@@ -5029,6 +5048,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);@@ -5783,6 +5805,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@@ -900,6 +900,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;@@ -954,8 +955,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 ) {@@ -1009,6 +1009,22 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)rcu_read_unlock (); } +if (!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 +3
-0@@ -1315,6 +1315,22 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) fl6.saddr = np->saddr; fl6.fl6_sport = inet->inet_sport; +if (!connected) { + err =BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK (sk, + (struct sockaddr *)sin6, &fl6.saddr);
net/ipv6/udp.c int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) · line 1330
Quoted 10 lines
net/ipv6/udp.c +10
-0+if (err) + goto out_no_dst; +if (sin6) { +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;
Could the bpf_prog change sin6 to a v4mapped address?
net/ipv6/udp.c int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) · line 1334
Quoted 4 lines
net/ipv6/udp.c +3
-0+ } + } + final_p =fl6_update_dst (&fl6, opt, &final);
It seems fl6_update_dst() may update fl6->daddr. Is it fine (or inline with the expectation after running a bpf_prog that has changed user_ip6)?
net/ipv6/udp.c int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) · line 1413
Quoted 7 lines
net/ipv6/udp.c +1
-0if (final_p) connected = false;@@ -1394,6 +1410,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) out:dst_release (dst); +out_no_dst:
A nit. If dst is init to NULL, can a new exit label be avoided?
Quoted 6 lines
net/ipv6/udp.c +0
-0fl6_sock_release (flowlabel);txopt_put (opt_to_free);if (!err)Signature · 1 line
2.9.5
Martin KaFai Lau <[email protected]> [Mon, 2018-05-21 16:17 -0700]:
Quoted 1 line · Andrey Ignatov
[...]
Quoted 8 lines
Quoted 8 lines
net/ipv6/udp.c +0
-0@@ -1315,6 +1315,22 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) fl6.saddr = np->saddr; fl6.fl6_sport = inet->inet_sport;
Quoted 14 lines
net/ipv6/udp.c int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) · line 1330
Quoted 13 lines
net/ipv6/udp.c +13
-0+if (!connected) { + err =BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK (sk, + (struct sockaddr *)sin6, &fl6.saddr); +if (err) + goto out_no_dst; +if (sin6) { +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;Could the
bpf_progchange sin6 to a v4mapped address?
It's a good point.
Yes, bpf_prog can rewrite sin6->sin6_addr with any IPv6 address, including IPv4-mapped IPv6 and this is not handled in this patch.
I see two possible options to handle this:
1) Return with error similar to how it's done for (sin6_port == 0).
2) Or delegate to udp_sendmsg() similar to how it's done in the beginning of udpv6_sendmsg().
By this point udpv6_sendmsg already checked that destination is not IPv4-mapped IPv6. And I think it might not be a good idea to rewrite IPv6-only address with IPv4-mapped IPv6 in bpf_prog.
So IMO "1)" is safer since user passed IPv6-only destination (IPv4-mapped IPv6 passed by user don't reach this point) and both msg->msg_name and msg->msg_control may have IPv6-only fields, e.g. sin6_flowinfo in msg->msg_name or SOL_IPV6 options in msg->msg_control. Also a few steps were already done based on this by that time, e.g. ancillary data, if present, was already parsed successfully by ip6_datagram_send_ctl().
As for specific errno, ENOTSUPP can be returned for now so that in the future there is a way to extend this code to support IPv4-mapped IPv6 if needed.
I'll send v2 with this change.
Quoted 7 lines
net/ipv6/udp.c int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) · line 1334
Quoted 4 lines
net/ipv6/udp.c +3
-0+ } + } + final_p =fl6_update_dst (&fl6, opt, &final);It seems
fl6_update_dst()may updatefl6->daddr. Is it fine (or inline with the expectation after running abpf_progthat has changeduser_ip6)?
Yeah, I checked this code and from what I see it should be fine.
If fl6_update_dst updates fl6->daddr, then it returns original daddr (e.g. that set by bpf_prog) as final_p. Later udpv6_sendmsg calls ip6_sk_dst_lookup_flow where the new daddr is used to lookup flow and it is set back to final_p in ip6_dst_lookup_flow:
if (final_dst) fl6->daddr = *final_dst;
So final value of fl6->daddr will be daddr set by bpf_prog.
Quoted 9 lines
net/ipv6/udp.c int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) · line 1413
Quoted 7 lines
net/ipv6/udp.c +1
-0if (final_p) connected = false;@@ -1394,6 +1410,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) out:dst_release (dst); +out_no_dst:A nit. If dst is init to NULL, can a new exit label be avoided?
Yes, if dst was init to NULL, new label could be avoided. But it's not initialized when created. It can probably be changed, but I'd need to check all dst usages to make sure that it's fine to remove dst = NULL from other places. Maybe in another patch.
Quoted 6 lines
Quoted 6 lines
net/ipv6/udp.c +0
-0fl6_sock_release (flowlabel);txopt_put (opt_to_free);if (!err)Signature · 1 line
2.9.5
Signature · 1 line
Andrey Ignatov
kbuild test robot
build botThank you for the patch! Yet something to improve:
Hi Andrey,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on bpf-next/master]
url: https://github.com/0day-ci/linux/commits/Andrey-Ignatov/bpf-Hooks-for-sys_sendmsg/20180522-065614 base: https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master config: x86_64-randconfig-x003-201820 (attached as .config) compiler: gcc-7 (Debian 7.3.0-16) 7.3.0 reproduce: # save the attached .config to linux build tree make ARCH=x86_64
All errors (new ones prefixed by >>):
net/ipv4/udp.c: In function 'udp_sendmsg':
>> net/ipv4/udp.c:1014:44: error: macro "BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK" passed 3 arguments, but takes just 2
(struct sockaddr *)usin, &ipc.addr);
^
>> net/ipv4/udp.c:1013:9: error: 'BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK' undeclared (first use in this function); did you mean 'BPF_CGROUP_UDP4_SENDMSG'? err = BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK(sk,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BPF_CGROUP_UDP4_SENDMSG
net/ipv4/udp.c:1013:9: note: each undeclared identifier is reported only once for each function it appears in
--
net/ipv6/udp.c: In function 'udpv6_sendmsg':
>> net/ipv6/udp.c:1320:44: error: macro "BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK" passed 3 arguments, but takes just 2
(struct sockaddr *)sin6, &fl6.saddr);
^
>> net/ipv6/udp.c:1319:9: error: 'BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK' undeclared (first use in this function); did you mean 'BPF_CGROUP_UDP6_SENDMSG'? err = BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK(sk,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BPF_CGROUP_UDP6_SENDMSG
net/ipv6/udp.c:1319:9: note: each undeclared identifier is reported only once for each function it appears in
vim +/BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK +1014 net/ipv4/udp.c
898
899 int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
900 {
901 struct inet_sock *inet = inet_sk(sk);
902 struct udp_sock *up = udp_sk(sk);
903 DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name);
904 struct flowi4 fl4_stack;
905 struct flowi4 *fl4;
906 int ulen = len;
907 struct ipcm_cookie ipc;
908 struct rtable *rt = NULL;
909 int free = 0;
910 int connected = 0;
911 __be32 daddr, faddr, saddr;
912 __be16 dport;
913 u8 tos;
914 int err, is_udplite = IS_UDPLITE(sk);
915 int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
916 int (*getfrag)(void *, char *, int, int, int, struct sk_buff *);
917 struct sk_buff *skb;
918 struct ip_options_data opt_copy;
919
920 if (len > 0xFFFF)
921 return -EMSGSIZE;
922
923 /*
924 * Check the flags.
925 */
926
927 if (msg->msg_flags & MSG_OOB) /* Mirror BSD error message compatibility */
928 return -EOPNOTSUPP;
929
930 ipc.opt = NULL;
931 ipc.tx_flags = 0;
932 ipc.ttl = 0;
933 ipc.tos = -1;
934
935 getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag;
936
937 fl4 = &inet->cork.fl.u.ip4;
938 if (up->pending) {
939 /*
940 * There are pending frames.
941 * The socket lock must be held while it's corked.
942 */
943 lock_sock(sk);
944 if (likely(up->pending)) {
945 if (unlikely(up->pending != AF_INET)) {
946 release_sock(sk);
947 return -EINVAL;
948 }
949 goto do_append_data;
950 }
951 release_sock(sk);
952 }
953 ulen += sizeof(struct udphdr);
954
955 /*
956 * Get and verify the address.
957 */
958 if (usin) {
959 if (msg->msg_namelen < sizeof(*usin))
960 return -EINVAL;
961 if (usin->sin_family != AF_INET) {
962 if (usin->sin_family != AF_UNSPEC)
963 return -EAFNOSUPPORT;
964 }
965
966 daddr = usin->sin_addr.s_addr;
967 dport = usin->sin_port;
968 if (dport == 0)
969 return -EINVAL;
970 } else {
971 if (sk->sk_state != TCP_ESTABLISHED)
972 return -EDESTADDRREQ;
973 daddr = inet->inet_daddr;
974 dport = inet->inet_dport;
975 /* Open fast path for connected socket.
976 Route will not be used, if at least one option is set.
977 */
978 connected = 1;
979 }
980
981 ipc.sockc.tsflags = sk->sk_tsflags;
982 ipc.addr = inet->inet_saddr;
983 ipc.oif = sk->sk_bound_dev_if;
984 ipc.gso_size = up->gso_size;
985
986 if (msg->msg_controllen) {
987 err = udp_cmsg_send(sk, msg, &ipc.gso_size);
988 if (err > 0)
989 err = ip_cmsg_send(sk, msg, &ipc,
990 sk->sk_family == AF_INET6);
991 if (unlikely(err < 0)) {
992 kfree(ipc.opt);
993 return err;
994 }
995 if (ipc.opt)
996 free = 1;
997 connected = 0;
998 }
999 if (!ipc.opt) {
1000 struct ip_options_rcu *inet_opt;
1001
1002 rcu_read_lock();
1003 inet_opt = rcu_dereference(inet->inet_opt);
1004 if (inet_opt) {
1005 memcpy(&opt_copy, inet_opt,
1006 sizeof(*inet_opt) + inet_opt->opt.optlen);
1007 ipc.opt = &opt_copy.opt;
1008 }
1009 rcu_read_unlock();
1010 }
1011
1012 if (!connected) {
> 1013 err = BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK(sk,
> 1014 (struct sockaddr *)usin, &ipc.addr);
1015 if (err)
1016 goto out_free;
1017 if (usin) {
1018 if (usin->sin_port == 0) {
1019 /* BPF program set invalid port. Reject it. */
1020 err = -EINVAL;
1021 goto out_free;
1022 }
1023 daddr = usin->sin_addr.s_addr;
1024 dport = usin->sin_port;
1025 }
1026 }
1027
1028 saddr = ipc.addr;
1029 ipc.addr = faddr = daddr;
1030
1031 sock_tx_timestamp(sk, ipc.sockc.tsflags, &ipc.tx_flags);
1032
1033 if (ipc.opt && ipc.opt->opt.srr) {
1034 if (!daddr) {
1035 err = -EINVAL;
1036 goto out_free;
1037 }
1038 faddr = ipc.opt->opt.faddr;
1039 connected = 0;
1040 }
1041 tos = get_rttos(&ipc, inet);
1042 if (sock_flag(sk, SOCK_LOCALROUTE) ||
1043 (msg->msg_flags & MSG_DONTROUTE) ||
1044 (ipc.opt && ipc.opt->opt.is_strictroute)) {
1045 tos |= RTO_ONLINK;
1046 connected = 0;
1047 }
1048
1049 if (ipv4_is_multicast(daddr)) {
1050 if (!ipc.oif)
1051 ipc.oif = inet->mc_index;
1052 if (!saddr)
1053 saddr = inet->mc_addr;
1054 connected = 0;
1055 } else if (!ipc.oif) {
1056 ipc.oif = inet->uc_index;
1057 } else if (ipv4_is_lbcast(daddr) && inet->uc_index) {
1058 /* oif is set, packet is to local broadcast and
1059 * and uc_index is set. oif is most likely set
1060 * by sk_bound_dev_if. If uc_index != oif check if the
1061 * oif is an L3 master and uc_index is an L3 slave.
1062 * If so, we want to allow the send using the uc_index.
1063 */
1064 if (ipc.oif != inet->uc_index &&
1065 ipc.oif == l3mdev_master_ifindex_by_index(sock_net(sk),
1066 inet->uc_index)) {
1067 ipc.oif = inet->uc_index;
1068 }
1069 }
1070
1071 if (connected)
1072 rt = (struct rtable *)sk_dst_check(sk, 0);
1073
1074 if (!rt) {
1075 struct net *net = sock_net(sk);
1076 __u8 flow_flags = inet_sk_flowi_flags(sk);
1077
1078 fl4 = &fl4_stack;
1079
1080 flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos,
1081 RT_SCOPE_UNIVERSE, sk->sk_protocol,
1082 flow_flags,
1083 faddr, saddr, dport, inet->inet_sport,
1084 sk->sk_uid); 1085
1086 security_sk_classify_flow(sk, flowi4_to_flowi(fl4));
1087 rt = ip_route_output_flow(net, fl4, sk);
1088 if (IS_ERR(rt)) {
1089 err = PTR_ERR(rt);
1090 rt = NULL;
1091 if (err == -ENETUNREACH)
1092 IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
1093 goto out;
1094 }
1095
1096 err = -EACCES;
1097 if ((rt->rt_flags & RTCF_BROADCAST) &&
1098 !sock_flag(sk, SOCK_BROADCAST))
1099 goto out;
1100 if (connected)
1101 sk_dst_set(sk, dst_clone(&rt->dst));
1102 }
1103
1104 if (msg->msg_flags&MSG_CONFIRM)
1105 goto do_confirm;
1106 back_from_confirm:
1107
1108 saddr = fl4->saddr;
1109 if (!ipc.addr)
1110 daddr = ipc.addr = fl4->daddr;
1111
1112 /* Lockless fast path for the non-corking case. */
1113 if (!corkreq) {
1114 struct inet_cork cork;
1115
1116 skb = ip_make_skb(sk, fl4, getfrag, msg, ulen,
1117 sizeof(struct udphdr), &ipc, &rt,
1118 &cork, msg->msg_flags);
1119 err = PTR_ERR(skb);
1120 if (!IS_ERR_OR_NULL(skb))
1121 err = udp_send_skb(skb, fl4, &cork);
1122 goto out;
1123 }
1124
1125 lock_sock(sk);
1126 if (unlikely(up->pending)) {
1127 /* The socket is already corked while preparing it. */
1128 /* ... which is an evident application bug. --ANK */
1129 release_sock(sk);
1130
1131 net_dbg_ratelimited("socket already corked\n");
1132 err = -EINVAL;
1133 goto out;
1134 }
1135 /*
1136 * Now cork the socket to pend data.
1137 */
1138 fl4 = &inet->cork.fl.u.ip4;
1139 fl4->daddr = daddr;
1140 fl4->saddr = saddr;
1141 fl4->fl4_dport = dport;
1142 fl4->fl4_sport = inet->inet_sport;
1143 up->pending = AF_INET;
1144
1145 do_append_data:
1146 up->len += ulen;
1147 err = ip_append_data(sk, fl4, getfrag, msg, ulen,
1148 sizeof(struct udphdr), &ipc, &rt,
1149 corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
1150 if (err)
1151 udp_flush_pending_frames(sk);
1152 else if (!corkreq)
1153 err = udp_push_pending_frames(sk);
1154 else if (unlikely(skb_queue_empty(&sk->sk_write_queue)))
1155 up->pending = 0;
1156 release_sock(sk);
1157
1158 out:
1159 ip_rt_put(rt);
1160 out_free:
1161 if (free)
1162 kfree(ipc.opt);
1163 if (!err)
1164 return len;
1165 /*
1166 * ENOBUFS = no kernel mem, SOCK_NOSPACE = no sndbuf space. Reporting
1167 * ENOBUFS might not be good (it's not tunable per se), but otherwise
1168 * we don't have a good statistic (IpOutDiscards but it can be too many
1169 * things). We could add another new stat but at least for now that
1170 * seems like overkill.
1171 */
1172 if (err == -ENOBUFS || test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) {
1173 UDP_INC_STATS(sock_net(sk),
1174 UDP_MIB_SNDBUFERRORS, is_udplite);
1175 }
1176 return err;
1177
1178 do_confirm:
1179 if (msg->msg_flags & MSG_PROBE)
1180 dst_confirm_neigh(&rt->dst, &fl4->daddr);
1181 if (!(msg->msg_flags&MSG_PROBE) || len)
1182 goto back_from_confirm;
1183 err = 0;
1184 goto out;
1185 }
1186 EXPORT_SYMBOL(udp_sendmsg);
1187
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
kbuild test robot
build botThank you for the patch! Yet something to improve:
Hi Andrey,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on bpf-next/master]
url: https://github.com/0day-ci/linux/commits/Andrey-Ignatov/bpf-Hooks-for-sys_sendmsg/20180522-065614 base: https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master config: i386-randconfig-s1-201820 (attached as .config) compiler: gcc-6 (Debian 6.4.0-9) 6.4.0 20171026 reproduce: # save the attached .config to linux build tree make ARCH=i386
All errors (new ones prefixed by >>):
net/ipv4/udp.c: In function 'udp_sendmsg':
net/ipv4/udp.c:1014:44: error: macro "BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK" passed 3 arguments, but takes just 2
(struct sockaddr *)usin, &ipc.addr);
^
>> net/ipv4/udp.c:1013:9: error: 'BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK' undeclared (first use in this function) err = BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK(sk,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
net/ipv4/udp.c:1013:9: note: each undeclared identifier is reported only once for each function it appears in
--
net/ipv6/udp.c: In function 'udpv6_sendmsg':
net/ipv6/udp.c:1320:44: error: macro "BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK" passed 3 arguments, but takes just 2
(struct sockaddr *)sin6, &fl6.saddr);
^
>> net/ipv6/udp.c:1319:9: error: 'BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK' undeclared (first use in this function) err = BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK(sk,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
net/ipv6/udp.c:1319:9: note: each undeclared identifier is reported only once for each function it appears in
vim +/BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK +1013 net/ipv4/udp.c
898
899 int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
900 {
901 struct inet_sock *inet = inet_sk(sk);
902 struct udp_sock *up = udp_sk(sk);
903 DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name);
904 struct flowi4 fl4_stack;
905 struct flowi4 *fl4;
906 int ulen = len;
907 struct ipcm_cookie ipc;
908 struct rtable *rt = NULL;
909 int free = 0;
910 int connected = 0;
911 __be32 daddr, faddr, saddr;
912 __be16 dport;
913 u8 tos;
914 int err, is_udplite = IS_UDPLITE(sk);
915 int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
916 int (*getfrag)(void *, char *, int, int, int, struct sk_buff *);
917 struct sk_buff *skb;
918 struct ip_options_data opt_copy;
919
920 if (len > 0xFFFF)
921 return -EMSGSIZE;
922
923 /*
924 * Check the flags.
925 */
926
927 if (msg->msg_flags & MSG_OOB) /* Mirror BSD error message compatibility */
928 return -EOPNOTSUPP;
929
930 ipc.opt = NULL;
931 ipc.tx_flags = 0;
932 ipc.ttl = 0;
933 ipc.tos = -1;
934
935 getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag;
936
937 fl4 = &inet->cork.fl.u.ip4;
938 if (up->pending) {
939 /*
940 * There are pending frames.
941 * The socket lock must be held while it's corked.
942 */
943 lock_sock(sk);
944 if (likely(up->pending)) {
945 if (unlikely(up->pending != AF_INET)) {
946 release_sock(sk);
947 return -EINVAL;
948 }
949 goto do_append_data;
950 }
951 release_sock(sk);
952 }
953 ulen += sizeof(struct udphdr);
954
955 /*
956 * Get and verify the address.
957 */
958 if (usin) {
959 if (msg->msg_namelen < sizeof(*usin))
960 return -EINVAL;
961 if (usin->sin_family != AF_INET) {
962 if (usin->sin_family != AF_UNSPEC)
963 return -EAFNOSUPPORT;
964 }
965
966 daddr = usin->sin_addr.s_addr;
967 dport = usin->sin_port;
968 if (dport == 0)
969 return -EINVAL;
970 } else {
971 if (sk->sk_state != TCP_ESTABLISHED)
972 return -EDESTADDRREQ;
973 daddr = inet->inet_daddr;
974 dport = inet->inet_dport;
975 /* Open fast path for connected socket.
976 Route will not be used, if at least one option is set.
977 */
978 connected = 1;
979 }
980
981 ipc.sockc.tsflags = sk->sk_tsflags;
982 ipc.addr = inet->inet_saddr;
983 ipc.oif = sk->sk_bound_dev_if;
984 ipc.gso_size = up->gso_size;
985
986 if (msg->msg_controllen) {
987 err = udp_cmsg_send(sk, msg, &ipc.gso_size);
988 if (err > 0)
989 err = ip_cmsg_send(sk, msg, &ipc,
990 sk->sk_family == AF_INET6);
991 if (unlikely(err < 0)) {
992 kfree(ipc.opt);
993 return err;
994 }
995 if (ipc.opt)
996 free = 1;
997 connected = 0;
998 }
999 if (!ipc.opt) {
1000 struct ip_options_rcu *inet_opt;
1001
1002 rcu_read_lock();
1003 inet_opt = rcu_dereference(inet->inet_opt);
1004 if (inet_opt) {
1005 memcpy(&opt_copy, inet_opt,
1006 sizeof(*inet_opt) + inet_opt->opt.optlen);
1007 ipc.opt = &opt_copy.opt;
1008 }
1009 rcu_read_unlock();
1010 }
1011
1012 if (!connected) {
> 1013 err = BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK(sk,
> 1014 (struct sockaddr *)usin, &ipc.addr);
1015 if (err)
1016 goto out_free;
1017 if (usin) {
1018 if (usin->sin_port == 0) {
1019 /* BPF program set invalid port. Reject it. */
1020 err = -EINVAL;
1021 goto out_free;
1022 }
1023 daddr = usin->sin_addr.s_addr;
1024 dport = usin->sin_port;
1025 }
1026 }
1027
1028 saddr = ipc.addr;
1029 ipc.addr = faddr = daddr;
1030
1031 sock_tx_timestamp(sk, ipc.sockc.tsflags, &ipc.tx_flags);
1032
1033 if (ipc.opt && ipc.opt->opt.srr) {
1034 if (!daddr) {
1035 err = -EINVAL;
1036 goto out_free;
1037 }
1038 faddr = ipc.opt->opt.faddr;
1039 connected = 0;
1040 }
1041 tos = get_rttos(&ipc, inet);
1042 if (sock_flag(sk, SOCK_LOCALROUTE) ||
1043 (msg->msg_flags & MSG_DONTROUTE) ||
1044 (ipc.opt && ipc.opt->opt.is_strictroute)) {
1045 tos |= RTO_ONLINK;
1046 connected = 0;
1047 }
1048
1049 if (ipv4_is_multicast(daddr)) {
1050 if (!ipc.oif)
1051 ipc.oif = inet->mc_index;
1052 if (!saddr)
1053 saddr = inet->mc_addr;
1054 connected = 0;
1055 } else if (!ipc.oif) {
1056 ipc.oif = inet->uc_index;
1057 } else if (ipv4_is_lbcast(daddr) && inet->uc_index) {
1058 /* oif is set, packet is to local broadcast and
1059 * and uc_index is set. oif is most likely set
1060 * by sk_bound_dev_if. If uc_index != oif check if the
1061 * oif is an L3 master and uc_index is an L3 slave.
1062 * If so, we want to allow the send using the uc_index.
1063 */
1064 if (ipc.oif != inet->uc_index &&
1065 ipc.oif == l3mdev_master_ifindex_by_index(sock_net(sk),
1066 inet->uc_index)) {
1067 ipc.oif = inet->uc_index;
1068 }
1069 }
1070
1071 if (connected)
1072 rt = (struct rtable *)sk_dst_check(sk, 0);
1073
1074 if (!rt) {
1075 struct net *net = sock_net(sk);
1076 __u8 flow_flags = inet_sk_flowi_flags(sk);
1077
1078 fl4 = &fl4_stack;
1079
1080 flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos,
1081 RT_SCOPE_UNIVERSE, sk->sk_protocol,
1082 flow_flags,
1083 faddr, saddr, dport, inet->inet_sport,
1084 sk->sk_uid); 1085
1086 security_sk_classify_flow(sk, flowi4_to_flowi(fl4));
1087 rt = ip_route_output_flow(net, fl4, sk);
1088 if (IS_ERR(rt)) {
1089 err = PTR_ERR(rt);
1090 rt = NULL;
1091 if (err == -ENETUNREACH)
1092 IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
1093 goto out;
1094 }
1095
1096 err = -EACCES;
1097 if ((rt->rt_flags & RTCF_BROADCAST) &&
1098 !sock_flag(sk, SOCK_BROADCAST))
1099 goto out;
1100 if (connected)
1101 sk_dst_set(sk, dst_clone(&rt->dst));
1102 }
1103
1104 if (msg->msg_flags&MSG_CONFIRM)
1105 goto do_confirm;
1106 back_from_confirm:
1107
1108 saddr = fl4->saddr;
1109 if (!ipc.addr)
1110 daddr = ipc.addr = fl4->daddr;
1111
1112 /* Lockless fast path for the non-corking case. */
1113 if (!corkreq) {
1114 struct inet_cork cork;
1115
1116 skb = ip_make_skb(sk, fl4, getfrag, msg, ulen,
1117 sizeof(struct udphdr), &ipc, &rt,
1118 &cork, msg->msg_flags);
1119 err = PTR_ERR(skb);
1120 if (!IS_ERR_OR_NULL(skb))
1121 err = udp_send_skb(skb, fl4, &cork);
1122 goto out;
1123 }
1124
1125 lock_sock(sk);
1126 if (unlikely(up->pending)) {
1127 /* The socket is already corked while preparing it. */
1128 /* ... which is an evident application bug. --ANK */
1129 release_sock(sk);
1130
1131 net_dbg_ratelimited("socket already corked\n");
1132 err = -EINVAL;
1133 goto out;
1134 }
1135 /*
1136 * Now cork the socket to pend data.
1137 */
1138 fl4 = &inet->cork.fl.u.ip4;
1139 fl4->daddr = daddr;
1140 fl4->saddr = saddr;
1141 fl4->fl4_dport = dport;
1142 fl4->fl4_sport = inet->inet_sport;
1143 up->pending = AF_INET;
1144
1145 do_append_data:
1146 up->len += ulen;
1147 err = ip_append_data(sk, fl4, getfrag, msg, ulen,
1148 sizeof(struct udphdr), &ipc, &rt,
1149 corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
1150 if (err)
1151 udp_flush_pending_frames(sk);
1152 else if (!corkreq)
1153 err = udp_push_pending_frames(sk);
1154 else if (unlikely(skb_queue_empty(&sk->sk_write_queue)))
1155 up->pending = 0;
1156 release_sock(sk);
1157
1158 out:
1159 ip_rt_put(rt);
1160 out_free:
1161 if (free)
1162 kfree(ipc.opt);
1163 if (!err)
1164 return len;
1165 /*
1166 * ENOBUFS = no kernel mem, SOCK_NOSPACE = no sndbuf space. Reporting
1167 * ENOBUFS might not be good (it's not tunable per se), but otherwise
1168 * we don't have a good statistic (IpOutDiscards but it can be too many
1169 * things). We could add another new stat but at least for now that
1170 * seems like overkill.
1171 */
1172 if (err == -ENOBUFS || test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) {
1173 UDP_INC_STATS(sock_net(sk),
1174 UDP_MIB_SNDBUFERRORS, is_udplite);
1175 }
1176 return err;
1177
1178 do_confirm:
1179 if (msg->msg_flags & MSG_PROBE)
1180 dst_confirm_neigh(&rt->dst, &fl4->daddr);
1181 if (!(msg->msg_flags&MSG_PROBE) || len)
1182 goto back_from_confirm;
1183 err = 0;
1184 goto out;
1185 }
1186 EXPORT_SYMBOL(udp_sendmsg);
1187
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
PATCH 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
@@ -158,6 +158,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 };
@@ -2247,6 +2249,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
Re: [PATCH bpf-next 2/5] bpf: Sync bpf.h to tools/
Quoted 5 lines · Andrey Ignatov
Sync new
BPF_CGROUP_UDP4_SENDMSGandBPF_CGROUP_UDP6_SENDMSGattach types totools/.Signed-off-by Andrey Ignatov <[email protected]>
PATCH 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
@@ -2042,6 +2042,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
Re: [PATCH bpf-next 3/5] libbpf: Support guessing sendmsg{4,6} progs
Quoted 6 lines · Andrey Ignatov
libbpf can guess prog type and expected attach type based on section name. Add hints for "cgroup/sendmsg4" and "cgroup/sendmsg6" section names.
Signed-off-by Andrey Ignatov <[email protected]>
PATCH 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
Re: [PATCH bpf-next 4/5] selftests/bpf: Prepare test_sock_addr for extension
Quoted 33 lines · Andrey Ignatov
test_sock_addrwas not easy to extend since it was focused onsys_bindandsys_connectquite 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 forsys_connectare 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
Quoted 5 lines
(stderr contains errors from libbpf when testing load/attach with invalid arguments)
Signed-off-by Andrey Ignatov <[email protected]>
PATCH 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: deny call .. [PASS] Summary: 26 PASSED, 0 FAILED
--- tools/testing/selftests/bpf/Makefile | 3 +- tools/testing/selftests/bpf/sendmsg4_prog.c | 49 +++ tools/testing/selftests/bpf/sendmsg6_prog.c | 60 ++++ tools/testing/selftests/bpf/test_sock_addr.c | 481 +++++++++++++++++++++++++++ 4 files changed, 592 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 +2 -1
@@ -33,7 +33,8 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test sample_map_ret0.o test_tcpbpf_kern.o test_stacktrace_build_id.o \ 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_get_stack_rawtp.o test_sockmap_kern.o test_sockhash_kern.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 +481 -0
554 lines not on this page · raw message
Signature · 1 line
2.9.5
Re: [PATCH bpf-next 5/5] selftests/bpf: Selftest for sys_sendmsg hooks
Quoted 25 lines · Andrey Ignatov
Add selftest for
BPF_CGROUP_UDP4_SENDMSGandBPF_CGROUP_UDP6_SENDMSGattach 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: deny call .. [PASS] Summary: 26 PASSED, 0 FAILED
Quoted 2 lines
Signed-off-by Andrey Ignatov <[email protected]>