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