1 // SPDX-License-Identifier: GPL-2.0
6 #include <libmnl/libmnl.h>
7 #include <netinet/ip.h>
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"
15 #define TEST_ZONE_ID 123
16 #define NF_CT_DEFAULT_ZONE_ID 0
18 static int reply_counter;
20 static 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)
24 struct nlattr *nest, *nest_ip, *nest_proto;
26 nest = mnl_attr_nest_start(nlh, type);
30 nest_ip = mnl_attr_nest_start(nlh, CTA_TUPLE_IP);
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);
37 nest_proto = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO);
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);
45 mnl_attr_nest_end(nlh, nest);
48 static 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)
52 struct nlattr *nest, *nest_ip, *nest_proto;
54 nest = mnl_attr_nest_start(nlh, type);
58 nest_ip = mnl_attr_nest_start(nlh, CTA_TUPLE_IP);
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);
65 nest_proto = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO);
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);
73 mnl_attr_nest_end(nlh, nest);
76 static int build_cta_proto(struct nlmsghdr *nlh)
78 struct nlattr *nest, *nest_proto;
80 nest = mnl_attr_nest_start(nlh, CTA_PROTOINFO);
84 nest_proto = mnl_attr_nest_start(nlh, CTA_PROTOINFO_TCP);
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);
92 mnl_attr_nest_end(nlh, nest);
95 static int conntrack_data_insert(struct mnl_socket *sock, struct nlmsghdr *nlh,
98 char buf[MNL_SOCKET_BUFFER_SIZE];
99 struct nlmsghdr *rplnlh;
103 portid = mnl_socket_get_portid(sock);
105 ret = build_cta_proto(nlh);
107 perror("build_cta_proto");
110 mnl_attr_put_u32(nlh, CTA_TIMEOUT, htonl(20000));
111 mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone));
113 if (mnl_socket_sendto(sock, nlh, nlh->nlmsg_len) < 0) {
114 perror("mnl_socket_sendto");
118 ret = mnl_socket_recvfrom(sock, buf, MNL_SOCKET_BUFFER_SIZE);
120 perror("mnl_socket_recvfrom");
124 ret = mnl_cb_run(buf, ret, nlh->nlmsg_seq, portid, NULL, NULL);
126 if (errno == EEXIST) {
127 /* The entries are probably still there from a previous
128 * run. So we are good
132 perror("mnl_cb_run");
139 static int conntrack_data_generate_v4(struct mnl_socket *sock, uint32_t src_ip,
140 uint32_t dst_ip, uint16_t zone)
142 char buf[MNL_SOCKET_BUFFER_SIZE];
143 struct nlmsghdr *nlh;
144 struct nfgenmsg *nfh;
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);
153 nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
154 nfh->nfgen_family = AF_INET;
155 nfh->version = NFNETLINK_V0;
158 ret = build_cta_tuple_v4(nlh, CTA_TUPLE_ORIG, src_ip, dst_ip, 12345, 443);
160 perror("build_cta_tuple_v4");
163 ret = build_cta_tuple_v4(nlh, CTA_TUPLE_REPLY, dst_ip, src_ip, 443, 12345);
165 perror("build_cta_tuple_v4");
168 return conntrack_data_insert(sock, nlh, zone);
171 static int conntrack_data_generate_v6(struct mnl_socket *sock,
172 struct in6_addr src_ip,
173 struct in6_addr dst_ip,
176 char buf[MNL_SOCKET_BUFFER_SIZE];
177 struct nlmsghdr *nlh;
178 struct nfgenmsg *nfh;
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);
187 nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
188 nfh->nfgen_family = AF_INET6;
189 nfh->version = NFNETLINK_V0;
192 ret = build_cta_tuple_v6(nlh, CTA_TUPLE_ORIG, src_ip, dst_ip,
195 perror("build_cta_tuple_v6");
198 ret = build_cta_tuple_v6(nlh, CTA_TUPLE_REPLY, dst_ip, src_ip,
201 perror("build_cta_tuple_v6");
204 return conntrack_data_insert(sock, nlh, zone);
207 static int count_entries(const struct nlmsghdr *nlh, void *data)
212 static int conntracK_count_zone(struct mnl_socket *sock, uint16_t zone)
214 char buf[MNL_SOCKET_BUFFER_SIZE];
215 struct nlmsghdr *nlh, *rplnlh;
216 struct nfgenmsg *nfh;
221 portid = mnl_socket_get_portid(sock);
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);
228 nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
229 nfh->nfgen_family = AF_UNSPEC;
230 nfh->version = NFNETLINK_V0;
233 mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone));
235 ret = mnl_socket_sendto(sock, nlh, nlh->nlmsg_len);
237 perror("mnl_socket_sendto");
242 ret = mnl_socket_recvfrom(sock, buf, MNL_SOCKET_BUFFER_SIZE);
244 ret = mnl_cb_run(buf, ret, nlh->nlmsg_seq, portid,
245 count_entries, NULL);
246 if (ret <= MNL_CB_STOP)
249 ret = mnl_socket_recvfrom(sock, buf, MNL_SOCKET_BUFFER_SIZE);
252 perror("mnl_socket_recvfrom");
256 return reply_counter;
259 static int conntrack_flush_zone(struct mnl_socket *sock, uint16_t zone)
261 char buf[MNL_SOCKET_BUFFER_SIZE];
262 struct nlmsghdr *nlh, *rplnlh;
263 struct nfgenmsg *nfh;
268 portid = mnl_socket_get_portid(sock);
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);
275 nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
276 nfh->nfgen_family = AF_UNSPEC;
277 nfh->version = NFNETLINK_V0;
280 mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone));
282 ret = mnl_socket_sendto(sock, nlh, nlh->nlmsg_len);
284 perror("mnl_socket_sendto");
288 ret = mnl_socket_recvfrom(sock, buf, MNL_SOCKET_BUFFER_SIZE);
290 perror("mnl_socket_recvfrom");
294 ret = mnl_cb_run(buf, ret, nlh->nlmsg_seq, portid, NULL, NULL);
296 perror("mnl_cb_run");
303 FIXTURE(conntrack_dump_flush)
305 struct mnl_socket *sock;
308 FIXTURE_SETUP(conntrack_dump_flush)
310 struct in6_addr src, dst;
313 self->sock = mnl_socket_open(NETLINK_NETFILTER);
315 perror("mnl_socket_open");
319 if (mnl_socket_bind(self->sock, 0, MNL_SOCKET_AUTOPID) < 0) {
320 perror("mnl_socket_bind");
324 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID);
325 if (ret < 0 && errno == EPERM)
326 SKIP(return, "Needs to be run as root");
327 else if (ret < 0 && errno == EOPNOTSUPP)
328 SKIP(return, "Kernel does not seem to support conntrack zones");
330 ret = conntrack_data_generate_v4(self->sock, 0xf0f0f0f0, 0xf1f1f1f1,
333 ret = conntrack_data_generate_v4(self->sock, 0xf2f2f2f2, 0xf3f3f3f3,
336 ret = conntrack_data_generate_v4(self->sock, 0xf4f4f4f4, 0xf5f5f5f5,
339 ret = conntrack_data_generate_v4(self->sock, 0xf6f6f6f6, 0xf7f7f7f7,
340 NF_CT_DEFAULT_ZONE_ID);
343 src = (struct in6_addr) {{
351 dst = (struct in6_addr) {{
359 ret = conntrack_data_generate_v6(self->sock, src, dst,
362 src = (struct in6_addr) {{
370 dst = (struct in6_addr) {{
378 ret = conntrack_data_generate_v6(self->sock, src, dst,
381 src = (struct in6_addr) {{
389 dst = (struct in6_addr) {{
397 ret = conntrack_data_generate_v6(self->sock, src, dst,
401 src = (struct in6_addr) {{
409 dst = (struct in6_addr) {{
417 ret = conntrack_data_generate_v6(self->sock, src, dst,
418 NF_CT_DEFAULT_ZONE_ID);
421 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID);
424 SKIP(return, "kernel does not support filtering by zone");
427 FIXTURE_TEARDOWN(conntrack_dump_flush)
431 TEST_F(conntrack_dump_flush, test_dump_by_zone)
435 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID);
439 TEST_F(conntrack_dump_flush, test_flush_by_zone)
443 ret = conntrack_flush_zone(self->sock, TEST_ZONE_ID);
445 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID);
447 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 1);
449 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 2);
451 ret = conntracK_count_zone(self->sock, NF_CT_DEFAULT_ZONE_ID);
455 TEST_F(conntrack_dump_flush, test_flush_by_zone_default)
459 ret = conntrack_flush_zone(self->sock, NF_CT_DEFAULT_ZONE_ID);
461 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID);
463 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 1);
465 ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 2);
467 ret = conntracK_count_zone(self->sock, NF_CT_DEFAULT_ZONE_ID);