Skip to content

Commit 40304b2

Browse files
Lawrence Brakmodavem330
Lawrence Brakmo
authored andcommitted
bpf: BPF support for sock_ops
Created a new BPF program type, BPF_PROG_TYPE_SOCK_OPS, and a corresponding struct that allows BPF programs of this type to access some of the socket's fields (such as IP addresses, ports, etc.). It uses the existing bpf cgroups infrastructure so the programs can be attached per cgroup with full inheritance support. The program will be called at appropriate times to set relevant connections parameters such as buffer sizes, SYN and SYN-ACK RTOs, etc., based on connection information such as IP addresses, port numbers, etc. Alghough there are already 3 mechanisms to set parameters (sysctls, route metrics and setsockopts), this new mechanism provides some distinct advantages. Unlike sysctls, it can set parameters per connection. In contrast to route metrics, it can also use port numbers and information provided by a user level program. In addition, it could set parameters probabilistically for evaluation purposes (i.e. do something different on 10% of the flows and compare results with the other 90% of the flows). Also, in cases where IPv6 addresses contain geographic information, the rules to make changes based on the distance (or RTT) between the hosts are much easier than route metric rules and can be global. Finally, unlike setsockopt, it oes not require application changes and it can be updated easily at any time. Although the bpf cgroup framework already contains a sock related program type (BPF_PROG_TYPE_CGROUP_SOCK), I created the new type (BPF_PROG_TYPE_SOCK_OPS) beccause the existing type expects to be called only once during the connections's lifetime. In contrast, the new program type will be called multiple times from different places in the network stack code. For example, before sending SYN and SYN-ACKs to set an appropriate timeout, when the connection is established to set congestion control, etc. As a result it has "op" field to specify the type of operation requested. The purpose of this new program type is to simplify setting connection parameters, such as buffer sizes, TCP's SYN RTO, etc. For example, it is easy to use facebook's internal IPv6 addresses to determine if both hosts of a connection are in the same datacenter. Therefore, it is easy to write a BPF program to choose a small SYN RTO value when both hosts are in the same datacenter. This patch only contains the framework to support the new BPF program type, following patches add the functionality to set various connection parameters. This patch defines a new BPF program type: BPF_PROG_TYPE_SOCKET_OPS and a new bpf syscall command to load a new program of this type: BPF_PROG_LOAD_SOCKET_OPS. Two new corresponding structs (one for the kernel one for the user/BPF program): /* kernel version */ struct bpf_sock_ops_kern { struct sock *sk; __u32 op; union { __u32 reply; __u32 replylong[4]; }; }; /* user version * Some fields are in network byte order reflecting the sock struct * Use the bpf_ntohl helper macro in samples/bpf/bpf_endian.h to * convert them to host byte order. */ struct bpf_sock_ops { __u32 op; union { __u32 reply; __u32 replylong[4]; }; __u32 family; __u32 remote_ip4; /* In network byte order */ __u32 local_ip4; /* In network byte order */ __u32 remote_ip6[4]; /* In network byte order */ __u32 local_ip6[4]; /* In network byte order */ __u32 remote_port; /* In network byte order */ __u32 local_port; /* In host byte horder */ }; Currently there are two types of ops. The first type expects the BPF program to return a value which is then used by the caller (or a negative value to indicate the operation is not supported). The second type expects state changes to be done by the BPF program, for example through a setsockopt BPF helper function, and they ignore the return value. The reply fields of the bpf_sockt_ops struct are there in case a bpf program needs to return a value larger than an integer. Signed-off-by: Lawrence Brakmo <[email protected]> Acked-by: Daniel Borkmann <[email protected]> Acked-by: Alexei Starovoitov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 57a53a0 commit 40304b2

File tree

9 files changed

+314
-3
lines changed

9 files changed

+314
-3
lines changed

include/linux/bpf-cgroup.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
struct sock;
88
struct cgroup;
99
struct sk_buff;
10+
struct bpf_sock_ops_kern;
1011

1112
#ifdef CONFIG_CGROUP_BPF
1213

@@ -42,6 +43,10 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk,
4243
int __cgroup_bpf_run_filter_sk(struct sock *sk,
4344
enum bpf_attach_type type);
4445

