1 // SPDX-License-Identifier: GPL-2.0 2 #include <test_progs.h> 3 #include <network_helpers.h> 4 #include <cgroup_helpers.h> 5 #include <linux/errqueue.h> 6 #include <poll.h> 7 #include <unistd.h> 8 #include "icmp_send.skel.h" 9 10 #define TIMEOUT_MS 1000 11 12 #define ICMP_DEST_UNREACH 3 13 #define ICMPV6_DEST_UNREACH 1 14 15 #define ICMP_HOST_UNREACH 1 16 #define ICMP_FRAG_NEEDED 4 17 #define NR_ICMP_UNREACH 15 18 #define ICMPV6_REJECT_ROUTE 6 19 20 #define KFUNC_RET_UNSET -1 21 22 static int connect_to_fd_nonblock(int server_fd) 23 { 24 struct sockaddr_storage addr; 25 socklen_t len = sizeof(addr); 26 int fd, err, on = 1; 27 28 if (getsockname(server_fd, (struct sockaddr *)&addr, &len)) 29 return -1; 30 31 fd = socket(addr.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0); 32 if (fd < 0) 33 return -1; 34 35 if (addr.ss_family == AF_INET6 && 36 setsockopt(fd, IPPROTO_IPV6, IPV6_RECVERR, &on, sizeof(on)) < 0) { 37 close(fd); 38 return -1; 39 } 40 41 err = connect(fd, (struct sockaddr *)&addr, len); 42 if (err < 0 && errno != EINPROGRESS) { 43 close(fd); 44 return -1; 45 } 46 47 return fd; 48 } 49 50 static void read_icmp_errqueue(int sockfd, int expected_code, int af) 51 { 52 int expected_ee_type = (af == AF_INET) ? ICMP_DEST_UNREACH : 53 ICMPV6_DEST_UNREACH; 54 int expected_origin = (af == AF_INET) ? SO_EE_ORIGIN_ICMP : 55 SO_EE_ORIGIN_ICMP6; 56 int expected_level = (af == AF_INET) ? IPPROTO_IP : IPPROTO_IPV6; 57 int expected_type = (af == AF_INET) ? IP_RECVERR : IPV6_RECVERR; 58 struct sock_extended_err *sock_err; 59 char ctrl_buf[512]; 60 struct msghdr msg = { 61 .msg_control = ctrl_buf, 62 .msg_controllen = sizeof(ctrl_buf), 63 }; 64 struct pollfd pfd = { 65 .fd = sockfd, 66 .events = POLLERR, 67 }; 68 struct cmsghdr *cm; 69 ssize_t n; 70 71 if (!ASSERT_GE(poll(&pfd, 1, TIMEOUT_MS), 1, "poll_errqueue")) 72 return; 73 74 n = recvmsg(sockfd, &msg, MSG_ERRQUEUE); 75 if (!ASSERT_GE(n, 0, "recvmsg_errqueue")) 76 return; 77 78 cm = CMSG_FIRSTHDR(&msg); 79 if (!ASSERT_NEQ(cm, NULL, "cm_firsthdr_null")) 80 return; 81 82 for (; cm; cm = CMSG_NXTHDR(&msg, cm)) { 83 if (cm->cmsg_level != expected_level || 84 cm->cmsg_type != expected_type) 85 continue; 86 87 sock_err = (struct sock_extended_err *)CMSG_DATA(cm); 88 89 if (!ASSERT_EQ(sock_err->ee_origin, expected_origin, 90 "sock_err_origin")) 91 return; 92 if (!ASSERT_EQ(sock_err->ee_type, expected_ee_type, 93 "sock_err_type_dest_unreach")) 94 return; 95 ASSERT_EQ(sock_err->ee_code, expected_code, "sock_err_code"); 96 return; 97 } 98 99 ASSERT_FAIL("no IP_RECVERR/IPV6_RECVERR control message found"); 100 } 101 102 static bool valid_unreach_code(int code, int af) 103 { 104 if (code < 0) 105 return false; 106 107 if (af == AF_INET) 108 return code <= NR_ICMP_UNREACH && code != ICMP_FRAG_NEEDED; 109 110 return code <= ICMPV6_REJECT_ROUTE; 111 } 112 113 static void trigger_prog_read_icmp_errqueue(struct icmp_send *skel, int code, 114 int af, const char *ip) 115 { 116 int srv_fd = -1, client_fd = -1; 117 int port; 118 119 srv_fd = start_server(af, SOCK_STREAM, ip, 0, TIMEOUT_MS); 120 if (!ASSERT_OK_FD(srv_fd, "start_server")) 121 return; 122 123 port = get_socket_local_port(srv_fd); 124 if (!ASSERT_GE(port, 0, "get_socket_local_port")) { 125 close(srv_fd); 126 return; 127 } 128 129 skel->bss->server_port = ntohs(port); 130 skel->bss->unreach_type = (af == AF_INET) ? ICMP_DEST_UNREACH : 131 ICMPV6_DEST_UNREACH; 132 skel->bss->unreach_code = code; 133 skel->data->kfunc_ret = KFUNC_RET_UNSET; 134 135 client_fd = connect_to_fd_nonblock(srv_fd); 136 if (!ASSERT_OK_FD(client_fd, "client_connect_nonblock")) { 137 close(srv_fd); 138 return; 139 } 140 141 if (valid_unreach_code(code, af)) 142 read_icmp_errqueue(client_fd, code, af); 143 144 close(client_fd); 145 close(srv_fd); 146 } 147 148 static void run_icmp_test(struct icmp_send *skel, int af, const char *ip, 149 int max_code) 150 { 151 for (int code = 0; code <= max_code; code++) { 152 if (af == AF_INET && code == ICMP_FRAG_NEEDED) 153 continue; 154 155 trigger_prog_read_icmp_errqueue(skel, code, af, ip); 156 ASSERT_EQ(skel->data->kfunc_ret, 0, "kfunc_ret"); 157 } 158 159 /* Test invalid codes */ 160 trigger_prog_read_icmp_errqueue(skel, -1, af, ip); 161 ASSERT_EQ(skel->data->kfunc_ret, -EINVAL, "kfunc_ret"); 162 163 trigger_prog_read_icmp_errqueue(skel, max_code + 1, af, ip); 164 ASSERT_EQ(skel->data->kfunc_ret, -EINVAL, "kfunc_ret"); 165 166 if (af == AF_INET) { 167 trigger_prog_read_icmp_errqueue(skel, ICMP_FRAG_NEEDED, af, ip); 168 ASSERT_EQ(skel->data->kfunc_ret, -EINVAL, "kfunc_ret"); 169 } 170 } 171 172 static void run_icmp_no_route_test(struct icmp_send *skel, int af) 173 { 174 union { 175 struct ipv4_packet v4; 176 struct ipv6_packet v6; 177 } pkt; 178 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts, 179 .data_in = &pkt, 180 ); 181 int err; 182 183 switch (af) { 184 case AF_INET: 185 pkt.v4 = pkt_v4; 186 pkt.v4.iph.version = 4; 187 pkt.v4.iph.daddr = htonl(INADDR_LOOPBACK); 188 pkt.v4.tcp.dest = htons(80); 189 opts.data_size_in = sizeof(pkt.v4); 190 skel->bss->unreach_type = ICMP_DEST_UNREACH; 191 break; 192 case AF_INET6: 193 pkt.v6 = pkt_v6; 194 pkt.v6.iph.version = 6; 195 pkt.v6.iph.daddr = in6addr_loopback; 196 pkt.v6.tcp.dest = htons(80); 197 opts.data_size_in = sizeof(pkt.v6); 198 skel->bss->unreach_type = ICMPV6_DEST_UNREACH; 199 break; 200 default: 201 ASSERT_FAIL("af_not_supported"); 202 return; 203 } 204 205 skel->bss->server_port = 80; 206 skel->data->kfunc_ret = KFUNC_RET_UNSET; 207 208 err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.egress), &opts); 209 if (!ASSERT_OK(err, "test_run")) 210 return; 211 212 ASSERT_EQ(skel->data->kfunc_ret, -ENETUNREACH, "kfunc_ret_no_route"); 213 } 214 215 void test_icmp_send_unreach_cgroup(void) 216 { 217 struct icmp_send *skel; 218 int cgroup_fd = -1; 219 220 skel = icmp_send__open_and_load(); 221 if (!ASSERT_OK_PTR(skel, "skel_open")) 222 goto cleanup; 223 224 cgroup_fd = test__join_cgroup("/icmp_send_unreach_cgroup"); 225 if (!ASSERT_OK_FD(cgroup_fd, "join_cgroup")) 226 goto cleanup; 227 228 skel->links.egress = 229 bpf_program__attach_cgroup(skel->progs.egress, cgroup_fd); 230 if (!ASSERT_OK_PTR(skel->links.egress, "prog_attach_cgroup")) 231 goto cleanup; 232 233 if (test__start_subtest("ipv4")) 234 run_icmp_test(skel, AF_INET, "127.0.0.1", NR_ICMP_UNREACH); 235 236 if (test__start_subtest("ipv6")) 237 run_icmp_test(skel, AF_INET6, "::1", ICMPV6_REJECT_ROUTE); 238 239 if (test__start_subtest("no_route_ipv4")) 240 run_icmp_no_route_test(skel, AF_INET); 241 242 if (test__start_subtest("no_route_ipv6")) 243 run_icmp_no_route_test(skel, AF_INET6); 244 245 cleanup: 246 icmp_send__destroy(skel); 247 if (cgroup_fd >= 0) 248 close(cgroup_fd); 249 } 250 251 void test_icmp_send_unreach_recursion(void) 252 { 253 struct icmp_send *skel; 254 int cgroup_fd = -1; 255 int err; 256 257 err = setup_cgroup_environment(); 258 if (!ASSERT_OK(err, "setup_cgroup_environment")) 259 return; 260 261 skel = icmp_send__open_and_load(); 262 if (!ASSERT_OK_PTR(skel, "skel_open")) 263 goto cleanup; 264 265 cgroup_fd = get_root_cgroup(); 266 if (!ASSERT_OK_FD(cgroup_fd, "get_root_cgroup")) 267 goto cleanup; 268 269 skel->data->target_pid = getpid(); 270 skel->links.recursion = 271 bpf_program__attach_cgroup(skel->progs.recursion, cgroup_fd); 272 if (!ASSERT_OK_PTR(skel->links.recursion, "prog_attach_cgroup")) 273 goto cleanup; 274 275 trigger_prog_read_icmp_errqueue(skel, ICMP_HOST_UNREACH, AF_INET, 276 "127.0.0.1"); 277 278 /* 279 * Because there's recursion involved, the first call will return at 280 * index 1 since it will return the second, and the second call will 281 * return at index 0 since it will return the first. 282 */ 283 ASSERT_EQ(skel->bss->rec_count, 2, "rec_count"); 284 ASSERT_EQ(skel->data->rec_kfunc_rets[0], -EBUSY, "kfunc_rets[0]"); 285 ASSERT_EQ(skel->data->rec_kfunc_rets[1], 0, "kfunc_rets[1]"); 286 287 cleanup: 288 icmp_send__destroy(skel); 289 if (cgroup_fd >= 0) 290 close(cgroup_fd); 291 cleanup_cgroup_environment(); 292 } 293