1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 3 /* 4 * BPF-based flow shaping 5 * 6 * The test brings up two veth in two isolated namespaces, attach some flow 7 * shaping program onto it, and ensures that a manual speedtest maximum 8 * value matches the rate set in the BPF shapers. 9 */ 10 11 #include <asm-generic/socket.h> 12 #include <stdio.h> 13 #include <unistd.h> 14 #include <fcntl.h> 15 #include <math.h> 16 #include <sys/time.h> 17 #include <sys/socket.h> 18 #include <bpf/libbpf.h> 19 #include <pthread.h> 20 #include "test_progs.h" 21 #include "network_helpers.h" 22 #include "test_tc_edt.skel.h" 23 24 #define SERVER_NS "tc-edt-server-ns" 25 #define CLIENT_NS "tc-edt-client-ns" 26 #define IP4_ADDR_VETH1 "192.168.1.1" 27 #define IP4_ADDR_VETH2 "192.168.1.2" 28 #define IP4_ADDR_VETH2_HEX 0xC0A80102 29 30 #define TIMEOUT_MS 2000 31 #define TEST_PORT 9000 32 #define TARGET_RATE_MBPS 5.0 33 #define TX_BYTES_COUNT (1 * 1000 * 1000) 34 #define RATE_ERROR_PERCENT 2.0 35 36 struct connection { 37 int server_listen_fd; 38 int server_conn_fd; 39 int client_conn_fd; 40 }; 41 42 static int setup(struct test_tc_edt *skel) 43 { 44 struct nstoken *nstoken_client, *nstoken_server; 45 int ret; 46 47 if (!ASSERT_OK(make_netns(CLIENT_NS), "create client ns")) 48 goto fail; 49 if (!ASSERT_OK(make_netns(SERVER_NS), "create server ns")) 50 goto fail_delete_client_ns; 51 52 nstoken_client = open_netns(CLIENT_NS); 53 if (!ASSERT_OK_PTR(nstoken_client, "open client ns")) 54 goto fail_delete_server_ns; 55 SYS(fail_close_client_ns, "ip link add veth1 type veth peer name %s", 56 "veth2 netns " SERVER_NS); 57 SYS(fail_close_client_ns, "ip -4 addr add " IP4_ADDR_VETH1 "/24 dev veth1"); 58 SYS(fail_close_client_ns, "ip link set veth1 up"); 59 60 nstoken_server = open_netns(SERVER_NS); 61 if (!ASSERT_OK_PTR(nstoken_server, "enter server ns")) 62 goto fail_close_client_ns; 63 SYS(fail_close_server_ns, "ip -4 addr add " IP4_ADDR_VETH2 "/24 dev veth2"); 64 SYS(fail_close_server_ns, "ip link set veth2 up"); 65 SYS(fail_close_server_ns, "tc qdisc add dev veth2 root fq"); 66 ret = tc_prog_attach("veth2", -1, bpf_program__fd(skel->progs.tc_prog)); 67 if (!ASSERT_OK(ret, "attach bpf prog")) 68 goto fail_close_server_ns; 69 skel->bss->target_rate = TARGET_RATE_MBPS * 1000 * 1000; 70 close_netns(nstoken_server); 71 close_netns(nstoken_client); 72 73 return 0; 74 75 fail_close_server_ns: 76 close_netns(nstoken_server); 77 fail_close_client_ns: 78 close_netns(nstoken_client); 79 fail_delete_server_ns: 80 remove_netns(SERVER_NS); 81 fail_delete_client_ns: 82 remove_netns(CLIENT_NS); 83 fail: 84 return -1; 85 } 86 87 static void cleanup(void) 88 { 89 remove_netns(CLIENT_NS); 90 remove_netns(SERVER_NS); 91 } 92 93 static void run_test(void) 94 { 95 int server_fd, client_fd, err; 96 double rate_mbps, rate_error; 97 struct nstoken *nstoken; 98 __u64 ts_start, ts_end; 99 100 nstoken = open_netns(SERVER_NS); 101 if (!ASSERT_OK_PTR(nstoken, "open server ns")) 102 return; 103 server_fd = start_server(AF_INET, SOCK_STREAM, IP4_ADDR_VETH2, 104 TEST_PORT, TIMEOUT_MS); 105 if (!ASSERT_OK_FD(server_fd, "start server")) 106 return; 107 108 close_netns(nstoken); 109 nstoken = open_netns(CLIENT_NS); 110 if (!ASSERT_OK_PTR(nstoken, "open client ns")) 111 return; 112 client_fd = connect_to_fd(server_fd, 0); 113 if (!ASSERT_OK_FD(client_fd, "connect client")) 114 return; 115 116 ts_start = get_time_ns(); 117 err = send_recv_data(server_fd, client_fd, TX_BYTES_COUNT); 118 ts_end = get_time_ns(); 119 close_netns(nstoken); 120 ASSERT_OK(err, "send_recv_data"); 121 122 rate_mbps = TX_BYTES_COUNT / ((ts_end - ts_start) / 1000.0); 123 rate_error = 124 fabs((rate_mbps - TARGET_RATE_MBPS) * 100.0 / TARGET_RATE_MBPS); 125 126 ASSERT_LE(rate_error, RATE_ERROR_PERCENT, 127 "rate error is lower than threshold"); 128 } 129 130 void test_tc_edt(void) 131 { 132 struct test_tc_edt *skel; 133 134 skel = test_tc_edt__open_and_load(); 135 if (!ASSERT_OK_PTR(skel, "skel open and load")) 136 return; 137 138 if (!ASSERT_OK(setup(skel), "global setup")) 139 return; 140 141 run_test(); 142 143 cleanup(); 144 test_tc_edt__destroy(skel); 145 } 146