46+
int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,
47+
struct bpf_sock_ops_kern *sock_ops,
48+
enum bpf_attach_type type);
49+
4550
/* Wrappers for __cgroup_bpf_run_filter_skb() guarded by cgroup_bpf_enabled. */
4651
#define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk, skb) \
4752
({ \
@@ -75,6 +80,18 @@ int __cgroup_bpf_run_filter_sk(struct sock *sk,
7580
__ret; \
7681
})
7782

83+
#define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) \
84+
({ \
85+
int __ret = 0; \
86+
if (cgroup_bpf_enabled && (sock_ops)->sk) { \
87+
typeof(sk) __sk = sk_to_full_sk((sock_ops)->sk); \
88+
if (sk_fullsock(__sk)) \
89+
__ret = __cgroup_bpf_run_filter_sock_ops(__sk, \
90+
sock_ops, \
91+
BPF_CGROUP_SOCK_OPS); \
92+
} \
93+
__ret; \
94+
})
7895
#else
7996

8097
struct cgroup_bpf {};
@@ -85,6 +102,7 @@ static inline void cgroup_bpf_inherit(struct cgroup *cgrp,
85102
#define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk,skb) ({ 0; })
86103
#define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) ({ 0; })
87104
#define BPF_CGROUP_RUN_PROG_INET_SOCK(sk) ({ 0; })
105+
#define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; })
88106

89107
#endif /* CONFIG_CGROUP_BPF */
90108

include/linux/bpf_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCK, cg_sock_prog_ops)
1010
BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_IN, lwt_inout_prog_ops)
1111
BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_OUT, lwt_inout_prog_ops)
1212
BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_XMIT, lwt_xmit_prog_ops)
13+
BPF_PROG_TYPE(BPF_PROG_TYPE_SOCK_OPS, sock_ops_prog_ops)
1314
#endif
1415
#ifdef CONFIG_BPF_EVENTS
1516
BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe_prog_ops)

include/linux/filter.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,4 +898,13 @@ static inline int bpf_tell_extensions(void)
898898
return SKF_AD_MAX;
899899
}
900900

901+
struct bpf_sock_ops_kern {
902+
struct sock *sk;
903+
u32 op;
904+
union {
905+
u32 reply;
906+
u32 replylong[4];
907+
};
908+
};
909+
901910
#endif /* __LINUX_FILTER_H__ */

include/net/tcp.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@
4646
#include <linux/seq_file.h>
4747
#include <linux/memcontrol.h>
4848

49+
#include <linux/bpf.h>
50+
#include <linux/filter.h>
51+
#include <linux/bpf-cgroup.h>
52+
4953
extern struct inet_hashinfo tcp_hashinfo;
5054

5155
extern struct percpu_counter tcp_orphan_count;
@@ -2021,4 +2025,36 @@ int tcp_set_ulp(struct sock *sk, const char *name);
20212025
void tcp_get_available_ulp(char *buf, size_t len);
20222026
void tcp_cleanup_ulp(struct sock *sk);
20232027

2028+
/* Call BPF_SOCK_OPS program that returns an int. If the return value
2029+
* is < 0, then the BPF op failed (for example if the loaded BPF
2030+
* program does not support the chosen operation or there is no BPF
2031+
* program loaded).
2032+
*/
2033+
#ifdef CONFIG_BPF
2034+
static inline int tcp_call_bpf(struct sock *sk, int op)
2035+
{
2036+
struct bpf_sock_ops_kern sock_ops;
2037+
int ret;
2038+
2039+
if (sk_fullsock(sk))
2040+
sock_owned_by_me(sk);
2041+
2042+
memset(&sock_ops, 0, sizeof(sock_ops));
2043+
sock_ops.sk = sk;
2044+
sock_ops.op = op;
2045+
2046+
ret = BPF_CGROUP_RUN_PROG_SOCK_OPS(&sock_ops);
2047+
if (ret == 0)
2048+
ret = sock_ops.reply;
2049+
else
2050+
ret = -1;
2051+
return ret;
2052+
}
2053+
#else
2054+
static inline int tcp_call_bpf(struct sock *sk, int op)
2055+
{
2056+
return -EPERM;
2057+
}
2058+
#endif
2059+
20242060
#endif /* _TCP_H */

include/uapi/linux/bpf.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,14 @@ enum bpf_prog_type {
120120
BPF_PROG_TYPE_LWT_IN,
121121
BPF_PROG_TYPE_LWT_OUT,
122122
BPF_PROG_TYPE_LWT_XMIT,
123+
BPF_PROG_TYPE_SOCK_OPS,
123124
};
124125

125126
enum bpf_attach_type {
126127
BPF_CGROUP_INET_INGRESS,
127128
BPF_CGROUP_INET_EGRESS,
128129
BPF_CGROUP_INET_SOCK_CREATE,
130+
BPF_CGROUP_SOCK_OPS,
129131
__MAX_BPF_ATTACH_TYPE
130132
};
131133

