1 #include <linux/net_tstamp.h> 2 #include <sys/time.h> 3 #include <linux/errqueue.h> 4 #include "test_progs.h" 5 #include "network_helpers.h" 6 #include "net_timestamping.skel.h" 7 8 #define CG_NAME "/net-timestamping-test" 9 #define NSEC_PER_SEC 1000000000LL 10 11 static const char addr4_str[] = "127.0.0.1"; 12 static const char addr6_str[] = "::1"; 13 static struct net_timestamping *skel; 14 static const int cfg_payload_len = 30; 15 static struct timespec usr_ts; 16 static u64 delay_tolerance_nsec = 10000000000; /* 10 seconds */ 17 int SK_TS_SCHED; 18 int SK_TS_TXSW; 19 int SK_TS_ACK; 20 timespec_to_ns64(struct timespec * ts)21 static int64_t timespec_to_ns64(struct timespec *ts) 22 { 23 return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec; 24 } 25 validate_key(int tskey,int tstype)26 static void validate_key(int tskey, int tstype) 27 { 28 static int expected_tskey = -1; 29 30 if (tstype == SCM_TSTAMP_SCHED) 31 expected_tskey = cfg_payload_len - 1; 32 33 ASSERT_EQ(expected_tskey, tskey, "tskey mismatch"); 34 35 expected_tskey = tskey; 36 } 37 validate_timestamp(struct timespec * cur,struct timespec * prev)38 static void validate_timestamp(struct timespec *cur, struct timespec *prev) 39 { 40 int64_t cur_ns, prev_ns; 41 42 cur_ns = timespec_to_ns64(cur); 43 prev_ns = timespec_to_ns64(prev); 44 45 ASSERT_LT(cur_ns - prev_ns, delay_tolerance_nsec, "latency"); 46 } 47 test_socket_timestamp(struct scm_timestamping * tss,int tstype,int tskey)48 static void test_socket_timestamp(struct scm_timestamping *tss, int tstype, 49 int tskey) 50 { 51 static struct timespec prev_ts; 52 53 validate_key(tskey, tstype); 54 55 switch (tstype) { 56 case SCM_TSTAMP_SCHED: 57 validate_timestamp(&tss->ts[0], &usr_ts); 58 SK_TS_SCHED += 1; 59 break; 60 case SCM_TSTAMP_SND: 61 validate_timestamp(&tss->ts[0], &prev_ts); 62 SK_TS_TXSW += 1; 63 break; 64 case SCM_TSTAMP_ACK: 65 validate_timestamp(&tss->ts[0], &prev_ts); 66 SK_TS_ACK += 1; 67 break; 68 } 69 70 prev_ts = tss->ts[0]; 71 } 72 test_recv_errmsg_cmsg(struct msghdr * msg)73 static void test_recv_errmsg_cmsg(struct msghdr *msg) 74 { 75 struct sock_extended_err *serr = NULL; 76 struct scm_timestamping *tss = NULL; 77 struct cmsghdr *cm; 78 79 for (cm = CMSG_FIRSTHDR(msg); 80 cm && cm->cmsg_len; 81 cm = CMSG_NXTHDR(msg, cm)) { 82 if (cm->cmsg_level == SOL_SOCKET && 83 cm->cmsg_type == SCM_TIMESTAMPING) { 84 tss = (void *)CMSG_DATA(cm); 85 } else if ((cm->cmsg_level == SOL_IP && 86 cm->cmsg_type == IP_RECVERR) || 87 (cm->cmsg_level == SOL_IPV6 && 88 cm->cmsg_type == IPV6_RECVERR) || 89 (cm->cmsg_level == SOL_PACKET && 90 cm->cmsg_type == PACKET_TX_TIMESTAMP)) { 91 serr = (void *)CMSG_DATA(cm); 92 ASSERT_EQ(serr->ee_origin, SO_EE_ORIGIN_TIMESTAMPING, 93 "cmsg type"); 94 } 95 96 if (serr && tss) 97 test_socket_timestamp(tss, serr->ee_info, 98 serr->ee_data); 99 } 100 } 101 socket_recv_errmsg(int fd)102 static bool socket_recv_errmsg(int fd) 103 { 104 static char ctrl[1024 /* overprovision*/]; 105 char data[cfg_payload_len]; 106 static struct msghdr msg; 107 struct iovec entry; 108 int n = 0; 109 110 memset(&msg, 0, sizeof(msg)); 111 memset(&entry, 0, sizeof(entry)); 112 memset(ctrl, 0, sizeof(ctrl)); 113 114 entry.iov_base = data; 115 entry.iov_len = cfg_payload_len; 116 msg.msg_iov = &entry; 117 msg.msg_iovlen = 1; 118 msg.msg_name = NULL; 119 msg.msg_namelen = 0; 120 msg.msg_control = ctrl; 121 msg.msg_controllen = sizeof(ctrl); 122 123 n = recvmsg(fd, &msg, MSG_ERRQUEUE); 124 if (n == -1) 125 ASSERT_EQ(errno, EAGAIN, "recvmsg MSG_ERRQUEUE"); 126 127 if (n >= 0) 128 test_recv_errmsg_cmsg(&msg); 129 130 return n == -1; 131 } 132 test_socket_timestamping(int fd)133 static void test_socket_timestamping(int fd) 134 { 135 while (!socket_recv_errmsg(fd)); 136 137 ASSERT_EQ(SK_TS_SCHED, 1, "SCM_TSTAMP_SCHED"); 138 ASSERT_EQ(SK_TS_TXSW, 1, "SCM_TSTAMP_SND"); 139 ASSERT_EQ(SK_TS_ACK, 1, "SCM_TSTAMP_ACK"); 140 141 SK_TS_SCHED = 0; 142 SK_TS_TXSW = 0; 143 SK_TS_ACK = 0; 144 } 145 test_tcp(int family,bool enable_socket_timestamping)146 static void test_tcp(int family, bool enable_socket_timestamping) 147 { 148 struct net_timestamping__bss *bss; 149 char buf[cfg_payload_len]; 150 int sfd = -1, cfd = -1; 151 unsigned int sock_opt; 152 struct netns_obj *ns; 153 int cg_fd; 154 int ret; 155 156 cg_fd = test__join_cgroup(CG_NAME); 157 if (!ASSERT_OK_FD(cg_fd, "join cgroup")) 158 return; 159 160 ns = netns_new("net_timestamping_ns", true); 161 if (!ASSERT_OK_PTR(ns, "create ns")) 162 goto out; 163 164 skel = net_timestamping__open_and_load(); 165 if (!ASSERT_OK_PTR(skel, "open and load skel")) 166 goto out; 167 168 if (!ASSERT_OK(net_timestamping__attach(skel), "attach skel")) 169 goto out; 170 171 skel->links.skops_sockopt = 172 bpf_program__attach_cgroup(skel->progs.skops_sockopt, cg_fd); 173 if (!ASSERT_OK_PTR(skel->links.skops_sockopt, "attach cgroup")) 174 goto out; 175 176 bss = skel->bss; 177 memset(bss, 0, sizeof(*bss)); 178 179 skel->bss->monitored_pid = getpid(); 180 181 sfd = start_server(family, SOCK_STREAM, 182 family == AF_INET6 ? addr6_str : addr4_str, 0, 0); 183 if (!ASSERT_OK_FD(sfd, "start_server")) 184 goto out; 185 186 cfd = connect_to_fd(sfd, 0); 187 if (!ASSERT_OK_FD(cfd, "connect_to_fd_server")) 188 goto out; 189 190 if (enable_socket_timestamping) { 191 sock_opt = SOF_TIMESTAMPING_SOFTWARE | 192 SOF_TIMESTAMPING_OPT_ID | 193 SOF_TIMESTAMPING_TX_SCHED | 194 SOF_TIMESTAMPING_TX_SOFTWARE | 195 SOF_TIMESTAMPING_TX_ACK; 196 ret = setsockopt(cfd, SOL_SOCKET, SO_TIMESTAMPING, 197 (char *) &sock_opt, sizeof(sock_opt)); 198 if (!ASSERT_OK(ret, "setsockopt SO_TIMESTAMPING")) 199 goto out; 200 201 ret = clock_gettime(CLOCK_REALTIME, &usr_ts); 202 if (!ASSERT_OK(ret, "get user time")) 203 goto out; 204 } 205 206 ret = write(cfd, buf, sizeof(buf)); 207 if (!ASSERT_EQ(ret, sizeof(buf), "send to server")) 208 goto out; 209 210 if (enable_socket_timestamping) 211 test_socket_timestamping(cfd); 212 213 ASSERT_EQ(bss->nr_active, 1, "nr_active"); 214 ASSERT_EQ(bss->nr_snd, 2, "nr_snd"); 215 ASSERT_EQ(bss->nr_sched, 1, "nr_sched"); 216 ASSERT_EQ(bss->nr_txsw, 1, "nr_txsw"); 217 ASSERT_EQ(bss->nr_ack, 1, "nr_ack"); 218 219 out: 220 if (sfd >= 0) 221 close(sfd); 222 if (cfd >= 0) 223 close(cfd); 224 net_timestamping__destroy(skel); 225 netns_free(ns); 226 close(cg_fd); 227 } 228 test_net_timestamping(void)229 void test_net_timestamping(void) 230 { 231 if (test__start_subtest("INET4: bpf timestamping")) 232 test_tcp(AF_INET, false); 233 if (test__start_subtest("INET4: bpf and socket timestamping")) 234 test_tcp(AF_INET, true); 235 if (test__start_subtest("INET6: bpf timestamping")) 236 test_tcp(AF_INET6, false); 237 if (test__start_subtest("INET6: bpf and socket timestamping")) 238 test_tcp(AF_INET6, true); 239 } 240