Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d6d4f60c authored by Lawrence Brakmo's avatar Lawrence Brakmo Committed by Alexei Starovoitov
Browse files

bpf: add selftest for tcpbpf



Added a selftest for tcpbpf (sock_ops) that checks that the appropriate
callbacks occured and that it can access tcp_sock fields and that their
values are correct.

Run with command: ./test_tcpbpf_user
Adding the flag "-d" will show why it did not pass.

Signed-off-by: default avatarLawrence Brakmo <brakmo@fb.com>
Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent d4487491
Loading
Loading
Loading
Loading
+82 −4
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@
#define BPF_ALU64	0x07	/* alu mode in double word width */

/* ld/ldx fields */
#define BPF_DW		0x18	/* double word */
#define BPF_DW		0x18	/* double word (64-bit) */
#define BPF_XADD	0xc0	/* exclusive add */

/* alu/jmp fields */
@@ -642,6 +642,14 @@ union bpf_attr {
 *     @optlen: length of optval in bytes
 *     Return: 0 or negative error
 *
 * int bpf_sock_ops_cb_flags_set(bpf_sock_ops, flags)
 *     Set callback flags for sock_ops
 *     @bpf_sock_ops: pointer to bpf_sock_ops_kern struct
 *     @flags: flags value
 *     Return: 0 for no error
 *             -EINVAL if there is no full tcp socket
 *             bits in flags that are not supported by current kernel
 *
 * int bpf_skb_adjust_room(skb, len_diff, mode, flags)
 *     Grow or shrink room in sk_buff.
 *     @skb: pointer to skb
@@ -748,7 +756,8 @@ union bpf_attr {
	FN(perf_event_read_value),	\
	FN(perf_prog_read_value),	\
	FN(getsockopt),			\
	FN(override_return),
	FN(override_return),		\
	FN(sock_ops_cb_flags_set),

/* integer value in 'imm' field of BPF_CALL instruction selects which helper
 * function eBPF program intends to call
@@ -952,8 +961,9 @@ struct bpf_map_info {
struct bpf_sock_ops {
	__u32 op;
	union {
		__u32 reply;
		__u32 replylong[4];
		__u32 args[4];		/* Optionally passed to bpf program */
		__u32 reply;		/* Returned by bpf program	    */
		__u32 replylong[4];	/* Optionally returned by bpf prog  */
	};
	__u32 family;
	__u32 remote_ip4;	/* Stored in network byte order */
@@ -968,8 +978,39 @@ struct bpf_sock_ops {
				 */
	__u32 snd_cwnd;
	__u32 srtt_us;		/* Averaged RTT << 3 in usecs */
	__u32 bpf_sock_ops_cb_flags; /* flags defined in uapi/linux/tcp.h */
	__u32 state;
	__u32 rtt_min;
	__u32 snd_ssthresh;
	__u32 rcv_nxt;
	__u32 snd_nxt;
	__u32 snd_una;
	__u32 mss_cache;
	__u32 ecn_flags;
	__u32 rate_delivered;
	__u32 rate_interval_us;
	__u32 packets_out;
	__u32 retrans_out;
	__u32 total_retrans;
	__u32 segs_in;
	__u32 data_segs_in;
	__u32 segs_out;
	__u32 data_segs_out;
	__u32 lost_out;
	__u32 sacked_out;
	__u32 sk_txhash;
	__u64 bytes_received;
	__u64 bytes_acked;
};

/* Definitions for bpf_sock_ops_cb_flags */
#define BPF_SOCK_OPS_RTO_CB_FLAG	(1<<0)
#define BPF_SOCK_OPS_RETRANS_CB_FLAG	(1<<1)
#define BPF_SOCK_OPS_STATE_CB_FLAG	(1<<2)
#define BPF_SOCK_OPS_ALL_CB_FLAGS       0x7		/* Mask of all currently
							 * supported cb flags
							 */

/* List of known BPF sock_ops operators.
 * New entries can only be added at the end
 */
@@ -1003,6 +1044,43 @@ enum {
					 * a congestion threshold. RTTs above
					 * this indicate congestion
					 */
	BPF_SOCK_OPS_RTO_CB,		/* Called when an RTO has triggered.
					 * Arg1: value of icsk_retransmits
					 * Arg2: value of icsk_rto
					 * Arg3: whether RTO has expired
					 */
	BPF_SOCK_OPS_RETRANS_CB,	/* Called when skb is retransmitted.
					 * Arg1: sequence number of 1st byte
					 * Arg2: # segments
					 * Arg3: return value of
					 *       tcp_transmit_skb (0 => success)
					 */
	BPF_SOCK_OPS_STATE_CB,		/* Called when TCP changes state.
					 * Arg1: old_state
					 * Arg2: new_state
					 */
};

/* List of TCP states. There is a build check in net/ipv4/tcp.c to detect
 * changes between the TCP and BPF versions. Ideally this should never happen.
 * If it does, we need to add code to convert them before calling
 * the BPF sock_ops function.
 */
enum {
	BPF_TCP_ESTABLISHED = 1,
	BPF_TCP_SYN_SENT,
	BPF_TCP_SYN_RECV,
	BPF_TCP_FIN_WAIT1,
	BPF_TCP_FIN_WAIT2,
	BPF_TCP_TIME_WAIT,
	BPF_TCP_CLOSE,
	BPF_TCP_CLOSE_WAIT,
	BPF_TCP_LAST_ACK,
	BPF_TCP_LISTEN,
	BPF_TCP_CLOSING,	/* Now a valid state */
	BPF_TCP_NEW_SYN_RECV,

	BPF_TCP_MAX_STATES	/* Leave at the end! */
};

#define TCP_BPF_IW		1001	/* Set TCP initial congestion window */
+2 −2
Original line number Diff line number Diff line
@@ -14,13 +14,13 @@ CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../i
LDLIBS += -lcap -lelf -lrt

TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
	test_align test_verifier_log test_dev_cgroup
	test_align test_verifier_log test_dev_cgroup test_tcpbpf_user

TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \
	test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o     \
	sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o test_tracepoint.o \
	test_l4lb_noinline.o test_xdp_noinline.o test_stacktrace_map.o \
	sample_map_ret0.o
	sample_map_ret0.o test_tcpbpf_kern.o

TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh \
	test_offload.py
+2 −0
Original line number Diff line number Diff line
@@ -71,6 +71,8 @@ static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval,
static int (*bpf_getsockopt)(void *ctx, int level, int optname, void *optval,
			     int optlen) =
	(void *) BPF_FUNC_getsockopt;
static int (*bpf_sock_ops_cb_flags_set)(void *ctx, int flags) =
	(void *) BPF_FUNC_sock_ops_cb_flags_set;
static int (*bpf_sk_redirect_map)(void *ctx, void *map, int key, int flags) =
	(void *) BPF_FUNC_sk_redirect_map;
static int (*bpf_sock_map_update)(void *map, void *key, void *value,
+51 −0
Original line number Diff line number Diff line
#!/usr/bin/env python2
#
# SPDX-License-Identifier: GPL-2.0
#

import sys, os, os.path, getopt
import socket, time
import subprocess
import select

def read(sock, n):
    buf = ''
    while len(buf) < n:
        rem = n - len(buf)
        try: s = sock.recv(rem)
        except (socket.error), e: return ''
        buf += s
    return buf

def send(sock, s):
    total = len(s)
    count = 0
    while count < total:
        try: n = sock.send(s)
        except (socket.error), e: n = 0
        if n == 0:
            return count;
        count += n
    return count


serverPort = int(sys.argv[1])
HostName = socket.gethostname()

# create active socket
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
try:
    sock.connect((HostName, serverPort))
except socket.error as e:
    sys.exit(1)

buf = ''
n = 0
while n < 1000:
    buf += '+'
    n += 1

sock.settimeout(1);
n = send(sock, buf)
n = read(sock, 500)
sys.exit(0)
+83 −0
Original line number Diff line number Diff line
#!/usr/bin/env python2
#
# SPDX-License-Identifier: GPL-2.0
#

import sys, os, os.path, getopt
import socket, time
import subprocess
import select

def read(sock, n):
    buf = ''
    while len(buf) < n:
        rem = n - len(buf)
        try: s = sock.recv(rem)
        except (socket.error), e: return ''
        buf += s
    return buf

def send(sock, s):
    total = len(s)
    count = 0
    while count < total:
        try: n = sock.send(s)
        except (socket.error), e: n = 0
        if n == 0:
            return count;
        count += n
    return count


SERVER_PORT = 12877
MAX_PORTS = 2

serverPort = SERVER_PORT
serverSocket = None

HostName = socket.gethostname()

# create passive socket
serverSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
host = socket.gethostname()

try: serverSocket.bind((host, 0))
except socket.error as msg:
    print 'bind fails: ', msg

sn = serverSocket.getsockname()
serverPort = sn[1]

cmdStr = ("./tcp_client.py %d &") % (serverPort)
os.system(cmdStr)

buf = ''
n = 0
while n < 500:
    buf += '.'
    n += 1

serverSocket.listen(MAX_PORTS)
readList = [serverSocket]

while True:
    readyRead, readyWrite, inError = \
        select.select(readList, [], [], 2)

    if len(readyRead) > 0:
        waitCount = 0
        for sock in readyRead:
            if sock == serverSocket:
                (clientSocket, address) = serverSocket.accept()
                address = str(address[0])
                readList.append(clientSocket)
            else:
                sock.settimeout(1);
                s = read(sock, 1000)
                n = send(sock, buf)
                sock.close()
                serverSocket.close()
                sys.exit(0)
    else:
        print 'Select timeout!'
        sys.exit(1)
Loading