@@ -720,4 +722,32 @@ struct bpf_map_info {
720722
__u32 map_flags;
721723
} __attribute__((aligned(8)));
722724

725+
/* User bpf_sock_ops struct to access socket values and specify request ops
726+
* and their replies.
727+
* Some of this fields are in network (bigendian) byte order and may need
728+
* to be converted before use (bpf_ntohl() defined in samples/bpf/bpf_endian.h).
729+
* New fields can only be added at the end of this structure
730+
*/
731+
struct bpf_sock_ops {
732+
__u32 op;
733+
union {
734+
__u32 reply;
735+
__u32 replylong[4];
736+
};
737+
__u32 family;
738+
__u32 remote_ip4; /* Stored in network byte order */
739+
__u32 local_ip4; /* Stored in network byte order */
740+
__u32 remote_ip6[4]; /* Stored in network byte order */
741+
__u32 local_ip6[4]; /* Stored in network byte order */
742+
__u32 remote_port; /* Stored in network byte order */
743+
__u32 local_port; /* stored in host byte order */
744+
};
745+
746+
/* List of known BPF sock_ops operators.
747+
* New entries can only be added at the end
748+
*/
749+
enum {
750+
BPF_SOCK_OPS_VOID,
751+
};
752+
723753
#endif /* _UAPI__LINUX_BPF_H__ */

kernel/bpf/cgroup.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,40 @@ int __cgroup_bpf_run_filter_sk(struct sock *sk,
236236
return ret;
237237
}
238238
EXPORT_SYMBOL(__cgroup_bpf_run_filter_sk);
239+
240+
/**
241+
* __cgroup_bpf_run_filter_sock_ops() - Run a program on a sock
242+
* @sk: socket to get cgroup from
243+
* @sock_ops: bpf_sock_ops_kern struct to pass to program. Contains
244+
* sk with connection information (IP addresses, etc.) May not contain
245+
* cgroup info if it is a req sock.
246+
* @type: The type of program to be exectuted
247+
*
248+
* socket passed is expected to be of type INET or INET6.
249+
*
250+
* The program type passed in via @type must be suitable for sock_ops
251+
* filtering. No further check is performed to assert that.
252+
*
253+
* This function will return %-EPERM if any if an attached program was found
254+
* and if it returned != 1 during execution. In all other cases, 0 is returned.
255+
*/
256+
int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,
257+
struct bpf_sock_ops_kern *sock_ops,
258+
enum bpf_attach_type type)
259+
{
260+
struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
261+
struct bpf_prog *prog;
262+
int ret = 0;
263+
264+
265+
rcu_read_lock();
266+
267+
prog = rcu_dereference(cgrp->bpf.effective[type]);
268+
if (prog)
269+
ret = BPF_PROG_RUN(prog, sock_ops) == 1 ? 0 : -EPERM;
270+
271+
rcu_read_unlock();
272+
273+
return ret;
274+
}
275+
EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_ops);

kernel/bpf/syscall.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,9 @@ static int bpf_prog_attach(const union bpf_attr *attr)
10791079
case BPF_CGROUP_INET_SOCK_CREATE:
10801080
ptype = BPF_PROG_TYPE_CGROUP_SOCK;
10811081
break;
1082+
case BPF_CGROUP_SOCK_OPS:
1083+
ptype = BPF_PROG_TYPE_SOCK_OPS;
1084+
break;
10821085
default:
10831086
return -EINVAL;
10841087
}
@@ -1119,6 +1122,7 @@ static int bpf_prog_detach(const union bpf_attr *attr)
11191122
case BPF_CGROUP_INET_INGRESS:
11201123
case BPF_CGROUP_INET_EGRESS:
11211124
case BPF_CGROUP_INET_SOCK_CREATE:
1125+
case BPF_CGROUP_SOCK_OPS:
11221126
cgrp = cgroup_get_from_fd(attr->target_fd);
11231127
if (IS_ERR(cgrp))
11241128
return PTR_ERR(cgrp);
@@ -1133,6 +1137,7 @@ static int bpf_prog_detach(const union bpf_attr *attr)
11331137

11341138
return ret;
11351139
}
1140+
11361141
#endif /* CONFIG_CGROUP_BPF */
11371142

11381143
#define BPF_PROG_TEST_RUN_LAST_FIELD test.duration

0 commit comments

Comments
 (0)