1// SPDX-License-Identifier: GPL-2.0 2 3#define _GNU_SOURCE 4 5#include <time.h> 6#include <libmnl/libmnl.h> 7#include <netinet/ip.h> 8 9#include <linux/netlink.h> 10#include <linux/netfilter/nfnetlink.h> 11#include <linux/netfilter/nfnetlink_conntrack.h> 12#include <linux/netfilter/nf_conntrack_tcp.h> 13#include "../../kselftest_harness.h" 14 15#define TEST_ZONE_ID 123 16#define NF_CT_DEFAULT_ZONE_ID 0 17 18static int reply_counter; 19 20static int build_cta_tuple_v4(struct nlmsghdr *nlh, int type, 21 uint32_t src_ip, uint32_t dst_ip, 22 uint16_t src_port, uint16_t dst_port) 23{ 24 struct nlattr *nest, *nest_ip, *nest_proto; 25 26 nest = mnl_attr_nest_start(nlh, type); 27 if (!nest) 28 return -1; 29 30 nest_ip = mnl_attr_nest_start(nlh, CTA_TUPLE_IP); 31 if (!nest_ip) 32 return -1; 33 mnl_attr_put_u32(nlh, CTA_IP_V4_SRC, src_ip); 34 mnl_attr_put_u32(nlh, CTA_IP_V4_DST, dst_ip); 35 mnl_attr_nest_end(nlh, nest_ip); 36 37 nest_proto = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO); 38 if (!nest_proto) 39 return -1; 40 mnl_attr_put_u8(nlh, CTA_PROTO_NUM, 6); 41 mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, htons(src_port)); 42 mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, htons(dst_port)); 43 mnl_attr_nest_end(nlh, nest_proto); 44 45 mnl_attr_nest_end(nlh, nest); 46} 47 48static int build_cta_tuple_v6(struct nlmsghdr *nlh, int type, 49 struct in6_addr src_ip, struct in6_addr dst_ip, 50 uint16_t src_port, uint16_t dst_port) 51{ 52 struct nlattr *nest, *nest_ip, *nest_proto; 53 54 nest = mnl_attr_nest_start(nlh, type); 55 if (!nest) 56 return -1; 57 58 nest_ip = mnl_attr_nest_start(nlh, CTA_TUPLE_IP); 59 if (!nest_ip) 60 return -1; 61 mnl_attr_put(nlh, CTA_IP_V6_SRC, sizeof(struct in6_addr), &src_ip); 62 mnl_attr_put(nlh, CTA_IP_V6_DST, sizeof(struct in6_addr), &dst_ip); 63 mnl_attr_nest_end(nlh, nest_ip); 64 65 nest_proto = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO); 66 if (!nest_proto) 67 return -1; 68 mnl_attr_put_u8(nlh, CTA_PROTO_NUM, 6); 69 mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, htons(src_port)); 70 mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, htons(dst_port)); 71 mnl_attr_nest_end(nlh, nest_proto); 72 73 mnl_attr_nest_end(nlh, nest); 74} 75 76static int build_cta_proto(struct nlmsghdr *nlh) 77{ 78 struct nlattr *nest, *nest_proto; 79 80 nest = mnl_attr_nest_start(nlh, CTA_PROTOINFO); 81 if (!nest) 82 return -1; 83 84 nest_proto = mnl_attr_nest_start(nlh, CTA_PROTOINFO_TCP); 85 if (!nest_proto) 86 return -1; 87 mnl_attr_put_u8(nlh, CTA_PROTOINFO_TCP_STATE, TCP_CONNTRACK_ESTABLISHED); 88 mnl_attr_put_u16(nlh, CTA_PROTOINFO_TCP_FLAGS_ORIGINAL, 0x0a0a); 89 mnl_attr_put_u16(nlh, CTA_PROTOINFO_TCP_FLAGS_REPLY, 0x0a0a); 90 mnl_attr_nest_end(nlh, nest_proto); 91 92 mnl_attr_nest_end(nlh, nest); 93} 94 95static int conntrack_data_insert(struct mnl_socket *sock, struct nlmsghdr *nlh, 96 uint16_t zone) 97{ 98 char buf[MNL_SOCKET_BUFFER_SIZE]; 99 struct nlmsghdr *rplnlh; 100 unsigned int portid; 101 int err, ret; 102 103 portid = mnl_socket_get_portid(sock); 104 105 ret = build_cta_proto(nlh); 106 if (ret < 0) { 107 perror("build_cta_proto"); 108 return -1; 109 } 110 mnl_attr_put_u32(nlh, CTA_TIMEOUT, htonl(20000)); 111 mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone)); 112 113 if (mnl_socket_sendto(sock, nlh, nlh->nlmsg_len) < 0) { 114 perror("mnl_socket_sendto"); 115 return -1; 116 } 117 118 ret = mnl_socket_recvfrom(sock, buf, MNL_SOCKET_BUFFER_SIZE); 119 if (ret < 0) { 120 perror("mnl_socket_recvfrom"); 121 return ret; 122 } 123 124 ret = mnl_cb_run(buf, ret, nlh->nlmsg_seq, portid, NULL, NULL); 125 if (ret < 0) { 126 if (errno == EEXIST) { 127 /* The entries are probably still there from a previous 128 * run. So we are good 129 */ 130 return 0; 131 } 132 perror("mnl_cb_run"); 133 return ret; 134 } 135 136 return 0; 137} 138 139static int conntrack_data_generate_v4(struct mnl_socket *sock, uint32_t src_ip, 140 uint32_t dst_ip, uint16_t zone) 141{ 142 char buf[MNL_SOCKET_BUFFER_SIZE]; 143 struct nlmsghdr *nlh; 144 struct nfgenmsg *nfh; 145 int ret; 146 147 nlh = mnl_nlmsg_put_header(buf); 148 nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW; 149 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | 150 NLM_F_ACK | NLM_F_EXCL; 151 nlh->nlmsg_seq = time(NULL); 152 153 nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); 154 nfh->nfgen_family = AF_INET; 155 nfh->version = NFNETLINK_V0; 156 nfh->res_id = 0; 157 158 ret = build_cta_tuple_v4(nlh, CTA_TUPLE_ORIG, src_ip, dst_ip, 12345, 443); 159 if (ret < 0) { 160 perror("build_cta_tuple_v4"); 161 return ret; 162 } 163 ret = build_cta_tuple_v4(nlh, CTA_TUPLE_REPLY, dst_ip, src_ip, 443, 12345); 164 if (ret < 0) { 165 perror("build_cta_tuple_v4"); 166 return ret; 167 } 168 return conntrack_data_insert(sock, nlh, zone); 169} 170 171static int conntrack_data_generate_v6(struct mnl_socket *sock, 172 struct in6_addr src_ip, 173 struct in6_addr dst_ip, 174 uint16_t zone) 175{ 176 char buf[MNL_SOCKET_BUFFER_SIZE]; 177 struct nlmsghdr *nlh; 178 struct nfgenmsg *nfh; 179 int ret; 180 181 nlh = mnl_nlmsg_put_header(buf); 182 nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW; 183 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | 184 NLM_F_ACK | NLM_F_EXCL; 185 nlh->nlmsg_seq = time(NULL); 186 187 nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); 188 nfh->nfgen_family = AF_INET6; 189 nfh->version = NFNETLINK_V0; 190 nfh->res_id = 0; 191 192 ret = build_cta_tuple_v6(nlh, CTA_TUPLE_ORIG, src_ip, dst_ip, 193 12345, 443); 194 if (ret < 0) { 195 perror("build_cta_tuple_v6"); 196 return ret; 197 } 198 ret = build_cta_tuple_v6(nlh, CTA_TUPLE_REPLY, dst_ip, src_ip, 199 12345, 443); 200 if (ret < 0) { 201 perror("build_cta_tuple_v6"); 202 return ret; 203 } 204 return conntrack_data_insert(sock, nlh, zone); 205} 206 207static int count_entries(const struct nlmsghdr *nlh, void *data) 208{ 209 reply_counter++; 210} 211 212static int conntracK_count_zone(struct mnl_socket *sock, uint16_t zone) 213{ 214 char buf[MNL_SOCKET_BUFFER_SIZE]; 215 struct nlmsghdr *nlh, *rplnlh; 216 struct nfgenmsg *nfh; 217 struct nlattr *nest; 218 unsigned int portid; 219 int err, ret; 220 221 portid = mnl_socket_get_portid(sock); 222 223 nlh = mnl_nlmsg_put_header(buf); 224 nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET; 225 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 226 nlh->nlmsg_seq = time(NULL); 227 228 nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); 229 nfh->nfgen_family = AF_UNSPEC; 230 nfh->version = NFNETLINK_V0; 231 nfh->res_id = 0; 232 233 mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone)); 234 235 ret = mnl_socket_sendto(sock, nlh, nlh->nlmsg_len); 236 if (ret < 0) { 237 perror("mnl_socket_sendto"); 238 return ret; 239 } 240 241 reply_counter = 0; 242 ret = mnl_socket_recvfrom(sock, buf, MNL_SOCKET_BUFFER_SIZE); 243 while (ret > 0) { 244 ret = mnl_cb_run(buf, ret, nlh->nlmsg_seq, portid, 245 count_entries, NULL); 246 if (ret <= MNL_CB_STOP) 247 break; 248 249 ret = mnl_socket_recvfrom(sock, buf, MNL_SOCKET_BUFFER_SIZE); 250 } 251 if (ret < 0) { 252 perror("mnl_socket_recvfrom"); 253 return ret; 254 } 255 256 return reply_counter; 257} 258 259static int conntrack_flush_zone(struct mnl_socket *sock, uint16_t zone) 260{ 261 char buf[MNL_SOCKET_BUFFER_SIZE]; 262 struct nlmsghdr *nlh, *rplnlh; 263 struct nfgenmsg *nfh; 264 struct nlattr *nest; 265 unsigned int portid; 266 int err, ret; 267 268 portid = mnl_socket_get_portid(sock); 269 270 nlh = mnl_nlmsg_put_header(buf); 271 nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_DELETE; 272 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 273 nlh->nlmsg_seq = time(NULL); 274 275 nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); 276 nfh->nfgen_family = AF_UNSPEC; 277 nfh->version = NFNETLINK_V0; 278 nfh->res_id = 0; 279 280 mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone)); 281 282 ret = mnl_socket_sendto(sock, nlh, nlh->nlmsg_len); 283 if (ret < 0) { 284 perror("mnl_socket_sendto"); 285 return ret; 286 } 287 288 ret = mnl_socket_recvfrom(sock, buf, MNL_SOCKET_BUFFER_SIZE); 289 if (ret < 0) { 290 perror("mnl_socket_recvfrom"); 291 return ret; 292 } 293 294 ret = mnl_cb_run(buf, ret, nlh->nlmsg_seq, portid, NULL, NULL); 295 if (ret < 0) { 296 perror("mnl_cb_run"); 297 return ret; 298 } 299 300 return 0; 301} 302 303FIXTURE(conntrack_dump_flush) 304{ 305 struct mnl_socket *sock; 306}; 307 308FIXTURE_SETUP(conntrack_dump_flush) 309{ 310 struct in6_addr src, dst; 311 int ret; 312 313 self->sock = mnl_socket_open(NETLINK_NETFILTER); 314 if (!self->sock) { 315 perror("mnl_socket_open"); 316 SKIP(return, "cannot open netlink_netfilter socket"); 317 } 318 319 ret = mnl_socket_bind(self->sock, 0, MNL_SOCKET_AUTOPID); 320 EXPECT_EQ(ret, 0); 321 322 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID); 323 if (ret < 0 && errno == EPERM) 324 SKIP(return, "Needs to be run as root"); 325 else if (ret < 0 && errno == EOPNOTSUPP) 326 SKIP(return, "Kernel does not seem to support conntrack zones"); 327 328 ret = conntrack_data_generate_v4(self->sock, 0xf0f0f0f0, 0xf1f1f1f1, 329 TEST_ZONE_ID); 330 EXPECT_EQ(ret, 0); 331 ret = conntrack_data_generate_v4(self->sock, 0xf2f2f2f2, 0xf3f3f3f3, 332 TEST_ZONE_ID + 1); 333 EXPECT_EQ(ret, 0); 334 ret = conntrack_data_generate_v4(self->sock, 0xf4f4f4f4, 0xf5f5f5f5, 335 TEST_ZONE_ID + 2); 336 EXPECT_EQ(ret, 0); 337 ret = conntrack_data_generate_v4(self->sock, 0xf6f6f6f6, 0xf7f7f7f7, 338 NF_CT_DEFAULT_ZONE_ID); 339 EXPECT_EQ(ret, 0); 340 341 src = (struct in6_addr) {{ 342 .__u6_addr32 = { 343 0xb80d0120, 344 0x00000000, 345 0x00000000, 346 0x01000000 347 } 348 }}; 349 dst = (struct in6_addr) {{ 350 .__u6_addr32 = { 351 0xb80d0120, 352 0x00000000, 353 0x00000000, 354 0x02000000 355 } 356 }}; 357 ret = conntrack_data_generate_v6(self->sock, src, dst, 358 TEST_ZONE_ID); 359 EXPECT_EQ(ret, 0); 360 src = (struct in6_addr) {{ 361 .__u6_addr32 = { 362 0xb80d0120, 363 0x00000000, 364 0x00000000, 365 0x03000000 366 } 367 }}; 368 dst = (struct in6_addr) {{ 369 .__u6_addr32 = { 370 0xb80d0120, 371 0x00000000, 372 0x00000000, 373 0x04000000 374 } 375 }}; 376 ret = conntrack_data_generate_v6(self->sock, src, dst, 377 TEST_ZONE_ID + 1); 378 EXPECT_EQ(ret, 0); 379 src = (struct in6_addr) {{ 380 .__u6_addr32 = { 381 0xb80d0120, 382 0x00000000, 383 0x00000000, 384 0x05000000 385 } 386 }}; 387 dst = (struct in6_addr) {{ 388 .__u6_addr32 = { 389 0xb80d0120, 390 0x00000000, 391 0x00000000, 392 0x06000000 393 } 394 }}; 395 ret = conntrack_data_generate_v6(self->sock, src, dst, 396 TEST_ZONE_ID + 2); 397 EXPECT_EQ(ret, 0); 398 399 src = (struct in6_addr) {{ 400 .__u6_addr32 = { 401 0xb80d0120, 402 0x00000000, 403 0x00000000, 404 0x07000000 405 } 406 }}; 407 dst = (struct in6_addr) {{ 408 .__u6_addr32 = { 409 0xb80d0120, 410 0x00000000, 411 0x00000000, 412 0x08000000 413 } 414 }}; 415 ret = conntrack_data_generate_v6(self->sock, src, dst, 416 NF_CT_DEFAULT_ZONE_ID); 417 EXPECT_EQ(ret, 0); 418 419 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID); 420 EXPECT_GE(ret, 2); 421 if (ret > 2) 422 SKIP(return, "kernel does not support filtering by zone"); 423} 424 425FIXTURE_TEARDOWN(conntrack_dump_flush) 426{ 427} 428 429TEST_F(conntrack_dump_flush, test_dump_by_zone) 430{ 431 int ret; 432 433 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID); 434 EXPECT_EQ(ret, 2); 435} 436 437TEST_F(conntrack_dump_flush, test_flush_by_zone) 438{ 439 int ret; 440 441 ret = conntrack_flush_zone(self->sock, TEST_ZONE_ID); 442 EXPECT_EQ(ret, 0); 443 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID); 444 EXPECT_EQ(ret, 0); 445 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 1); 446 EXPECT_EQ(ret, 2); 447 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 2); 448 EXPECT_EQ(ret, 2); 449 ret = conntracK_count_zone(self->sock, NF_CT_DEFAULT_ZONE_ID); 450 EXPECT_EQ(ret, 2); 451} 452 453TEST_F(conntrack_dump_flush, test_flush_by_zone_default) 454{ 455 int ret; 456 457 ret = conntrack_flush_zone(self->sock, NF_CT_DEFAULT_ZONE_ID); 458 EXPECT_EQ(ret, 0); 459 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID); 460 EXPECT_EQ(ret, 2); 461 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 1); 462 EXPECT_EQ(ret, 2); 463 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 2); 464 EXPECT_EQ(ret, 2); 465 ret = conntracK_count_zone(self->sock, NF_CT_DEFAULT_ZONE_ID); 466 EXPECT_EQ(ret, 0); 467} 468 469TEST_HARNESS_MAIN 470