xref: /aosp_15_r20/external/iptables/iptables/nft.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1 /*
2  * (C) 2012 by Pablo Neira Ayuso <[email protected]>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published
6  * by the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
10  */
11 
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <sys/types.h>
15 #include <sys/socket.h>
16 #include <stdbool.h>
17 #include <errno.h>
18 #include <netdb.h>	/* getprotobynumber */
19 #include <time.h>
20 #include <stdarg.h>
21 #include <inttypes.h>
22 #include <assert.h>
23 
24 #include <xtables.h>
25 #include <libiptc/libxtc.h>
26 #include <libiptc/xtcshared.h>
27 
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include <linux/netfilter/x_tables.h>
32 #include <linux/netfilter_ipv4/ip_tables.h>
33 #include <linux/netfilter_ipv6/ip6_tables.h>
34 #include <netinet/ip6.h>
35 
36 #include <linux/netlink.h>
37 #include <linux/netfilter/nfnetlink.h>
38 #include <linux/netfilter/nf_tables.h>
39 #include <linux/netfilter/nf_tables_compat.h>
40 
41 #include <linux/netfilter/xt_limit.h>
42 #include <linux/netfilter/xt_NFLOG.h>
43 #include <linux/netfilter/xt_mark.h>
44 
45 #include <libmnl/libmnl.h>
46 #include <libnftnl/gen.h>
47 #include <libnftnl/table.h>
48 #include <libnftnl/chain.h>
49 #include <libnftnl/rule.h>
50 #include <libnftnl/expr.h>
51 #include <libnftnl/set.h>
52 #include <libnftnl/udata.h>
53 #include <libnftnl/batch.h>
54 
55 #include <netinet/in.h>	/* inet_ntoa */
56 #include <arpa/inet.h>
57 
58 #include "nft.h"
59 #include "xshared.h" /* proto_to_name */
60 #include "nft-cache.h"
61 #include "nft-shared.h"
62 #include "nft-bridge.h" /* EBT_NOPROTO */
63 
64 static void *nft_fn;
65 
mnl_talk(struct nft_handle * h,struct nlmsghdr * nlh,int (* cb)(const struct nlmsghdr * nlh,void * data),void * data)66 int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
67 	     int (*cb)(const struct nlmsghdr *nlh, void *data),
68 	     void *data)
69 {
70 	int ret;
71 	char buf[32768];
72 
73 	if (mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len) < 0)
74 		return -1;
75 
76 	ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
77 	while (ret > 0) {
78 		ret = mnl_cb_run(buf, ret, h->seq, h->portid, cb, data);
79 		if (ret <= 0)
80 			break;
81 
82 		ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
83 	}
84 	if (ret == -1) {
85 		return -1;
86 	}
87 
88 	return 0;
89 }
90 
91 #define NFT_NLMSG_MAXSIZE (UINT16_MAX + getpagesize())
92 
93 /* Selected batch page is 2 Mbytes long to support loading a ruleset of 3.5M
94  * rules matching on source and destination address as well as input and output
95  * interfaces. This is what legacy iptables supports.
96  */
97 #define BATCH_PAGE_SIZE 2 * 1024 * 1024
98 
mnl_batch_init(void)99 static struct nftnl_batch *mnl_batch_init(void)
100 {
101 	struct nftnl_batch *batch;
102 
103 	batch = nftnl_batch_alloc(BATCH_PAGE_SIZE, NFT_NLMSG_MAXSIZE);
104 	if (batch == NULL)
105 		return NULL;
106 
107 	return batch;
108 }
109 
mnl_nft_batch_continue(struct nftnl_batch * batch)110 static void mnl_nft_batch_continue(struct nftnl_batch *batch)
111 {
112 	int ret = nftnl_batch_update(batch);
113 
114 	assert(ret >= 0);
115 }
116 
mnl_batch_begin(struct nftnl_batch * batch,uint32_t genid,uint32_t seqnum)117 static uint32_t mnl_batch_begin(struct nftnl_batch *batch, uint32_t genid, uint32_t seqnum)
118 {
119 	struct nlmsghdr *nlh;
120 
121 	nlh = nftnl_batch_begin(nftnl_batch_buffer(batch), seqnum);
122 
123 	mnl_attr_put_u32(nlh, NFTA_GEN_ID, htonl(genid));
124 
125 	mnl_nft_batch_continue(batch);
126 
127 	return seqnum;
128 }
129 
mnl_batch_end(struct nftnl_batch * batch,uint32_t seqnum)130 static void mnl_batch_end(struct nftnl_batch *batch, uint32_t seqnum)
131 {
132 	nftnl_batch_end(nftnl_batch_buffer(batch), seqnum);
133 	mnl_nft_batch_continue(batch);
134 }
135 
mnl_batch_reset(struct nftnl_batch * batch)136 static void mnl_batch_reset(struct nftnl_batch *batch)
137 {
138 	nftnl_batch_free(batch);
139 }
140 
141 struct mnl_err {
142 	struct list_head	head;
143 	int			err;
144 	uint32_t		seqnum;
145 };
146 
mnl_err_list_node_add(struct list_head * err_list,int error,int seqnum)147 static void mnl_err_list_node_add(struct list_head *err_list, int error,
148 				  int seqnum)
149 {
150 	struct mnl_err *err = xtables_malloc(sizeof(struct mnl_err));
151 
152 	err->seqnum = seqnum;
153 	err->err = error;
154 	list_add_tail(&err->head, err_list);
155 }
156 
mnl_err_list_free(struct mnl_err * err)157 static void mnl_err_list_free(struct mnl_err *err)
158 {
159 	list_del(&err->head);
160 	free(err);
161 }
162 
mnl_set_sndbuffer(struct nft_handle * h)163 static void mnl_set_sndbuffer(struct nft_handle *h)
164 {
165 	int newbuffsiz = nftnl_batch_iovec_len(h->batch) * BATCH_PAGE_SIZE;
166 
167 	if (newbuffsiz <= h->nlsndbuffsiz)
168 		return;
169 
170 	/* Rise sender buffer length to avoid hitting -EMSGSIZE */
171 	if (setsockopt(mnl_socket_get_fd(h->nl), SOL_SOCKET, SO_SNDBUFFORCE,
172 		       &newbuffsiz, sizeof(socklen_t)) < 0)
173 		return;
174 
175 	h->nlsndbuffsiz = newbuffsiz;
176 }
177 
mnl_set_rcvbuffer(struct nft_handle * h,int numcmds)178 static void mnl_set_rcvbuffer(struct nft_handle *h, int numcmds)
179 {
180 	int newbuffsiz = getpagesize() * numcmds;
181 
182 	if (newbuffsiz <= h->nlrcvbuffsiz)
183 		return;
184 
185 	/* Rise receiver buffer length to avoid hitting -ENOBUFS */
186 	if (setsockopt(mnl_socket_get_fd(h->nl), SOL_SOCKET, SO_RCVBUFFORCE,
187 		       &newbuffsiz, sizeof(socklen_t)) < 0)
188 		return;
189 
190 	h->nlrcvbuffsiz = newbuffsiz;
191 }
192 
mnl_nft_socket_sendmsg(struct nft_handle * h,int numcmds)193 static ssize_t mnl_nft_socket_sendmsg(struct nft_handle *h, int numcmds)
194 {
195 	static const struct sockaddr_nl snl = {
196 		.nl_family = AF_NETLINK
197 	};
198 	uint32_t iov_len = nftnl_batch_iovec_len(h->batch);
199 	struct iovec iov[iov_len];
200 	struct msghdr msg = {
201 		.msg_name	= (struct sockaddr *) &snl,
202 		.msg_namelen	= sizeof(snl),
203 		.msg_iov	= iov,
204 		.msg_iovlen	= iov_len,
205 	};
206 
207 	mnl_set_sndbuffer(h);
208 	mnl_set_rcvbuffer(h, numcmds);
209 	nftnl_batch_iovec(h->batch, iov, iov_len);
210 
211 	return sendmsg(mnl_socket_get_fd(h->nl), &msg, 0);
212 }
213 
mnl_batch_talk(struct nft_handle * h,int numcmds)214 static int mnl_batch_talk(struct nft_handle *h, int numcmds)
215 {
216 	const struct mnl_socket *nl = h->nl;
217 	int ret, fd = mnl_socket_get_fd(nl), portid = mnl_socket_get_portid(nl);
218 	char rcv_buf[MNL_SOCKET_BUFFER_SIZE];
219 	fd_set readfds;
220 	struct timeval tv = {
221 		.tv_sec		= 0,
222 		.tv_usec	= 0
223 	};
224 	int err = 0;
225 
226 	ret = mnl_nft_socket_sendmsg(h, numcmds);
227 	if (ret == -1) {
228 		fprintf(stderr, "sendmsg() failed: %s\n", strerror(errno));
229 		return -1;
230 	}
231 
232 	FD_ZERO(&readfds);
233 	FD_SET(fd, &readfds);
234 
235 	/* receive and digest all the acknowledgments from the kernel. */
236 	ret = select(fd+1, &readfds, NULL, NULL, &tv);
237 	if (ret == -1)
238 		return -1;
239 
240 	while (ret > 0 && FD_ISSET(fd, &readfds)) {
241 		struct nlmsghdr *nlh = (struct nlmsghdr *)rcv_buf;
242 
243 		ret = mnl_socket_recvfrom(nl, rcv_buf, sizeof(rcv_buf));
244 		if (ret == -1)
245 			return -1;
246 
247 		ret = mnl_cb_run(rcv_buf, ret, 0, portid, NULL, NULL);
248 		/* Continue on error, make sure we get all acknowledgments */
249 		if (ret == -1) {
250 			mnl_err_list_node_add(&h->err_list, errno,
251 					      nlh->nlmsg_seq);
252 			err = -1;
253 		}
254 
255 		ret = select(fd+1, &readfds, NULL, NULL, &tv);
256 		if (ret == -1)
257 			return -1;
258 
259 		FD_ZERO(&readfds);
260 		FD_SET(fd, &readfds);
261 	}
262 	return err;
263 }
264 
265 enum obj_action {
266 	NFT_COMPAT_COMMIT,
267 	NFT_COMPAT_ABORT,
268 };
269 
270 struct obj_update {
271 	struct list_head	head;
272 	enum obj_update_type	type:8;
273 	uint8_t			skip:1;
274 	unsigned int		seq;
275 	union {
276 		struct nftnl_table	*table;
277 		struct nftnl_chain	*chain;
278 		struct nftnl_rule	*rule;
279 		struct nftnl_set	*set;
280 		void			*ptr;
281 	};
282 	struct {
283 		unsigned int		lineno;
284 	} error;
285 };
286 
mnl_append_error(const struct nft_handle * h,const struct obj_update * o,const struct mnl_err * err,char * buf,unsigned int len)287 static int mnl_append_error(const struct nft_handle *h,
288 			    const struct obj_update *o,
289 			    const struct mnl_err *err,
290 			    char *buf, unsigned int len)
291 {
292 	static const char *type_name[] = {
293 		[NFT_COMPAT_TABLE_ADD] = "TABLE_ADD",
294 		[NFT_COMPAT_TABLE_FLUSH] = "TABLE_FLUSH",
295 		[NFT_COMPAT_CHAIN_ADD] = "CHAIN_ADD",
296 		[NFT_COMPAT_CHAIN_USER_ADD] = "CHAIN_USER_ADD",
297 		[NFT_COMPAT_CHAIN_DEL] = "CHAIN_DEL",
298 		[NFT_COMPAT_CHAIN_USER_FLUSH] = "CHAIN_USER_FLUSH",
299 		[NFT_COMPAT_CHAIN_UPDATE] = "CHAIN_UPDATE",
300 		[NFT_COMPAT_CHAIN_RENAME] = "CHAIN_RENAME",
301 		[NFT_COMPAT_CHAIN_ZERO] = "CHAIN_ZERO",
302 		[NFT_COMPAT_RULE_APPEND] = "RULE_APPEND",
303 		[NFT_COMPAT_RULE_INSERT] = "RULE_INSERT",
304 		[NFT_COMPAT_RULE_REPLACE] = "RULE_REPLACE",
305 		[NFT_COMPAT_RULE_DELETE] = "RULE_DELETE",
306 		[NFT_COMPAT_RULE_FLUSH] = "RULE_FLUSH",
307 		[NFT_COMPAT_SET_ADD] = "SET_ADD",
308 	};
309 	char errmsg[256];
310 	char tcr[128];
311 
312 	if (o->error.lineno)
313 		snprintf(errmsg, sizeof(errmsg), "\nline %u: %s failed (%s)",
314 			 o->error.lineno, type_name[o->type], strerror(err->err));
315 	else
316 		snprintf(errmsg, sizeof(errmsg), " %s failed (%s)",
317 			 type_name[o->type], strerror(err->err));
318 
319 	switch (o->type) {
320 	case NFT_COMPAT_TABLE_ADD:
321 	case NFT_COMPAT_TABLE_FLUSH:
322 		snprintf(tcr, sizeof(tcr), "table %s",
323 			 nftnl_table_get_str(o->table, NFTNL_TABLE_NAME));
324 		break;
325 	case NFT_COMPAT_CHAIN_ADD:
326 	case NFT_COMPAT_CHAIN_ZERO:
327 	case NFT_COMPAT_CHAIN_USER_ADD:
328 	case NFT_COMPAT_CHAIN_DEL:
329 	case NFT_COMPAT_CHAIN_USER_FLUSH:
330 	case NFT_COMPAT_CHAIN_UPDATE:
331 	case NFT_COMPAT_CHAIN_RENAME:
332 		snprintf(tcr, sizeof(tcr), "chain %s",
333 			 nftnl_chain_get_str(o->chain, NFTNL_CHAIN_NAME));
334 		break;
335 	case NFT_COMPAT_RULE_APPEND:
336 	case NFT_COMPAT_RULE_INSERT:
337 	case NFT_COMPAT_RULE_REPLACE:
338 	case NFT_COMPAT_RULE_DELETE:
339 	case NFT_COMPAT_RULE_FLUSH:
340 		snprintf(tcr, sizeof(tcr), "rule in chain %s",
341 			 nftnl_rule_get_str(o->rule, NFTNL_RULE_CHAIN));
342 #if 0
343 		{
344 			nft_rule_print_save(h, o->rule, NFT_RULE_APPEND, FMT_NOCOUNTS);
345 		}
346 #endif
347 		break;
348 	case NFT_COMPAT_SET_ADD:
349 		snprintf(tcr, sizeof(tcr), "set %s",
350 			 nftnl_set_get_str(o->set, NFTNL_SET_NAME));
351 		break;
352 	case NFT_COMPAT_RULE_LIST:
353 	case NFT_COMPAT_RULE_CHECK:
354 	case NFT_COMPAT_CHAIN_RESTORE:
355 	case NFT_COMPAT_RULE_SAVE:
356 	case NFT_COMPAT_RULE_ZERO:
357 	case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
358 		assert(0);
359 		break;
360 	}
361 
362 	return snprintf(buf, len, "%s: %s", errmsg, tcr);
363 }
364 
batch_add(struct nft_handle * h,enum obj_update_type type,void * ptr)365 static struct obj_update *batch_add(struct nft_handle *h, enum obj_update_type type, void *ptr)
366 {
367 	struct obj_update *obj;
368 
369 	obj = xtables_calloc(1, sizeof(struct obj_update));
370 	obj->ptr = ptr;
371 	obj->error.lineno = h->error.lineno;
372 	obj->type = type;
373 	list_add_tail(&obj->head, &h->obj_list);
374 	h->obj_list_num++;
375 
376 	return obj;
377 }
378 
379 static struct obj_update *
batch_table_add(struct nft_handle * h,enum obj_update_type type,struct nftnl_table * t)380 batch_table_add(struct nft_handle *h, enum obj_update_type type,
381 		struct nftnl_table *t)
382 {
383 	return batch_add(h, type, t);
384 }
385 
386 static struct obj_update *
batch_set_add(struct nft_handle * h,enum obj_update_type type,struct nftnl_set * s)387 batch_set_add(struct nft_handle *h, enum obj_update_type type,
388 	      struct nftnl_set *s)
389 {
390 	return batch_add(h, type, s);
391 }
392 
393 static struct obj_update *
batch_chain_add(struct nft_handle * h,enum obj_update_type type,struct nftnl_chain * c)394 batch_chain_add(struct nft_handle *h, enum obj_update_type type,
395 			   struct nftnl_chain *c)
396 {
397 	return batch_add(h, type, c);
398 }
399 
400 static struct obj_update *
batch_rule_add(struct nft_handle * h,enum obj_update_type type,struct nftnl_rule * r)401 batch_rule_add(struct nft_handle *h, enum obj_update_type type,
402 			  struct nftnl_rule *r)
403 {
404 	return batch_add(h, type, r);
405 }
406 
407 static void batch_obj_del(struct nft_handle *h, struct obj_update *o);
408 
batch_chain_flush(struct nft_handle * h,const char * table,const char * chain)409 static void batch_chain_flush(struct nft_handle *h,
410 			      const char *table, const char *chain)
411 {
412 	struct obj_update *obj, *tmp;
413 
414 	list_for_each_entry_safe(obj, tmp, &h->obj_list, head) {
415 		struct nftnl_rule *r = obj->ptr;
416 
417 		switch (obj->type) {
418 		case NFT_COMPAT_RULE_APPEND:
419 		case NFT_COMPAT_RULE_INSERT:
420 		case NFT_COMPAT_RULE_REPLACE:
421 		case NFT_COMPAT_RULE_DELETE:
422 			break;
423 		default:
424 			continue;
425 		}
426 
427 		if (table &&
428 		    strcmp(table, nftnl_rule_get_str(r, NFTNL_RULE_TABLE)))
429 			continue;
430 
431 		if (chain &&
432 		    strcmp(chain, nftnl_rule_get_str(r, NFTNL_RULE_CHAIN)))
433 			continue;
434 
435 		batch_obj_del(h, obj);
436 	}
437 }
438 
439 static const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
440 	[NFT_TABLE_RAW] = {
441 		.name	= "raw",
442 		.type	= NFT_TABLE_RAW,
443 		.chains = {
444 			{
445 				.name	= "PREROUTING",
446 				.type	= "filter",
447 				.prio	= -300,	/* NF_IP_PRI_RAW */
448 				.hook	= NF_INET_PRE_ROUTING,
449 			},
450 			{
451 				.name	= "OUTPUT",
452 				.type	= "filter",
453 				.prio	= -300,	/* NF_IP_PRI_RAW */
454 				.hook	= NF_INET_LOCAL_OUT,
455 			},
456 		},
457 	},
458 	[NFT_TABLE_MANGLE] = {
459 		.name	= "mangle",
460 		.type	= NFT_TABLE_MANGLE,
461 		.chains = {
462 			{
463 				.name	= "PREROUTING",
464 				.type	= "filter",
465 				.prio	= -150,	/* NF_IP_PRI_MANGLE */
466 				.hook	= NF_INET_PRE_ROUTING,
467 			},
468 			{
469 				.name	= "INPUT",
470 				.type	= "filter",
471 				.prio	= -150,	/* NF_IP_PRI_MANGLE */
472 				.hook	= NF_INET_LOCAL_IN,
473 			},
474 			{
475 				.name	= "FORWARD",
476 				.type	= "filter",
477 				.prio	= -150,	/* NF_IP_PRI_MANGLE */
478 				.hook	= NF_INET_FORWARD,
479 			},
480 			{
481 				.name	= "OUTPUT",
482 				.type	= "route",
483 				.prio	= -150,	/* NF_IP_PRI_MANGLE */
484 				.hook	= NF_INET_LOCAL_OUT,
485 			},
486 			{
487 				.name	= "POSTROUTING",
488 				.type	= "filter",
489 				.prio	= -150,	/* NF_IP_PRI_MANGLE */
490 				.hook	= NF_INET_POST_ROUTING,
491 			},
492 		},
493 	},
494 	[NFT_TABLE_FILTER] = {
495 		.name	= "filter",
496 		.type	= NFT_TABLE_FILTER,
497 		.chains = {
498 			{
499 				.name	= "INPUT",
500 				.type	= "filter",
501 				.prio	= 0,	/* NF_IP_PRI_FILTER */
502 				.hook	= NF_INET_LOCAL_IN,
503 			},
504 			{
505 				.name	= "FORWARD",
506 				.type	= "filter",
507 				.prio	= 0,	/* NF_IP_PRI_FILTER */
508 				.hook	= NF_INET_FORWARD,
509 			},
510 			{
511 				.name	= "OUTPUT",
512 				.type	= "filter",
513 				.prio	= 0,	/* NF_IP_PRI_FILTER */
514 				.hook	= NF_INET_LOCAL_OUT,
515 			},
516 		},
517 	},
518 	[NFT_TABLE_SECURITY] = {
519 		.name	= "security",
520 		.type	= NFT_TABLE_SECURITY,
521 		.chains = {
522 			{
523 				.name	= "INPUT",
524 				.type	= "filter",
525 				.prio	= 150,	/* NF_IP_PRI_SECURITY */
526 				.hook	= NF_INET_LOCAL_IN,
527 			},
528 			{
529 				.name	= "FORWARD",
530 				.type	= "filter",
531 				.prio	= 150,	/* NF_IP_PRI_SECURITY */
532 				.hook	= NF_INET_FORWARD,
533 			},
534 			{
535 				.name	= "OUTPUT",
536 				.type	= "filter",
537 				.prio	= 150,	/* NF_IP_PRI_SECURITY */
538 				.hook	= NF_INET_LOCAL_OUT,
539 			},
540 		},
541 	},
542 	[NFT_TABLE_NAT] = {
543 		.name	= "nat",
544 		.type	= NFT_TABLE_NAT,
545 		.chains = {
546 			{
547 				.name	= "PREROUTING",
548 				.type	= "nat",
549 				.prio	= -100, /* NF_IP_PRI_NAT_DST */
550 				.hook	= NF_INET_PRE_ROUTING,
551 			},
552 			{
553 				.name	= "INPUT",
554 				.type	= "nat",
555 				.prio	= 100, /* NF_IP_PRI_NAT_SRC */
556 				.hook	= NF_INET_LOCAL_IN,
557 			},
558 			{
559 				.name	= "POSTROUTING",
560 				.type	= "nat",
561 				.prio	= 100, /* NF_IP_PRI_NAT_SRC */
562 				.hook	= NF_INET_POST_ROUTING,
563 			},
564 			{
565 				.name	= "OUTPUT",
566 				.type	= "nat",
567 				.prio	= -100, /* NF_IP_PRI_NAT_DST */
568 				.hook	= NF_INET_LOCAL_OUT,
569 			},
570 		},
571 	},
572 };
573 
574 #include <linux/netfilter_arp.h>
575 
576 static const struct builtin_table xtables_arp[NFT_TABLE_MAX] = {
577 	[NFT_TABLE_FILTER] = {
578 	.name   = "filter",
579 	.type	= NFT_TABLE_FILTER,
580 	.chains = {
581 			{
582 				.name   = "INPUT",
583 				.type   = "filter",
584 				.prio   = NF_IP_PRI_FILTER,
585 				.hook   = NF_ARP_IN,
586 			},
587 			{
588 				.name   = "OUTPUT",
589 				.type   = "filter",
590 				.prio   = NF_IP_PRI_FILTER,
591 				.hook   = NF_ARP_OUT,
592 			},
593 		},
594 	},
595 };
596 
597 #include <linux/netfilter_bridge.h>
598 
599 static const struct builtin_table xtables_bridge[NFT_TABLE_MAX] = {
600 	[NFT_TABLE_FILTER] = {
601 		.name = "filter",
602 		.type	= NFT_TABLE_FILTER,
603 		.chains = {
604 			{
605 				.name   = "INPUT",
606 				.type   = "filter",
607 				.prio   = NF_BR_PRI_FILTER_BRIDGED,
608 				.hook   = NF_BR_LOCAL_IN,
609 			},
610 			{
611 				.name   = "FORWARD",
612 				.type   = "filter",
613 				.prio   = NF_BR_PRI_FILTER_BRIDGED,
614 				.hook   = NF_BR_FORWARD,
615 			},
616 			{
617 				.name   = "OUTPUT",
618 				.type   = "filter",
619 				.prio   = NF_BR_PRI_FILTER_BRIDGED,
620 				.hook   = NF_BR_LOCAL_OUT,
621 			},
622 		},
623 	},
624 	[NFT_TABLE_NAT] = {
625 		.name = "nat",
626 		.type	= NFT_TABLE_NAT,
627 		.chains = {
628 			{
629 				.name   = "PREROUTING",
630 				.type   = "filter",
631 				.prio   = NF_BR_PRI_NAT_DST_BRIDGED,
632 				.hook   = NF_BR_PRE_ROUTING,
633 			},
634 			{
635 				.name   = "OUTPUT",
636 				.type   = "filter",
637 				.prio   = NF_BR_PRI_NAT_DST_OTHER,
638 				.hook   = NF_BR_LOCAL_OUT,
639 			},
640 			{
641 				.name   = "POSTROUTING",
642 				.type   = "filter",
643 				.prio   = NF_BR_PRI_NAT_SRC,
644 				.hook   = NF_BR_POST_ROUTING,
645 			},
646 		},
647 	},
648 	[NFT_TABLE_BROUTE] = {
649 		.name = "broute",
650 		.type	= NFT_TABLE_BROUTE,
651 		.chains = {
652 			{
653 				.name   = "BROUTING",
654 				.type   = "filter",
655 				.prio   = NF_BR_PRI_FIRST,
656 				.hook   = NF_BR_PRE_ROUTING,
657 			},
658 		},
659 	},
660 
661 };
662 
nft_table_builtin_add(struct nft_handle * h,const struct builtin_table * _t)663 static int nft_table_builtin_add(struct nft_handle *h,
664 				 const struct builtin_table *_t)
665 {
666 	struct nftnl_table *t;
667 	int ret;
668 
669 	if (h->cache->table[_t->type].exists)
670 		return 0;
671 
672 	t = nftnl_table_alloc();
673 	if (t == NULL)
674 		return -1;
675 
676 	nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, h->family);
677 	nftnl_table_set_str(t, NFTNL_TABLE_NAME, _t->name);
678 
679 	ret = batch_table_add(h, NFT_COMPAT_TABLE_ADD, t) ? 0 : - 1;
680 
681 	return ret;
682 }
683 
684 static struct nftnl_chain *
nft_chain_builtin_alloc(int family,const char * tname,const struct builtin_chain * chain,int policy)685 nft_chain_builtin_alloc(int family, const char *tname,
686 			const struct builtin_chain *chain, int policy)
687 {
688 	struct nftnl_chain *c;
689 
690 	c = nftnl_chain_alloc();
691 	if (c == NULL)
692 		return NULL;
693 
694 	nftnl_chain_set_u32(c, NFTNL_CHAIN_FAMILY, family);
695 	nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, tname);
696 	nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain->name);
697 	nftnl_chain_set_u32(c, NFTNL_CHAIN_HOOKNUM, chain->hook);
698 	nftnl_chain_set_u32(c, NFTNL_CHAIN_PRIO, chain->prio);
699 	if (policy >= 0)
700 		nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, policy);
701 
702 	nftnl_chain_set_str(c, NFTNL_CHAIN_TYPE, chain->type);
703 
704 	nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, 0);
705 	nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, 0);
706 
707 	return c;
708 }
709 
nft_chain_builtin_add(struct nft_handle * h,const struct builtin_table * table,const struct builtin_chain * chain,bool fake)710 static void nft_chain_builtin_add(struct nft_handle *h,
711 				  const struct builtin_table *table,
712 				  const struct builtin_chain *chain,
713 				  bool fake)
714 {
715 	struct nftnl_chain *c;
716 
717 	c = nft_chain_builtin_alloc(h->family, table->name, chain, NF_ACCEPT);
718 	if (c == NULL)
719 		return;
720 
721 	if (!fake)
722 		batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, c);
723 	nft_cache_add_chain(h, table, c);
724 }
725 
726 /* find if built-in table already exists */
727 const struct builtin_table *
nft_table_builtin_find(struct nft_handle * h,const char * table)728 nft_table_builtin_find(struct nft_handle *h, const char *table)
729 {
730 	int i;
731 	bool found = false;
732 
733 	for (i = 0; i < NFT_TABLE_MAX; i++) {
734 		if (h->tables[i].name == NULL)
735 			continue;
736 
737 		if (strcmp(h->tables[i].name, table) != 0)
738 			continue;
739 
740 		found = true;
741 		break;
742 	}
743 
744 	return found ? &h->tables[i] : NULL;
745 }
746 
747 /* find if built-in chain already exists */
748 const struct builtin_chain *
nft_chain_builtin_find(const struct builtin_table * t,const char * chain)749 nft_chain_builtin_find(const struct builtin_table *t, const char *chain)
750 {
751 	int i;
752 	bool found = false;
753 
754 	for (i=0; i<NF_IP_NUMHOOKS && t->chains[i].name != NULL; i++) {
755 		if (strcmp(t->chains[i].name, chain) != 0)
756 			continue;
757 
758 		found = true;
759 		break;
760 	}
761 	return found ? &t->chains[i] : NULL;
762 }
763 
nft_chain_builtin_init(struct nft_handle * h,const struct builtin_table * table)764 static void nft_chain_builtin_init(struct nft_handle *h,
765 				   const struct builtin_table *table)
766 {
767 	int i;
768 
769 	/* Initialize built-in chains if they don't exist yet */
770 	for (i=0; i < NF_INET_NUMHOOKS && table->chains[i].name != NULL; i++) {
771 		if (nft_chain_find(h, table->name, table->chains[i].name))
772 			continue;
773 
774 		nft_chain_builtin_add(h, table, &table->chains[i], false);
775 	}
776 }
777 
778 static const struct builtin_table *
nft_xt_builtin_table_init(struct nft_handle * h,const char * table)779 nft_xt_builtin_table_init(struct nft_handle *h, const char *table)
780 {
781 	const struct builtin_table *t;
782 
783 	if (!h->cache_init)
784 		return NULL;
785 
786 	t = nft_table_builtin_find(h, table);
787 	if (t == NULL)
788 		return NULL;
789 
790 	if (nft_table_builtin_add(h, t) < 0)
791 		return NULL;
792 
793 	return t;
794 }
795 
nft_xt_builtin_init(struct nft_handle * h,const char * table,const char * chain)796 static int nft_xt_builtin_init(struct nft_handle *h, const char *table,
797 			       const char *chain)
798 {
799 	const struct builtin_table *t;
800 	const struct builtin_chain *c;
801 
802 	if (!h->cache_init)
803 		return 0;
804 
805 	t = nft_xt_builtin_table_init(h, table);
806 	if (!t)
807 		return -1;
808 
809 	if (h->cache_req.level < NFT_CL_CHAINS)
810 		return 0;
811 
812 	if (!chain) {
813 		nft_chain_builtin_init(h, t);
814 		return 0;
815 	}
816 
817 	c = nft_chain_builtin_find(t, chain);
818 	if (!c)
819 		return -1;
820 
821 	if (h->cache->table[t->type].base_chains[c->hook])
822 		return 0;
823 
824 	nft_chain_builtin_add(h, t, c, false);
825 	return 0;
826 }
827 
nft_chain_builtin(struct nftnl_chain * c)828 static bool nft_chain_builtin(struct nftnl_chain *c)
829 {
830 	/* Check if this chain has hook number, in that case is built-in.
831 	 * Should we better export the flags to user-space via nf_tables?
832 	 */
833 	return nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM) != NULL;
834 }
835 
__nft_xt_fake_builtin_chains(struct nft_handle * h,const char * table,void * data)836 static int __nft_xt_fake_builtin_chains(struct nft_handle *h,
837 				        const char *table, void *data)
838 {
839 	const char *chain = data ? *(const char **)data : NULL;
840 	const struct builtin_table *t;
841 	struct nft_chain **bcp;
842 	int i;
843 
844 	t = nft_table_builtin_find(h, table);
845 	if (!t)
846 		return -1;
847 
848 	bcp = h->cache->table[t->type].base_chains;
849 	for (i = 0; i < NF_INET_NUMHOOKS && t->chains[i].name; i++) {
850 		if (bcp[t->chains[i].hook])
851 			continue;
852 
853 		if (chain && strcmp(chain, t->chains[i].name))
854 			continue;
855 
856 		nft_chain_builtin_add(h, t, &t->chains[i], true);
857 	}
858 	return 0;
859 }
860 
nft_xt_fake_builtin_chains(struct nft_handle * h,const char * table,const char * chain)861 int nft_xt_fake_builtin_chains(struct nft_handle *h,
862 			       const char *table, const char *chain)
863 {
864 	if (table)
865 		return __nft_xt_fake_builtin_chains(h, table, &chain);
866 
867 	return nft_for_each_table(h, __nft_xt_fake_builtin_chains, &chain);
868 }
869 
nft_restart(struct nft_handle * h)870 int nft_restart(struct nft_handle *h)
871 {
872 	mnl_socket_close(h->nl);
873 
874 	h->nl = mnl_socket_open(NETLINK_NETFILTER);
875 	if (h->nl == NULL)
876 		return -1;
877 
878 	if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0)
879 		return -1;
880 
881 	h->portid = mnl_socket_get_portid(h->nl);
882 	h->nlsndbuffsiz = 0;
883 	h->nlrcvbuffsiz = 0;
884 
885 	return 0;
886 }
887 
builtin_tables_lookup(int family)888 static const struct builtin_table *builtin_tables_lookup(int family)
889 {
890 	switch (family) {
891 	case AF_INET:
892 	case AF_INET6:
893 		return xtables_ipv4;
894 	case NFPROTO_ARP:
895 		return xtables_arp;
896 	case NFPROTO_BRIDGE:
897 		return xtables_bridge;
898 	default:
899 		return NULL;
900 	}
901 }
902 
nft_init(struct nft_handle * h,int family)903 int nft_init(struct nft_handle *h, int family)
904 {
905 	memset(h, 0, sizeof(*h));
906 
907 	h->nl = mnl_socket_open(NETLINK_NETFILTER);
908 	if (h->nl == NULL)
909 		return -1;
910 
911 	if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0) {
912 		mnl_socket_close(h->nl);
913 		return -1;
914 	}
915 
916 	h->ops = nft_family_ops_lookup(family);
917 	if (!h->ops)
918 		xtables_error(PARAMETER_PROBLEM, "Unknown family");
919 
920 	h->portid = mnl_socket_get_portid(h->nl);
921 	h->tables = builtin_tables_lookup(family);
922 	h->cache = &h->__cache[0];
923 	h->family = family;
924 
925 	INIT_LIST_HEAD(&h->obj_list);
926 	INIT_LIST_HEAD(&h->err_list);
927 	INIT_LIST_HEAD(&h->cmd_list);
928 	INIT_LIST_HEAD(&h->cache_req.chain_list);
929 
930 	return 0;
931 }
932 
nft_fini(struct nft_handle * h)933 void nft_fini(struct nft_handle *h)
934 {
935 	struct list_head *pos, *n;
936 
937 	list_for_each_safe(pos, n, &h->cmd_list)
938 		nft_cmd_free(list_entry(pos, struct nft_cmd, head));
939 
940 	list_for_each_safe(pos, n, &h->obj_list)
941 		batch_obj_del(h, list_entry(pos, struct obj_update, head));
942 
943 	list_for_each_safe(pos, n, &h->err_list)
944 		mnl_err_list_free(list_entry(pos, struct mnl_err, head));
945 
946 	nft_release_cache(h);
947 	mnl_socket_close(h->nl);
948 }
949 
nft_chain_print_debug(struct nft_handle * h,struct nftnl_chain * c,struct nlmsghdr * nlh)950 static void nft_chain_print_debug(struct nft_handle *h,
951 				  struct nftnl_chain *c, struct nlmsghdr *nlh)
952 {
953 	if (h->verbose > 1) {
954 		nftnl_chain_fprintf(stdout, c, 0, 0);
955 		fprintf(stdout, "\n");
956 	}
957 	if (h->verbose > 2)
958 		mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len,
959 				  sizeof(struct nfgenmsg));
960 }
961 
nft_chain_new(struct nft_handle * h,const char * table,const char * chain,int policy,const struct xt_counters * counters)962 static struct nftnl_chain *nft_chain_new(struct nft_handle *h,
963 				       const char *table, const char *chain,
964 				       int policy,
965 				       const struct xt_counters *counters)
966 {
967 	static const struct xt_counters zero = {};
968 	struct nftnl_chain *c;
969 	const struct builtin_table *_t;
970 	const struct builtin_chain *_c;
971 
972 	_t = nft_table_builtin_find(h, table);
973 	if (!_t) {
974 		errno = ENXIO;
975 		return NULL;
976 	}
977 
978 	/* if this built-in table does not exists, create it */
979 	nft_xt_builtin_init(h, table, chain);
980 
981 	_c = nft_chain_builtin_find(_t, chain);
982 	if (_c != NULL) {
983 		/* This is a built-in chain */
984 		c = nft_chain_builtin_alloc(h->family, _t->name, _c, policy);
985 		if (c == NULL)
986 			return NULL;
987 	} else {
988 		errno = ENOENT;
989 		return NULL;
990 	}
991 
992 	if (!counters)
993 		counters = &zero;
994 	nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, counters->bcnt);
995 	nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, counters->pcnt);
996 
997 	return c;
998 }
999 
nft_chain_set(struct nft_handle * h,const char * table,const char * chain,const char * policy,const struct xt_counters * counters)1000 int nft_chain_set(struct nft_handle *h, const char *table,
1001 		  const char *chain, const char *policy,
1002 		  const struct xt_counters *counters)
1003 {
1004 	struct nftnl_chain *c = NULL;
1005 
1006 	nft_fn = nft_chain_set;
1007 
1008 	if (strcmp(policy, "DROP") == 0)
1009 		c = nft_chain_new(h, table, chain, NF_DROP, counters);
1010 	else if (strcmp(policy, "ACCEPT") == 0)
1011 		c = nft_chain_new(h, table, chain, NF_ACCEPT, counters);
1012 	else if (strcmp(policy, "-") == 0)
1013 		c = nft_chain_new(h, table, chain, -1, counters);
1014 	else
1015 		errno = EINVAL;
1016 
1017 	if (c == NULL)
1018 		return 0;
1019 
1020 	if (!batch_chain_add(h, NFT_COMPAT_CHAIN_UPDATE, c))
1021 		return 0;
1022 
1023 	/* the core expects 1 for success and 0 for error */
1024 	return 1;
1025 }
1026 
__add_match(struct nftnl_expr * e,struct xt_entry_match * m)1027 static int __add_match(struct nftnl_expr *e, struct xt_entry_match *m)
1028 {
1029 	void *info;
1030 
1031 	nftnl_expr_set(e, NFTNL_EXPR_MT_NAME, m->u.user.name, strlen(m->u.user.name));
1032 	nftnl_expr_set_u32(e, NFTNL_EXPR_MT_REV, m->u.user.revision);
1033 
1034 	info = xtables_calloc(1, m->u.match_size);
1035 	memcpy(info, m->data, m->u.match_size - sizeof(*m));
1036 	nftnl_expr_set(e, NFTNL_EXPR_MT_INFO, info, m->u.match_size - sizeof(*m));
1037 
1038 	return 0;
1039 }
1040 
add_nft_limit(struct nftnl_rule * r,struct xt_entry_match * m)1041 static int add_nft_limit(struct nftnl_rule *r, struct xt_entry_match *m)
1042 {
1043 	struct xt_rateinfo *rinfo = (void *)m->data;
1044 	static const uint32_t mult[] = {
1045 		XT_LIMIT_SCALE*24*60*60,	/* day */
1046 		XT_LIMIT_SCALE*60*60,		/* hour */
1047 		XT_LIMIT_SCALE*60,		/* min */
1048 		XT_LIMIT_SCALE,			/* sec */
1049 	};
1050 	struct nftnl_expr *expr;
1051 	int i;
1052 
1053 	expr = nftnl_expr_alloc("limit");
1054 	if (!expr)
1055 		return -ENOMEM;
1056 
1057 	for (i = 1; i < ARRAY_SIZE(mult); i++) {
1058 		if (rinfo->avg > mult[i] ||
1059 		    mult[i] / rinfo->avg < mult[i] % rinfo->avg)
1060 			break;
1061 	}
1062 
1063 	nftnl_expr_set_u32(expr, NFTNL_EXPR_LIMIT_TYPE, NFT_LIMIT_PKTS);
1064 	nftnl_expr_set_u32(expr, NFTNL_EXPR_LIMIT_FLAGS, 0);
1065 
1066 	nftnl_expr_set_u64(expr, NFTNL_EXPR_LIMIT_RATE,
1067 			   mult[i - 1] / rinfo->avg);
1068         nftnl_expr_set_u64(expr, NFTNL_EXPR_LIMIT_UNIT,
1069 			   mult[i - 1] / XT_LIMIT_SCALE);
1070 
1071 	nftnl_expr_set_u32(expr, NFTNL_EXPR_LIMIT_BURST, rinfo->burst);
1072 
1073 	nftnl_rule_add_expr(r, expr);
1074 	return 0;
1075 }
1076 
add_anon_set(struct nft_handle * h,const char * table,uint32_t flags,uint32_t key_type,uint32_t key_len,uint32_t size)1077 static struct nftnl_set *add_anon_set(struct nft_handle *h, const char *table,
1078 				      uint32_t flags, uint32_t key_type,
1079 				      uint32_t key_len, uint32_t size)
1080 {
1081 	static uint32_t set_id = 0;
1082 	struct nftnl_set *s;
1083 	struct nft_cmd *cmd;
1084 
1085 	s = nftnl_set_alloc();
1086 	if (!s)
1087 		return NULL;
1088 
1089 	nftnl_set_set_u32(s, NFTNL_SET_FAMILY, h->family);
1090 	nftnl_set_set_str(s, NFTNL_SET_TABLE, table);
1091 	nftnl_set_set_str(s, NFTNL_SET_NAME, "__set%d");
1092 	nftnl_set_set_u32(s, NFTNL_SET_ID, ++set_id);
1093 	nftnl_set_set_u32(s, NFTNL_SET_FLAGS,
1094 			  NFT_SET_ANONYMOUS | NFT_SET_CONSTANT | flags);
1095 	nftnl_set_set_u32(s, NFTNL_SET_KEY_TYPE, key_type);
1096 	nftnl_set_set_u32(s, NFTNL_SET_KEY_LEN, key_len);
1097 	nftnl_set_set_u32(s, NFTNL_SET_DESC_SIZE, size);
1098 
1099 	cmd = nft_cmd_new(h, NFT_COMPAT_SET_ADD, table, NULL, NULL, -1, false);
1100 	if (!cmd) {
1101 		nftnl_set_free(s);
1102 		return NULL;
1103 	}
1104 	cmd->obj.set = s;
1105 
1106 	return s;
1107 }
1108 
1109 static struct nftnl_expr *
__gen_payload(uint32_t base,uint32_t offset,uint32_t len,uint8_t reg)1110 __gen_payload(uint32_t base, uint32_t offset, uint32_t len, uint8_t reg)
1111 {
1112 	struct nftnl_expr *e = nftnl_expr_alloc("payload");
1113 
1114 	if (!e)
1115 		return NULL;
1116 
1117 	nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, base);
1118 	nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
1119 	nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, len);
1120 	nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, reg);
1121 
1122 	return e;
1123 }
1124 
1125 static struct nftnl_expr *
gen_payload(struct nft_handle * h,uint32_t base,uint32_t offset,uint32_t len,uint8_t * dreg)1126 gen_payload(struct nft_handle *h, uint32_t base, uint32_t offset, uint32_t len,
1127 	    uint8_t *dreg)
1128 {
1129 	struct nftnl_expr *e;
1130 	uint8_t reg;
1131 
1132 	reg = NFT_REG_1;
1133 	e = __gen_payload(base, offset, len, reg);
1134 	*dreg = reg;
1135 
1136 	return e;
1137 }
1138 
1139 static struct nftnl_expr *
gen_lookup(uint32_t sreg,const char * set_name,uint32_t set_id,uint32_t flags)1140 gen_lookup(uint32_t sreg, const char *set_name, uint32_t set_id, uint32_t flags)
1141 {
1142 	struct nftnl_expr *e = nftnl_expr_alloc("lookup");
1143 
1144 	if (!e)
1145 		return NULL;
1146 	nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_SREG, sreg);
1147 	nftnl_expr_set_str(e, NFTNL_EXPR_LOOKUP_SET, set_name);
1148 	nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_SET_ID, set_id);
1149 	nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_FLAGS, flags);
1150 	return e;
1151 }
1152 
1153 /* from nftables:include/datatype.h, TYPE_BITS */
1154 #define CONCAT_TYPE_BITS	6
1155 
1156 /* from nftables:include/datatype.h, enum datatypes */
1157 #define NFT_DATATYPE_IPADDR	7
1158 #define NFT_DATATYPE_ETHERADDR	9
1159 
__add_nft_among(struct nft_handle * h,const char * table,struct nftnl_rule * r,struct nft_among_pair * pairs,int cnt,bool dst,bool inv,bool ip)1160 static int __add_nft_among(struct nft_handle *h, const char *table,
1161 			   struct nftnl_rule *r, struct nft_among_pair *pairs,
1162 			   int cnt, bool dst, bool inv, bool ip)
1163 {
1164 	uint32_t set_id, type = NFT_DATATYPE_ETHERADDR, len = ETH_ALEN;
1165 	/* { !dst, dst } */
1166 	static const int eth_addr_off[] = {
1167 		offsetof(struct ether_header, ether_shost),
1168 		offsetof(struct ether_header, ether_dhost)
1169 	};
1170 	static const int ip_addr_off[] = {
1171 		offsetof(struct iphdr, saddr),
1172 		offsetof(struct iphdr, daddr)
1173 	};
1174 	struct nftnl_expr *e;
1175 	struct nftnl_set *s;
1176 	uint32_t flags = 0;
1177 	uint8_t reg;
1178 	int idx = 0;
1179 
1180 	if (ip) {
1181 		type = type << CONCAT_TYPE_BITS | NFT_DATATYPE_IPADDR;
1182 		len += sizeof(struct in_addr) + NETLINK_ALIGN - 1;
1183 		len &= ~(NETLINK_ALIGN - 1);
1184 		flags = NFT_SET_INTERVAL | NFT_SET_CONCAT;
1185 	}
1186 
1187 	s = add_anon_set(h, table, flags, type, len, cnt);
1188 	if (!s)
1189 		return -ENOMEM;
1190 	set_id = nftnl_set_get_u32(s, NFTNL_SET_ID);
1191 
1192 	if (ip) {
1193 		uint8_t field_len[2] = { ETH_ALEN, sizeof(struct in_addr) };
1194 
1195 		nftnl_set_set_data(s, NFTNL_SET_DESC_CONCAT,
1196 				   field_len, sizeof(field_len));
1197 	}
1198 
1199 	for (idx = 0; idx < cnt; idx++) {
1200 		struct nftnl_set_elem *elem = nftnl_set_elem_alloc();
1201 
1202 		if (!elem)
1203 			return -ENOMEM;
1204 		nftnl_set_elem_set(elem, NFTNL_SET_ELEM_KEY,
1205 				   &pairs[idx], len);
1206 		if (ip) {
1207 			struct in_addr tmp = pairs[idx].in;
1208 
1209 			if (tmp.s_addr == INADDR_ANY)
1210 				pairs[idx].in.s_addr = INADDR_BROADCAST;
1211 			nftnl_set_elem_set(elem, NFTNL_SET_ELEM_KEY_END,
1212 					   &pairs[idx], len);
1213 			pairs[idx].in = tmp;
1214 		}
1215 		nftnl_set_elem_add(s, elem);
1216 	}
1217 
1218 	e = gen_payload(h, NFT_PAYLOAD_LL_HEADER,
1219 			eth_addr_off[dst], ETH_ALEN, &reg);
1220 	if (!e)
1221 		return -ENOMEM;
1222 	nftnl_rule_add_expr(r, e);
1223 
1224 	if (ip) {
1225 		reg = nft_get_next_reg(reg, ETH_ALEN);
1226 		e = __gen_payload(NFT_PAYLOAD_NETWORK_HEADER, ip_addr_off[dst],
1227 				  sizeof(struct in_addr), reg);
1228 		if (!e)
1229 			return -ENOMEM;
1230 		nftnl_rule_add_expr(r, e);
1231 	}
1232 
1233 	reg = NFT_REG_1;
1234 	e = gen_lookup(reg, "__set%d", set_id, inv);
1235 	if (!e)
1236 		return -ENOMEM;
1237 	nftnl_rule_add_expr(r, e);
1238 
1239 	return 0;
1240 }
1241 
add_nft_among(struct nft_handle * h,struct nftnl_rule * r,struct xt_entry_match * m)1242 static int add_nft_among(struct nft_handle *h,
1243 			 struct nftnl_rule *r, struct xt_entry_match *m)
1244 {
1245 	struct nft_among_data *data = (struct nft_among_data *)m->data;
1246 	const char *table = nftnl_rule_get(r, NFTNL_RULE_TABLE);
1247 
1248 	if ((data->src.cnt && data->src.ip) ||
1249 	    (data->dst.cnt && data->dst.ip)) {
1250 		uint16_t eth_p_ip = htons(ETH_P_IP);
1251 		uint8_t reg;
1252 
1253 		add_meta(h, r, NFT_META_PROTOCOL, &reg);
1254 		add_cmp_ptr(r, NFT_CMP_EQ, &eth_p_ip, 2, reg);
1255 	}
1256 
1257 	if (data->src.cnt)
1258 		__add_nft_among(h, table, r, data->pairs, data->src.cnt,
1259 				false, data->src.inv, data->src.ip);
1260 	if (data->dst.cnt)
1261 		__add_nft_among(h, table, r, data->pairs + data->src.cnt,
1262 				data->dst.cnt, true, data->dst.inv,
1263 				data->dst.ip);
1264 	return 0;
1265 }
1266 
expr_gen_range_cmp16(struct nftnl_rule * r,uint16_t lo,uint16_t hi,bool invert,uint8_t reg)1267 static int expr_gen_range_cmp16(struct nftnl_rule *r,
1268 				uint16_t lo,
1269 				uint16_t hi,
1270 				bool invert, uint8_t reg)
1271 {
1272 	struct nftnl_expr *e;
1273 
1274 	if (lo == hi) {
1275 		add_cmp_u16(r, htons(lo), invert ? NFT_CMP_NEQ : NFT_CMP_EQ, reg);
1276 		return 0;
1277 	}
1278 
1279 	if (lo == 0 && hi < 0xffff) {
1280 		add_cmp_u16(r, htons(hi) , invert ? NFT_CMP_GT : NFT_CMP_LTE, reg);
1281 		return 0;
1282 	}
1283 
1284 	e = nftnl_expr_alloc("range");
1285 	if (!e)
1286 		return -ENOMEM;
1287 
1288 	nftnl_expr_set_u32(e, NFTNL_EXPR_RANGE_SREG, reg);
1289 	nftnl_expr_set_u32(e, NFTNL_EXPR_RANGE_OP, invert ? NFT_RANGE_NEQ : NFT_RANGE_EQ);
1290 
1291 	lo = htons(lo);
1292 	nftnl_expr_set(e, NFTNL_EXPR_RANGE_FROM_DATA, &lo, sizeof(lo));
1293 	hi = htons(hi);
1294 	nftnl_expr_set(e, NFTNL_EXPR_RANGE_TO_DATA, &hi, sizeof(hi));
1295 
1296 	nftnl_rule_add_expr(r, e);
1297 	return 0;
1298 }
1299 
add_nft_tcpudp(struct nft_handle * h,struct nftnl_rule * r,uint16_t src[2],bool invert_src,uint16_t dst[2],bool invert_dst)1300 static int add_nft_tcpudp(struct nft_handle *h,struct nftnl_rule *r,
1301 			  uint16_t src[2], bool invert_src,
1302 			  uint16_t dst[2], bool invert_dst)
1303 {
1304 	struct nftnl_expr *expr;
1305 	uint8_t op = NFT_CMP_EQ;
1306 	uint8_t reg;
1307 	int ret;
1308 
1309 	if (src[0] && src[0] == src[1] &&
1310 	    dst[0] && dst[0] == dst[1] &&
1311 	    invert_src == invert_dst) {
1312 		uint32_t combined = dst[0] | (src[0] << 16);
1313 
1314 		if (invert_src)
1315 			op = NFT_CMP_NEQ;
1316 
1317 		expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 0, 4, &reg);
1318 		if (!expr)
1319 			return -ENOMEM;
1320 
1321 		nftnl_rule_add_expr(r, expr);
1322 		add_cmp_u32(r, htonl(combined), op, reg);
1323 		return 0;
1324 	}
1325 
1326 	if (src[0] || src[1] < 0xffff) {
1327 		expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 0, 2, &reg);
1328 		if (!expr)
1329 			return -ENOMEM;
1330 
1331 		nftnl_rule_add_expr(r, expr);
1332 		ret = expr_gen_range_cmp16(r, src[0], src[1], invert_src, reg);
1333 		if (ret)
1334 			return ret;
1335 	}
1336 
1337 	if (dst[0] || dst[1] < 0xffff) {
1338 		expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 2, 2, &reg);
1339 		if (!expr)
1340 			return -ENOMEM;
1341 
1342 		nftnl_rule_add_expr(r, expr);
1343 		ret = expr_gen_range_cmp16(r, dst[0], dst[1], invert_dst, reg);
1344 		if (ret)
1345 			return ret;
1346 	}
1347 
1348 	return 0;
1349 }
1350 
1351 /* without this, "iptables -A INPUT -m udp" is
1352  * turned into "iptables -A INPUT", which isn't
1353  * compatible with iptables-legacy behaviour.
1354  */
udp_all_zero(const struct xt_udp * u)1355 static bool udp_all_zero(const struct xt_udp *u)
1356 {
1357 	static const struct xt_udp zero = {
1358 		.spts[1] = 0xffff,
1359 		.dpts[1] = 0xffff,
1360 	};
1361 
1362 	return memcmp(u, &zero, sizeof(*u)) == 0;
1363 }
1364 
add_nft_udp(struct nft_handle * h,struct nftnl_rule * r,struct xt_entry_match * m)1365 static int add_nft_udp(struct nft_handle *h, struct nftnl_rule *r,
1366 		       struct xt_entry_match *m)
1367 {
1368 	struct xt_udp *udp = (void *)m->data;
1369 
1370 	if (udp->invflags > XT_UDP_INV_MASK ||
1371 	    udp_all_zero(udp)) {
1372 		struct nftnl_expr *expr = nftnl_expr_alloc("match");
1373 		int ret;
1374 
1375 		ret = __add_match(expr, m);
1376 		nftnl_rule_add_expr(r, expr);
1377 		return ret;
1378 	}
1379 
1380 	if (nftnl_rule_get_u32(r, NFTNL_RULE_COMPAT_PROTO) != IPPROTO_UDP)
1381 		xtables_error(PARAMETER_PROBLEM, "UDP match requires '-p udp'");
1382 
1383 	return add_nft_tcpudp(h, r, udp->spts, udp->invflags & XT_UDP_INV_SRCPT,
1384 			      udp->dpts, udp->invflags & XT_UDP_INV_DSTPT);
1385 }
1386 
add_nft_tcpflags(struct nft_handle * h,struct nftnl_rule * r,uint8_t cmp,uint8_t mask,bool invert)1387 static int add_nft_tcpflags(struct nft_handle *h, struct nftnl_rule *r,
1388 			    uint8_t cmp, uint8_t mask,
1389 			    bool invert)
1390 {
1391 	struct nftnl_expr *e;
1392 	uint8_t reg;
1393 
1394 	e = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 13, 1, &reg);
1395 
1396 	if (!e)
1397 		return -ENOMEM;
1398 
1399 	nftnl_rule_add_expr(r, e);
1400 
1401 	add_bitwise(h, r, &mask, 1, reg, &reg);
1402 	add_cmp_u8(r, cmp, invert ? NFT_CMP_NEQ : NFT_CMP_EQ, reg);
1403 
1404 	return 0;
1405 }
1406 
tcp_all_zero(const struct xt_tcp * t)1407 static bool tcp_all_zero(const struct xt_tcp *t)
1408 {
1409 	static const struct xt_tcp zero = {
1410 		.spts[1] = 0xffff,
1411 		.dpts[1] = 0xffff,
1412 	};
1413 
1414 	return memcmp(t, &zero, sizeof(*t)) == 0;
1415 }
1416 
add_nft_tcp(struct nft_handle * h,struct nftnl_rule * r,struct xt_entry_match * m)1417 static int add_nft_tcp(struct nft_handle *h, struct nftnl_rule *r,
1418 		       struct xt_entry_match *m)
1419 {
1420 	static const uint8_t supported = XT_TCP_INV_SRCPT | XT_TCP_INV_DSTPT | XT_TCP_INV_FLAGS;
1421 	struct xt_tcp *tcp = (void *)m->data;
1422 
1423 	if (tcp->invflags & ~supported || tcp->option ||
1424 	    tcp_all_zero(tcp)) {
1425 		struct nftnl_expr *expr = nftnl_expr_alloc("match");
1426 		int ret;
1427 
1428 		ret = __add_match(expr, m);
1429 		nftnl_rule_add_expr(r, expr);
1430 		return ret;
1431 	}
1432 
1433 	if (nftnl_rule_get_u32(r, NFTNL_RULE_COMPAT_PROTO) != IPPROTO_TCP)
1434 		xtables_error(PARAMETER_PROBLEM, "TCP match requires '-p tcp'");
1435 
1436 	if (tcp->flg_mask) {
1437 		int ret = add_nft_tcpflags(h, r, tcp->flg_cmp, tcp->flg_mask,
1438 					   tcp->invflags & XT_TCP_INV_FLAGS);
1439 
1440 		if (ret < 0)
1441 			return ret;
1442 	}
1443 
1444 	return add_nft_tcpudp(h, r, tcp->spts, tcp->invflags & XT_TCP_INV_SRCPT,
1445 			      tcp->dpts, tcp->invflags & XT_TCP_INV_DSTPT);
1446 }
1447 
add_nft_mark(struct nft_handle * h,struct nftnl_rule * r,struct xt_entry_match * m)1448 static int add_nft_mark(struct nft_handle *h, struct nftnl_rule *r,
1449 			struct xt_entry_match *m)
1450 {
1451 	struct xt_mark_mtinfo1 *mark = (void *)m->data;
1452 	uint8_t reg;
1453 	int op;
1454 
1455 	add_meta(h, r, NFT_META_MARK, &reg);
1456 	if (mark->mask != 0xffffffff)
1457 		add_bitwise(h, r, (uint8_t *)&mark->mask, sizeof(uint32_t), reg, &reg);
1458 
1459 	if (mark->invert)
1460 		op = NFT_CMP_NEQ;
1461 	else
1462 		op = NFT_CMP_EQ;
1463 
1464 	add_cmp_u32(r, mark->mark, op, reg);
1465 
1466 	return 0;
1467 }
1468 
add_match(struct nft_handle * h,struct nft_rule_ctx * ctx,struct nftnl_rule * r,struct xt_entry_match * m)1469 int add_match(struct nft_handle *h, struct nft_rule_ctx *ctx,
1470 	      struct nftnl_rule *r, struct xt_entry_match *m)
1471 {
1472 	struct nftnl_expr *expr;
1473 	int ret;
1474 
1475 	switch (ctx->command) {
1476 	case NFT_COMPAT_RULE_APPEND:
1477 	case NFT_COMPAT_RULE_INSERT:
1478 	case NFT_COMPAT_RULE_REPLACE:
1479 		if (!strcmp(m->u.user.name, "limit"))
1480 			return add_nft_limit(r, m);
1481 		else if (!strcmp(m->u.user.name, "among"))
1482 			return add_nft_among(h, r, m);
1483 		else if (!strcmp(m->u.user.name, "udp"))
1484 			return add_nft_udp(h, r, m);
1485 		else if (!strcmp(m->u.user.name, "tcp"))
1486 			return add_nft_tcp(h, r, m);
1487 		else if (!strcmp(m->u.user.name, "mark"))
1488 			return add_nft_mark(h, r, m);
1489 		break;
1490 	default:
1491 		break;
1492 	}
1493 
1494 	expr = nftnl_expr_alloc("match");
1495 	if (expr == NULL)
1496 		return -ENOMEM;
1497 
1498 	ret = __add_match(expr, m);
1499 	nftnl_rule_add_expr(r, expr);
1500 
1501 	return ret;
1502 }
1503 
__add_target(struct nftnl_expr * e,struct xt_entry_target * t)1504 static int __add_target(struct nftnl_expr *e, struct xt_entry_target *t)
1505 {
1506 	void *info;
1507 
1508 	nftnl_expr_set(e, NFTNL_EXPR_TG_NAME, t->u.user.name,
1509 			  strlen(t->u.user.name));
1510 	nftnl_expr_set_u32(e, NFTNL_EXPR_TG_REV, t->u.user.revision);
1511 
1512 	info = xtables_calloc(1, t->u.target_size);
1513 	memcpy(info, t->data, t->u.target_size - sizeof(*t));
1514 	nftnl_expr_set(e, NFTNL_EXPR_TG_INFO, info, t->u.target_size - sizeof(*t));
1515 
1516 	return 0;
1517 }
1518 
add_meta_nftrace(struct nftnl_rule * r)1519 static int add_meta_nftrace(struct nftnl_rule *r)
1520 {
1521 	struct nftnl_expr *expr;
1522 
1523 	expr = nftnl_expr_alloc("immediate");
1524 	if (expr == NULL)
1525 		return -ENOMEM;
1526 
1527 	nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG32_01);
1528 	nftnl_expr_set_u8(expr, NFTNL_EXPR_IMM_DATA, 1);
1529 	nftnl_rule_add_expr(r, expr);
1530 
1531 	expr = nftnl_expr_alloc("meta");
1532 	if (expr == NULL)
1533 		return -ENOMEM;
1534 	nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_NFTRACE);
1535 	nftnl_expr_set_u32(expr, NFTNL_EXPR_META_SREG, NFT_REG32_01);
1536 
1537 	nftnl_rule_add_expr(r, expr);
1538 	return 0;
1539 }
1540 
add_target(struct nftnl_rule * r,struct xt_entry_target * t)1541 int add_target(struct nftnl_rule *r, struct xt_entry_target *t)
1542 {
1543 	struct nftnl_expr *expr;
1544 	int ret;
1545 
1546 	if (strcmp(t->u.user.name, "TRACE") == 0)
1547 		return add_meta_nftrace(r);
1548 
1549 	expr = nftnl_expr_alloc("target");
1550 	if (expr == NULL)
1551 		return -ENOMEM;
1552 
1553 	ret = __add_target(expr, t);
1554 	nftnl_rule_add_expr(r, expr);
1555 
1556 	return ret;
1557 }
1558 
add_jumpto(struct nftnl_rule * r,const char * name,int verdict)1559 int add_jumpto(struct nftnl_rule *r, const char *name, int verdict)
1560 {
1561 	struct nftnl_expr *expr;
1562 
1563 	expr = nftnl_expr_alloc("immediate");
1564 	if (expr == NULL)
1565 		return -ENOMEM;
1566 
1567 	nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT);
1568 	nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_VERDICT, verdict);
1569 	nftnl_expr_set_str(expr, NFTNL_EXPR_IMM_CHAIN, (char *)name);
1570 	nftnl_rule_add_expr(r, expr);
1571 
1572 	return 0;
1573 }
1574 
add_verdict(struct nftnl_rule * r,int verdict)1575 int add_verdict(struct nftnl_rule *r, int verdict)
1576 {
1577 	struct nftnl_expr *expr;
1578 
1579 	expr = nftnl_expr_alloc("immediate");
1580 	if (expr == NULL)
1581 		return -ENOMEM;
1582 
1583 	nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT);
1584 	nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_VERDICT, verdict);
1585 	nftnl_rule_add_expr(r, expr);
1586 
1587 	return 0;
1588 }
1589 
add_action(struct nftnl_rule * r,struct iptables_command_state * cs,bool goto_set)1590 int add_action(struct nftnl_rule *r, struct iptables_command_state *cs,
1591 	       bool goto_set)
1592 {
1593 	int ret = 0;
1594 
1595 	/* If no target at all, add nothing (default to continue) */
1596 	if (cs->target != NULL) {
1597 		/* Standard target? */
1598 		if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
1599 			ret = add_verdict(r, NF_ACCEPT);
1600 		else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
1601 			ret = add_verdict(r, NF_DROP);
1602 		else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
1603 			ret = add_verdict(r, NFT_RETURN);
1604 		else if (strcmp(cs->jumpto, "NFLOG") == 0)
1605 			ret = add_log(r, cs);
1606 		else
1607 			ret = add_target(r, cs->target->t);
1608 	} else if (strlen(cs->jumpto) > 0) {
1609 		/* Not standard, then it's a go / jump to chain */
1610 		if (goto_set)
1611 			ret = add_jumpto(r, cs->jumpto, NFT_GOTO);
1612 		else
1613 			ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
1614 	}
1615 	return ret;
1616 }
1617 
add_log(struct nftnl_rule * r,struct iptables_command_state * cs)1618 int add_log(struct nftnl_rule *r, struct iptables_command_state *cs)
1619 {
1620 	struct nftnl_expr *expr;
1621 	struct xt_nflog_info *info = (struct xt_nflog_info *)cs->target->t->data;
1622 
1623 	expr = nftnl_expr_alloc("log");
1624 	if (!expr)
1625 		return -ENOMEM;
1626 
1627 	if (info->prefix[0] != '\0')
1628 		nftnl_expr_set_str(expr, NFTNL_EXPR_LOG_PREFIX,
1629 				   cs->target->udata);
1630 
1631 	nftnl_expr_set_u16(expr, NFTNL_EXPR_LOG_GROUP, info->group);
1632 	if (info->flags & XT_NFLOG_F_COPY_LEN)
1633 		nftnl_expr_set_u32(expr, NFTNL_EXPR_LOG_SNAPLEN,
1634 				   info->len);
1635 	if (info->threshold)
1636 		nftnl_expr_set_u16(expr, NFTNL_EXPR_LOG_QTHRESHOLD,
1637 				   info->threshold);
1638 
1639 	nftnl_rule_add_expr(r, expr);
1640 	return 0;
1641 }
1642 
nft_rule_print_debug(struct nft_handle * h,struct nftnl_rule * r,struct nlmsghdr * nlh)1643 static void nft_rule_print_debug(struct nft_handle *h,
1644 				 struct nftnl_rule *r, struct nlmsghdr *nlh)
1645 {
1646 	if (h->verbose > 1) {
1647 		nftnl_rule_fprintf(stdout, r, 0, 0);
1648 		fprintf(stdout, "\n");
1649 	}
1650 	if (h->verbose > 2)
1651 		mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len,
1652 				  sizeof(struct nfgenmsg));
1653 }
1654 
add_counters(struct nftnl_rule * r,uint64_t packets,uint64_t bytes)1655 int add_counters(struct nftnl_rule *r, uint64_t packets, uint64_t bytes)
1656 {
1657 	struct nftnl_expr *expr;
1658 
1659 	expr = nftnl_expr_alloc("counter");
1660 	if (expr == NULL)
1661 		return -ENOMEM;
1662 
1663 	nftnl_expr_set_u64(expr, NFTNL_EXPR_CTR_PACKETS, packets);
1664 	nftnl_expr_set_u64(expr, NFTNL_EXPR_CTR_BYTES, bytes);
1665 
1666 	nftnl_rule_add_expr(r, expr);
1667 
1668 	return 0;
1669 }
1670 
1671 enum udata_type {
1672 	UDATA_TYPE_COMMENT,
1673 	UDATA_TYPE_EBTABLES_POLICY,
1674 	__UDATA_TYPE_MAX,
1675 };
1676 #define UDATA_TYPE_MAX (__UDATA_TYPE_MAX - 1)
1677 
parse_udata_cb(const struct nftnl_udata * attr,void * data)1678 static int parse_udata_cb(const struct nftnl_udata *attr, void *data)
1679 {
1680 	unsigned char *value = nftnl_udata_get(attr);
1681 	uint8_t type = nftnl_udata_type(attr);
1682 	uint8_t len = nftnl_udata_len(attr);
1683 	const struct nftnl_udata **tb = data;
1684 
1685 	switch (type) {
1686 	case UDATA_TYPE_COMMENT:
1687 		if (value[len - 1] != '\0')
1688 			return -1;
1689 		break;
1690 	case UDATA_TYPE_EBTABLES_POLICY:
1691 		break;
1692 	default:
1693 		return 0;
1694 	}
1695 	tb[type] = attr;
1696 	return 0;
1697 }
1698 
get_comment(const void * data,uint32_t data_len)1699 char *get_comment(const void *data, uint32_t data_len)
1700 {
1701 	const struct nftnl_udata *tb[UDATA_TYPE_MAX + 1] = {};
1702 
1703 	if (nftnl_udata_parse(data, data_len, parse_udata_cb, tb) < 0)
1704 		return NULL;
1705 
1706 	if (!tb[UDATA_TYPE_COMMENT])
1707 		return NULL;
1708 
1709 	return nftnl_udata_get(tb[UDATA_TYPE_COMMENT]);
1710 }
1711 
add_compat(struct nftnl_rule * r,uint32_t proto,bool inv)1712 void add_compat(struct nftnl_rule *r, uint32_t proto, bool inv)
1713 {
1714 	nftnl_rule_set_u32(r, NFTNL_RULE_COMPAT_PROTO, proto);
1715 	nftnl_rule_set_u32(r, NFTNL_RULE_COMPAT_FLAGS,
1716 			      inv ? NFT_RULE_COMPAT_F_INV : 0);
1717 }
1718 
1719 struct nftnl_rule *
nft_rule_new(struct nft_handle * h,struct nft_rule_ctx * ctx,const char * chain,const char * table,struct iptables_command_state * cs)1720 nft_rule_new(struct nft_handle *h, struct nft_rule_ctx *ctx,
1721 	     const char *chain, const char *table,
1722 	     struct iptables_command_state *cs)
1723 {
1724 	struct nftnl_rule *r;
1725 
1726 	r = nftnl_rule_alloc();
1727 	if (r == NULL)
1728 		return NULL;
1729 
1730 	nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, h->family);
1731 	nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
1732 	nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
1733 
1734 	if (h->ops->add(h, ctx, r, cs) < 0)
1735 		goto err;
1736 
1737 	return r;
1738 err:
1739 	nftnl_rule_free(r);
1740 	return NULL;
1741 }
1742 
1743 int
nft_rule_append(struct nft_handle * h,const char * chain,const char * table,struct nftnl_rule * r,struct nftnl_rule * ref,bool verbose)1744 nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
1745 		struct nftnl_rule *r, struct nftnl_rule *ref, bool verbose)
1746 {
1747 	struct nft_chain *c;
1748 	int type;
1749 
1750 	nft_xt_builtin_init(h, table, chain);
1751 
1752 	nft_fn = nft_rule_append;
1753 
1754 	if (ref) {
1755 		nftnl_rule_set_u64(r, NFTNL_RULE_HANDLE,
1756 				   nftnl_rule_get_u64(ref, NFTNL_RULE_HANDLE));
1757 		type = NFT_COMPAT_RULE_REPLACE;
1758 	} else
1759 		type = NFT_COMPAT_RULE_APPEND;
1760 
1761 	if (batch_rule_add(h, type, r) == NULL)
1762 		return 0;
1763 
1764 	if (verbose)
1765 		h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
1766 
1767 	if (ref) {
1768 		nftnl_chain_rule_insert_at(r, ref);
1769 		nftnl_chain_rule_del(ref);
1770 		nftnl_rule_free(ref);
1771 	} else {
1772 		c = nft_chain_find(h, table, chain);
1773 		if (!c) {
1774 			errno = ENOENT;
1775 			return 0;
1776 		}
1777 		nftnl_chain_rule_add_tail(r, c->nftnl);
1778 	}
1779 
1780 	return 1;
1781 }
1782 
1783 bool
nft_rule_print_save(struct nft_handle * h,const struct nftnl_rule * r,enum nft_rule_print type,unsigned int format)1784 nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
1785 		    enum nft_rule_print type, unsigned int format)
1786 {
1787 	const char *chain = nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
1788 	struct iptables_command_state cs = {};
1789 	struct nft_family_ops *ops = h->ops;
1790 	bool ret;
1791 
1792 	ret = ops->rule_to_cs(h, r, &cs);
1793 
1794 	if (!(format & (FMT_NOCOUNTS | FMT_C_COUNTS)))
1795 		printf("[%llu:%llu] ", (unsigned long long)cs.counters.pcnt,
1796 				       (unsigned long long)cs.counters.bcnt);
1797 
1798 	/* print chain name */
1799 	switch(type) {
1800 	case NFT_RULE_APPEND:
1801 		printf("-A %s", chain);
1802 		break;
1803 	case NFT_RULE_DEL:
1804 		printf("-D %s", chain);
1805 		break;
1806 	}
1807 
1808 	if (ops->save_rule)
1809 		ops->save_rule(&cs, format);
1810 
1811 	if (ops->clear_cs)
1812 		ops->clear_cs(&cs);
1813 
1814 	return ret;
1815 }
1816 
nft_rule_is_policy_rule(struct nftnl_rule * r)1817 static bool nft_rule_is_policy_rule(struct nftnl_rule *r)
1818 {
1819 	const struct nftnl_udata *tb[UDATA_TYPE_MAX + 1] = {};
1820 	const void *data;
1821 	uint32_t len;
1822 
1823 	if (!nftnl_rule_is_set(r, NFTNL_RULE_USERDATA))
1824 		return false;
1825 
1826 	data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len);
1827 	if (nftnl_udata_parse(data, len, parse_udata_cb, tb) < 0)
1828 		return NULL;
1829 
1830 	if (!tb[UDATA_TYPE_EBTABLES_POLICY] ||
1831 	    nftnl_udata_get_u32(tb[UDATA_TYPE_EBTABLES_POLICY]) != 1)
1832 		return false;
1833 
1834 	return true;
1835 }
1836 
nft_chain_last_rule(struct nftnl_chain * c)1837 static struct nftnl_rule *nft_chain_last_rule(struct nftnl_chain *c)
1838 {
1839 	struct nftnl_rule *r = NULL, *last;
1840 	struct nftnl_rule_iter *iter;
1841 
1842 	iter = nftnl_rule_iter_create(c);
1843 	if (!iter)
1844 		return NULL;
1845 
1846 	do {
1847 		last = r;
1848 		r = nftnl_rule_iter_next(iter);
1849 	} while (r);
1850 	nftnl_rule_iter_destroy(iter);
1851 
1852 	return last;
1853 }
1854 
nft_bridge_chain_postprocess(struct nft_handle * h,struct nftnl_chain * c)1855 void nft_bridge_chain_postprocess(struct nft_handle *h,
1856 				  struct nftnl_chain *c)
1857 {
1858 	struct nftnl_rule *last = nft_chain_last_rule(c);
1859 	struct nftnl_expr_iter *iter;
1860 	struct nftnl_expr *expr;
1861 	int verdict;
1862 
1863 	if (!last || !nft_rule_is_policy_rule(last))
1864 		return;
1865 
1866 	iter = nftnl_expr_iter_create(last);
1867 	if (!iter)
1868 		return;
1869 
1870 	expr = nftnl_expr_iter_next(iter);
1871 	if (!expr ||
1872 	    strcmp("counter", nftnl_expr_get_str(expr, NFTNL_EXPR_NAME)))
1873 		goto out_iter;
1874 
1875 	expr = nftnl_expr_iter_next(iter);
1876 	if (!expr ||
1877 	    strcmp("immediate", nftnl_expr_get_str(expr, NFTNL_EXPR_NAME)) ||
1878 	    !nftnl_expr_is_set(expr, NFTNL_EXPR_IMM_VERDICT))
1879 		goto out_iter;
1880 
1881 	verdict = nftnl_expr_get_u32(expr, NFTNL_EXPR_IMM_VERDICT);
1882 	switch (verdict) {
1883 	case NF_ACCEPT:
1884 	case NF_DROP:
1885 		break;
1886 	default:
1887 		goto out_iter;
1888 	}
1889 
1890 	nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, verdict);
1891 	if (batch_rule_add(h, NFT_COMPAT_RULE_DELETE, last) == NULL)
1892 		fprintf(stderr, "Failed to delete old policy rule\n");
1893 	nftnl_chain_rule_del(last);
1894 out_iter:
1895 	nftnl_expr_iter_destroy(iter);
1896 }
1897 static const char *policy_name[NF_ACCEPT+1] = {
1898 	[NF_DROP] = "DROP",
1899 	[NF_ACCEPT] = "ACCEPT",
1900 };
1901 
nft_chain_save(struct nft_chain * nc,void * data)1902 int nft_chain_save(struct nft_chain *nc, void *data)
1903 {
1904 	struct nftnl_chain *c = nc->nftnl;
1905 	struct nft_handle *h = data;
1906 	const char *policy = NULL;
1907 
1908 	if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY)) {
1909 		policy = policy_name[nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY)];
1910 	} else if (nft_chain_builtin(c)) {
1911 		policy = "ACCEPT";
1912 	} else if (h->family == NFPROTO_BRIDGE) {
1913 		policy = "RETURN";
1914 	}
1915 
1916 	if (h->ops->save_chain)
1917 		h->ops->save_chain(c, policy);
1918 
1919 	return 0;
1920 }
1921 
1922 struct nft_rule_save_data {
1923 	struct nft_handle *h;
1924 	unsigned int format;
1925 	unsigned int errors;
1926 };
1927 
nft_rule_save_cb(struct nft_chain * c,void * data)1928 static int nft_rule_save_cb(struct nft_chain *c, void *data)
1929 {
1930 	struct nft_rule_save_data *d = data;
1931 	struct nftnl_rule_iter *iter;
1932 	struct nftnl_rule *r;
1933 
1934 	iter = nftnl_rule_iter_create(c->nftnl);
1935 	if (iter == NULL)
1936 		return 1;
1937 
1938 	r = nftnl_rule_iter_next(iter);
1939 	while (r != NULL) {
1940 		bool ret = nft_rule_print_save(d->h, r, NFT_RULE_APPEND, d->format);
1941 
1942 		if (!ret)
1943 			d->errors++;
1944 
1945 		r = nftnl_rule_iter_next(iter);
1946 	}
1947 
1948 	nftnl_rule_iter_destroy(iter);
1949 	return 0;
1950 }
1951 
nft_rule_save(struct nft_handle * h,const char * table,unsigned int format)1952 int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format)
1953 {
1954 	struct nft_rule_save_data d = {
1955 		.h = h,
1956 		.format = format,
1957 	};
1958 	int ret;
1959 
1960 	ret = nft_chain_foreach(h, table, nft_rule_save_cb, &d);
1961 
1962 	if (ret == 0 && d.errors)
1963 		xtables_error(VERSION_PROBLEM, "Cannot decode all rules provided by kernel");
1964 
1965 	/* the core expects 1 for success and 0 for error */
1966 	return ret == 0 ? 1 : 0;
1967 }
1968 
nft_set_batch_lookup_byid(struct nft_handle * h,uint32_t set_id)1969 struct nftnl_set *nft_set_batch_lookup_byid(struct nft_handle *h,
1970 					    uint32_t set_id)
1971 {
1972 	struct obj_update *n;
1973 
1974 	list_for_each_entry(n, &h->obj_list, head) {
1975 		if (n->type == NFT_COMPAT_SET_ADD &&
1976 		    nftnl_set_get_u32(n->set, NFTNL_SET_ID) == set_id)
1977 			return n->set;
1978 	}
1979 
1980 	return NULL;
1981 }
1982 
1983 static void
__nft_rule_flush(struct nft_handle * h,const char * table,const char * chain,bool verbose,bool skip)1984 __nft_rule_flush(struct nft_handle *h, const char *table,
1985 		 const char *chain, bool verbose, bool skip)
1986 {
1987 	struct obj_update *obj;
1988 	struct nftnl_rule *r;
1989 
1990 	if (verbose && chain)
1991 		fprintf(stdout, "Flushing chain `%s'\n", chain);
1992 
1993 	r = nftnl_rule_alloc();
1994 	if (r == NULL)
1995 		return;
1996 
1997 	nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
1998 	if (chain)
1999 		nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
2000 
2001 	obj = batch_rule_add(h, NFT_COMPAT_RULE_FLUSH, r);
2002 	if (!obj) {
2003 		nftnl_rule_free(r);
2004 		return;
2005 	}
2006 
2007 	obj->skip = skip;
2008 }
2009 
2010 struct nft_rule_flush_data {
2011 	struct nft_handle *h;
2012 	const char *table;
2013 	bool verbose;
2014 };
2015 
nft_rule_flush_cb(struct nft_chain * c,void * data)2016 static int nft_rule_flush_cb(struct nft_chain *c, void *data)
2017 {
2018 	const char *chain = nftnl_chain_get_str(c->nftnl, NFTNL_CHAIN_NAME);
2019 	struct nft_rule_flush_data *d = data;
2020 
2021 	batch_chain_flush(d->h, d->table, chain);
2022 	__nft_rule_flush(d->h, d->table, chain, d->verbose, false);
2023 	flush_rule_cache(d->h, d->table, c);
2024 	return 0;
2025 }
2026 
nft_rule_flush(struct nft_handle * h,const char * chain,const char * table,bool verbose)2027 int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
2028 		   bool verbose)
2029 {
2030 	struct nft_rule_flush_data d = {
2031 		.h = h,
2032 		.table = table,
2033 		.verbose = verbose,
2034 	};
2035 	struct nft_chain *c = NULL;
2036 	int ret = 0;
2037 
2038 	nft_fn = nft_rule_flush;
2039 
2040 	if (chain || verbose)
2041 		nft_xt_builtin_init(h, table, chain);
2042 	else if (!nft_table_find(h, table))
2043 		return 1;
2044 
2045 	if (chain) {
2046 		c = nft_chain_find(h, table, chain);
2047 		if (!c) {
2048 			errno = ENOENT;
2049 			return 0;
2050 		}
2051 	}
2052 
2053 	if (chain || !verbose) {
2054 		batch_chain_flush(h, table, chain);
2055 		__nft_rule_flush(h, table, chain, verbose, false);
2056 		flush_rule_cache(h, table, c);
2057 		return 1;
2058 	}
2059 
2060 	nft_cache_sort_chains(h, table);
2061 
2062 	ret = nft_chain_foreach(h, table, nft_rule_flush_cb, &d);
2063 
2064 	/* the core expects 1 for success and 0 for error */
2065 	return ret == 0 ? 1 : 0;
2066 }
2067 
nft_chain_user_add(struct nft_handle * h,const char * chain,const char * table)2068 int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table)
2069 {
2070 	const struct builtin_table *t;
2071 	struct nftnl_chain *c;
2072 
2073 	nft_fn = nft_chain_user_add;
2074 
2075 	t = nft_xt_builtin_table_init(h, table);
2076 
2077 	if (nft_chain_exists(h, table, chain)) {
2078 		errno = EEXIST;
2079 		return 0;
2080 	}
2081 
2082 	c = nftnl_chain_alloc();
2083 	if (c == NULL)
2084 		return 0;
2085 
2086 	nftnl_chain_set_u32(c, NFTNL_CHAIN_FAMILY, h->family);
2087 	nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table);
2088 	nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain);
2089 	if (h->family == NFPROTO_BRIDGE)
2090 		nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, NF_ACCEPT);
2091 
2092 	if (!batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c))
2093 		return 0;
2094 
2095 	nft_cache_add_chain(h, t, c);
2096 
2097 	/* the core expects 1 for success and 0 for error */
2098 	return 1;
2099 }
2100 
nft_chain_restore(struct nft_handle * h,const char * chain,const char * table)2101 int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table)
2102 {
2103 	const struct builtin_table *t;
2104 	struct obj_update *obj;
2105 	struct nftnl_chain *c;
2106 	struct nft_chain *nc;
2107 	bool created = false;
2108 
2109 	t = nft_xt_builtin_table_init(h, table);
2110 
2111 	nc = nft_chain_find(h, table, chain);
2112 	if (!nc) {
2113 		c = nftnl_chain_alloc();
2114 		if (!c)
2115 			return 0;
2116 
2117 		nftnl_chain_set_u32(c, NFTNL_CHAIN_FAMILY, h->family);
2118 		nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table);
2119 		nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain);
2120 		created = true;
2121 
2122 		nft_cache_add_chain(h, t, c);
2123 	} else {
2124 		c = nc->nftnl;
2125 
2126 		/* If the chain should vanish meanwhile, kernel genid changes
2127 		 * and the transaction is refreshed enabling the chain add
2128 		 * object. With the handle still set, kernel interprets it as a
2129 		 * chain replace job and errors since it is not found anymore.
2130 		 */
2131 		nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
2132 	}
2133 
2134 	__nft_rule_flush(h, table, chain, false, created);
2135 
2136 	obj = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
2137 	if (!obj)
2138 		return 0;
2139 
2140 	obj->skip = !created;
2141 
2142 	/* the core expects 1 for success and 0 for error */
2143 	return 1;
2144 }
2145 
2146 /* From linux/netlink.h */
2147 #ifndef NLM_F_NONREC
2148 #define NLM_F_NONREC	0x100	/* Do not delete recursively    */
2149 #endif
2150 
2151 struct chain_del_data {
2152 	struct nft_handle	*handle;
2153 	const char		*chain;
2154 	bool			verbose;
2155 };
2156 
nft_may_delete_chain(struct nftnl_chain * c)2157 static bool nft_may_delete_chain(struct nftnl_chain *c)
2158 {
2159 	if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY) &&
2160 	    nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY) != NF_ACCEPT)
2161 		return false;
2162 
2163 	return nftnl_rule_lookup_byindex(c, 0) == NULL;
2164 }
2165 
__nft_chain_del(struct nft_chain * nc,void * data)2166 static int __nft_chain_del(struct nft_chain *nc, void *data)
2167 {
2168 	struct chain_del_data *d = data;
2169 	struct nftnl_chain *c = nc->nftnl;
2170 	struct nft_handle *h = d->handle;
2171 	bool builtin = nft_chain_builtin(c);
2172 	struct obj_update *obj;
2173 	int ret = 0;
2174 
2175 	if (d->verbose && !builtin)
2176 		fprintf(stdout, "Deleting chain `%s'\n",
2177 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
2178 
2179 
2180 	/* XXX This triggers a fast lookup from the kernel. */
2181 	nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
2182 	obj = batch_chain_add(h, NFT_COMPAT_CHAIN_DEL, c);
2183 	if (!obj)
2184 		return -1;
2185 
2186 	if (builtin) {
2187 		obj->skip = !nft_may_delete_chain(c);
2188 		if (obj->skip && d->chain) {
2189 			/* complain if explicitly requested */
2190 			errno = EBUSY;
2191 			ret = -1;
2192 		}
2193 		*nc->base_slot = NULL;
2194 	}
2195 
2196 	/* nftnl_chain is freed when deleting the batch object */
2197 	nc->nftnl = NULL;
2198 
2199 	nft_chain_list_del(nc);
2200 	nft_chain_free(nc);
2201 	return ret;
2202 }
2203 
nft_chain_del(struct nft_handle * h,const char * chain,const char * table,bool verbose)2204 int nft_chain_del(struct nft_handle *h, const char *chain,
2205 		  const char *table, bool verbose)
2206 {
2207 	struct chain_del_data d = {
2208 		.handle = h,
2209 		.chain = chain,
2210 		.verbose = verbose,
2211 	};
2212 	struct nft_chain *c;
2213 	int ret = 0;
2214 
2215 	nft_fn = nft_chain_del;
2216 
2217 	if (chain) {
2218 		c = nft_chain_find(h, table, chain);
2219 		if (!c) {
2220 			errno = ENOENT;
2221 			return 0;
2222 		}
2223 
2224 		ret = __nft_chain_del(c, &d);
2225 		goto out;
2226 	}
2227 
2228 	if (verbose)
2229 		nft_cache_sort_chains(h, table);
2230 
2231 	ret = nft_chain_foreach(h, table, __nft_chain_del, &d);
2232 out:
2233 	/* the core expects 1 for success and 0 for error */
2234 	return ret == 0 ? 1 : 0;
2235 }
2236 
nft_chain_exists(struct nft_handle * h,const char * table,const char * chain)2237 bool nft_chain_exists(struct nft_handle *h,
2238 		      const char *table, const char *chain)
2239 {
2240 	const struct builtin_table *t = nft_table_builtin_find(h, table);
2241 
2242 	/* xtables does not support custom tables */
2243 	if (!t)
2244 		return false;
2245 
2246 	if (nft_chain_builtin_find(t, chain))
2247 		return true;
2248 
2249 	return !!nft_chain_find(h, table, chain);
2250 }
2251 
nft_chain_user_rename(struct nft_handle * h,const char * chain,const char * table,const char * newname)2252 int nft_chain_user_rename(struct nft_handle *h,const char *chain,
2253 			  const char *table, const char *newname)
2254 {
2255 	struct nftnl_chain *c;
2256 	struct nft_chain *nc;
2257 	uint64_t handle;
2258 
2259 	nft_fn = nft_chain_user_rename;
2260 
2261 	if (nft_chain_exists(h, table, newname)) {
2262 		errno = EEXIST;
2263 		return 0;
2264 	}
2265 
2266 	/* Find the old chain to be renamed */
2267 	nc = nft_chain_find(h, table, chain);
2268 	if (nc == NULL) {
2269 		errno = ENOENT;
2270 		return 0;
2271 	}
2272 	handle = nftnl_chain_get_u64(nc->nftnl, NFTNL_CHAIN_HANDLE);
2273 
2274 	/* Now prepare the new name for the chain */
2275 	c = nftnl_chain_alloc();
2276 	if (c == NULL)
2277 		return 0;
2278 
2279 	nftnl_chain_set_u32(c, NFTNL_CHAIN_FAMILY, h->family);
2280 	nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table);
2281 	nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, newname);
2282 	nftnl_chain_set_u64(c, NFTNL_CHAIN_HANDLE, handle);
2283 
2284 	if (!batch_chain_add(h, NFT_COMPAT_CHAIN_RENAME, c))
2285 		return 0;
2286 
2287 	/* the core expects 1 for success and 0 for error */
2288 	return 1;
2289 }
2290 
nft_table_find(struct nft_handle * h,const char * tablename)2291 bool nft_table_find(struct nft_handle *h, const char *tablename)
2292 {
2293 	const struct builtin_table *t;
2294 
2295 	t = nft_table_builtin_find(h, tablename);
2296 	return t ? h->cache->table[t->type].exists : false;
2297 }
2298 
nft_for_each_table(struct nft_handle * h,int (* func)(struct nft_handle * h,const char * tablename,void * data),void * data)2299 int nft_for_each_table(struct nft_handle *h,
2300 		       int (*func)(struct nft_handle *h, const char *tablename, void *data),
2301 		       void *data)
2302 {
2303 	int i;
2304 
2305 	for (i = 0; i < NFT_TABLE_MAX; i++) {
2306 		if (h->tables[i].name == NULL)
2307 			continue;
2308 
2309 		if (!h->cache->table[h->tables[i].type].exists)
2310 			continue;
2311 
2312 		func(h, h->tables[i].name, data);
2313 	}
2314 
2315 	return 0;
2316 }
2317 
__nft_table_flush(struct nft_handle * h,const char * table,bool exists)2318 static int __nft_table_flush(struct nft_handle *h, const char *table, bool exists)
2319 {
2320 	const struct builtin_table *_t;
2321 	struct obj_update *obj;
2322 	struct nftnl_table *t;
2323 
2324 	t = nftnl_table_alloc();
2325 	if (t == NULL)
2326 		return -1;
2327 
2328 	nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, h->family);
2329 	nftnl_table_set_str(t, NFTNL_TABLE_NAME, table);
2330 
2331 	obj = batch_table_add(h, NFT_COMPAT_TABLE_FLUSH, t);
2332 	if (!obj) {
2333 		nftnl_table_free(t);
2334 		return -1;
2335 	}
2336 
2337 	if (!exists)
2338 		obj->skip = 1;
2339 
2340 	_t = nft_table_builtin_find(h, table);
2341 	assert(_t);
2342 	h->cache->table[_t->type].exists = false;
2343 
2344 	flush_chain_cache(h, table);
2345 
2346 	return 0;
2347 }
2348 
nft_table_flush(struct nft_handle * h,const char * table)2349 int nft_table_flush(struct nft_handle *h, const char *table)
2350 {
2351 	const struct builtin_table *t;
2352 	int ret = 0;
2353 
2354 	nft_fn = nft_table_flush;
2355 
2356 	t = nft_table_builtin_find(h, table);
2357 	if (!t)
2358 		return 0;
2359 
2360 	ret = __nft_table_flush(h, table, h->cache->table[t->type].exists);
2361 
2362 	/* the core expects 1 for success and 0 for error */
2363 	return ret == 0 ? 1 : 0;
2364 }
2365 
__nft_rule_del(struct nft_handle * h,struct nftnl_rule * r)2366 static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule *r)
2367 {
2368 	struct obj_update *obj;
2369 
2370 	nftnl_rule_list_del(r);
2371 
2372 	if (!nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE) &&
2373 	    !nftnl_rule_get_u32(r, NFTNL_RULE_ID))
2374 		nftnl_rule_set_u32(r, NFTNL_RULE_ID, ++h->rule_id);
2375 
2376 	obj = batch_rule_add(h, NFT_COMPAT_RULE_DELETE, r);
2377 	if (!obj) {
2378 		nftnl_rule_free(r);
2379 		return -1;
2380 	}
2381 	return 1;
2382 }
2383 
nft_rule_cmp(struct nft_handle * h,struct nftnl_rule * r,struct nftnl_rule * rule)2384 static bool nft_rule_cmp(struct nft_handle *h, struct nftnl_rule *r,
2385 			 struct nftnl_rule *rule)
2386 {
2387 	struct iptables_command_state _cs = {}, this = {}, *cs = &_cs;
2388 	bool ret = false, ret_this, ret_that;
2389 
2390 	ret_this = h->ops->rule_to_cs(h, r, &this);
2391 	ret_that = h->ops->rule_to_cs(h, rule, cs);
2392 
2393 	DEBUGP("comparing with... ");
2394 #ifdef DEBUG_DEL
2395 	nft_rule_print_save(h, r, NFT_RULE_APPEND, 0);
2396 #endif
2397 	if (!ret_this || !ret_that)
2398 		DEBUGP("Cannot convert rules: %d %d\n", ret_this, ret_that);
2399 
2400 	if (!h->ops->is_same(cs, &this))
2401 		goto out;
2402 
2403 	if (!compare_matches(cs->matches, this.matches)) {
2404 		DEBUGP("Different matches\n");
2405 		goto out;
2406 	}
2407 
2408 	if (!compare_targets(cs->target, this.target)) {
2409 		DEBUGP("Different target\n");
2410 		goto out;
2411 	}
2412 
2413 	if ((!cs->target || !this.target) &&
2414 	    strcmp(cs->jumpto, this.jumpto) != 0) {
2415 		DEBUGP("Different verdict\n");
2416 		goto out;
2417 	}
2418 
2419 	ret = true;
2420 out:
2421 	h->ops->clear_cs(&this);
2422 	h->ops->clear_cs(cs);
2423 	return ret;
2424 }
2425 
2426 static struct nftnl_rule *
nft_rule_find(struct nft_handle * h,struct nft_chain * nc,struct nftnl_rule * rule,int rulenum)2427 nft_rule_find(struct nft_handle *h, struct nft_chain *nc,
2428 	      struct nftnl_rule *rule, int rulenum)
2429 {
2430 	struct nftnl_chain *c = nc->nftnl;
2431 	struct nftnl_rule *r;
2432 	struct nftnl_rule_iter *iter;
2433 	bool found = false;
2434 
2435 	if (rulenum >= 0)
2436 		/* Delete by rule number case */
2437 		return nftnl_rule_lookup_byindex(c, rulenum);
2438 
2439 	iter = nftnl_rule_iter_create(c);
2440 	if (iter == NULL)
2441 		return 0;
2442 
2443 	r = nftnl_rule_iter_next(iter);
2444 	while (r != NULL) {
2445 		found = nft_rule_cmp(h, r, rule);
2446 		if (found)
2447 			break;
2448 		r = nftnl_rule_iter_next(iter);
2449 	}
2450 
2451 	nftnl_rule_iter_destroy(iter);
2452 
2453 	return found ? r : NULL;
2454 }
2455 
nft_rule_check(struct nft_handle * h,const char * chain,const char * table,struct nftnl_rule * rule,bool verbose)2456 int nft_rule_check(struct nft_handle *h, const char *chain,
2457 		   const char *table, struct nftnl_rule *rule, bool verbose)
2458 {
2459 	struct nftnl_rule *r;
2460 	struct nft_chain *c;
2461 
2462 	nft_fn = nft_rule_check;
2463 
2464 	c = nft_chain_find(h, table, chain);
2465 	if (!c)
2466 		goto fail_enoent;
2467 
2468 	r = nft_rule_find(h, c, rule, -1);
2469 	if (r == NULL)
2470 		goto fail_enoent;
2471 
2472 	if (verbose)
2473 		h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
2474 
2475 	return 1;
2476 fail_enoent:
2477 	errno = ENOENT;
2478 	return 0;
2479 }
2480 
nft_rule_delete(struct nft_handle * h,const char * chain,const char * table,struct nftnl_rule * rule,bool verbose)2481 int nft_rule_delete(struct nft_handle *h, const char *chain,
2482 		    const char *table, struct nftnl_rule *rule, bool verbose)
2483 {
2484 	int ret = 0;
2485 	struct nftnl_rule *r;
2486 	struct nft_chain *c;
2487 
2488 	nft_fn = nft_rule_delete;
2489 
2490 	c = nft_chain_find(h, table, chain);
2491 	if (!c) {
2492 		errno = ENOENT;
2493 		return 0;
2494 	}
2495 
2496 	r = nft_rule_find(h, c, rule, -1);
2497 	if (r != NULL) {
2498 		ret =__nft_rule_del(h, r);
2499 		if (ret < 0)
2500 			errno = ENOMEM;
2501 		if (verbose)
2502 			h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
2503 	} else
2504 		errno = ENOENT;
2505 
2506 	return ret;
2507 }
2508 
2509 static struct nftnl_rule *
nft_rule_add(struct nft_handle * h,const char * chain,const char * table,struct nftnl_rule * r,struct nftnl_rule * ref,bool verbose)2510 nft_rule_add(struct nft_handle *h, const char *chain,
2511 	     const char *table, struct nftnl_rule *r,
2512 	     struct nftnl_rule *ref, bool verbose)
2513 {
2514 	uint64_t ref_id;
2515 
2516 	if (ref) {
2517 		ref_id = nftnl_rule_get_u64(ref, NFTNL_RULE_HANDLE);
2518 		if (ref_id > 0) {
2519 			nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, ref_id);
2520 			DEBUGP("adding after rule handle %"PRIu64"\n", ref_id);
2521 		} else {
2522 			ref_id = nftnl_rule_get_u32(ref, NFTNL_RULE_ID);
2523 			if (!ref_id) {
2524 				ref_id = ++h->rule_id;
2525 				nftnl_rule_set_u32(ref, NFTNL_RULE_ID, ref_id);
2526 			}
2527 			nftnl_rule_set_u32(r, NFTNL_RULE_POSITION_ID, ref_id);
2528 			DEBUGP("adding after rule ID %"PRIu64"\n", ref_id);
2529 		}
2530 	}
2531 
2532 	if (!batch_rule_add(h, NFT_COMPAT_RULE_INSERT, r))
2533 		return NULL;
2534 
2535 	if (verbose)
2536 		h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
2537 
2538 	return r;
2539 }
2540 
nft_rule_insert(struct nft_handle * h,const char * chain,const char * table,struct nftnl_rule * new_rule,int rulenum,bool verbose)2541 int nft_rule_insert(struct nft_handle *h, const char *chain,
2542 		    const char *table, struct nftnl_rule *new_rule, int rulenum,
2543 		    bool verbose)
2544 {
2545 	struct nftnl_rule *r = NULL;
2546 	struct nft_chain *c;
2547 
2548 	nft_xt_builtin_init(h, table, chain);
2549 
2550 	nft_fn = nft_rule_insert;
2551 
2552 	c = nft_chain_find(h, table, chain);
2553 	if (!c) {
2554 		errno = ENOENT;
2555 		goto err;
2556 	}
2557 
2558 	if (rulenum > 0) {
2559 		r = nft_rule_find(h, c, new_rule, rulenum);
2560 		if (r == NULL) {
2561 			/* special case: iptables allows to insert into
2562 			 * rule_count + 1 position.
2563 			 */
2564 			r = nft_rule_find(h, c, new_rule, rulenum - 1);
2565 			if (r != NULL)
2566 				return nft_rule_append(h, chain, table,
2567 						       new_rule, NULL, verbose);
2568 
2569 			errno = E2BIG;
2570 			goto err;
2571 		}
2572 	}
2573 
2574 	new_rule = nft_rule_add(h, chain, table, new_rule, r, verbose);
2575 	if (!new_rule)
2576 		goto err;
2577 
2578 	if (r)
2579 		nftnl_chain_rule_insert_at(new_rule, r);
2580 	else
2581 		nftnl_chain_rule_add(new_rule, c->nftnl);
2582 
2583 	return 1;
2584 err:
2585 	return 0;
2586 }
2587 
nft_rule_delete_num(struct nft_handle * h,const char * chain,const char * table,int rulenum,bool verbose)2588 int nft_rule_delete_num(struct nft_handle *h, const char *chain,
2589 			const char *table, int rulenum, bool verbose)
2590 {
2591 	int ret = 0;
2592 	struct nftnl_rule *r;
2593 	struct nft_chain *c;
2594 
2595 	nft_fn = nft_rule_delete_num;
2596 
2597 	c = nft_chain_find(h, table, chain);
2598 	if (!c) {
2599 		errno = ENOENT;
2600 		return 0;
2601 	}
2602 
2603 	r = nft_rule_find(h, c, NULL, rulenum);
2604 	if (r != NULL) {
2605 		DEBUGP("deleting rule by number %d\n", rulenum);
2606 		ret = __nft_rule_del(h, r);
2607 		if (ret < 0)
2608 			errno = ENOMEM;
2609 	} else
2610 		errno = E2BIG;
2611 
2612 	return ret;
2613 }
2614 
nft_rule_replace(struct nft_handle * h,const char * chain,const char * table,struct nftnl_rule * rule,int rulenum,bool verbose)2615 int nft_rule_replace(struct nft_handle *h, const char *chain,
2616 		     const char *table, struct nftnl_rule *rule,
2617 		     int rulenum, bool verbose)
2618 {
2619 	int ret = 0;
2620 	struct nftnl_rule *r;
2621 	struct nft_chain *c;
2622 
2623 	nft_fn = nft_rule_replace;
2624 
2625 	c = nft_chain_find(h, table, chain);
2626 	if (!c) {
2627 		errno = ENOENT;
2628 		return 0;
2629 	}
2630 
2631 	r = nft_rule_find(h, c, rule, rulenum);
2632 	if (r != NULL) {
2633 		DEBUGP("replacing rule with handle=%llu\n",
2634 			(unsigned long long)
2635 			nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE));
2636 
2637 		ret = nft_rule_append(h, chain, table, rule, r, verbose);
2638 	} else
2639 		errno = E2BIG;
2640 
2641 	return ret;
2642 }
2643 
2644 static int
__nft_rule_list(struct nft_handle * h,struct nftnl_chain * c,int rulenum,unsigned int format,void (* cb)(struct nft_handle * h,struct nftnl_rule * r,unsigned int num,unsigned int format))2645 __nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
2646 		int rulenum, unsigned int format,
2647 		void (*cb)(struct nft_handle *h, struct nftnl_rule *r,
2648 			   unsigned int num, unsigned int format))
2649 {
2650 	struct nftnl_rule_iter *iter;
2651 	struct nftnl_rule *r;
2652 	int rule_ctr = 0;
2653 
2654 	if (rulenum > 0) {
2655 		r = nftnl_rule_lookup_byindex(c, rulenum - 1);
2656 		if (!r)
2657 			/* iptables-legacy returns 0 when listing for
2658 			 * valid chain but invalid rule number
2659 			 */
2660 			return 1;
2661 		cb(h, r, rulenum, format);
2662 		return 1;
2663 	}
2664 
2665 	iter = nftnl_rule_iter_create(c);
2666 	if (iter == NULL)
2667 		return 0;
2668 
2669 	r = nftnl_rule_iter_next(iter);
2670 	while (r != NULL) {
2671 		cb(h, r, ++rule_ctr, format);
2672 		r = nftnl_rule_iter_next(iter);
2673 	}
2674 
2675 	nftnl_rule_iter_destroy(iter);
2676 	return 1;
2677 }
2678 
nft_rule_count(struct nft_handle * h,struct nftnl_chain * c)2679 static int nft_rule_count(struct nft_handle *h, struct nftnl_chain *c)
2680 {
2681 	struct nftnl_rule_iter *iter;
2682 	struct nftnl_rule *r;
2683 	int rule_ctr = 0;
2684 
2685 	iter = nftnl_rule_iter_create(c);
2686 	if (iter == NULL)
2687 		return 0;
2688 
2689 	r = nftnl_rule_iter_next(iter);
2690 	while (r != NULL) {
2691 		rule_ctr++;
2692 		r = nftnl_rule_iter_next(iter);
2693 	}
2694 
2695 	nftnl_rule_iter_destroy(iter);
2696 	return rule_ctr;
2697 }
2698 
__nft_print_header(struct nft_handle * h,struct nft_chain * nc,unsigned int format)2699 static void __nft_print_header(struct nft_handle *h,
2700 			       struct nft_chain *nc, unsigned int format)
2701 {
2702 	struct nftnl_chain *c = nc->nftnl;
2703 	const char *chain_name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
2704 	uint32_t refs = nftnl_chain_get_u32(c, NFTNL_CHAIN_USE);
2705 	uint32_t entries = nft_rule_count(h, c);
2706 	struct xt_counters ctrs = {
2707 		.pcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
2708 		.bcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES),
2709 	};
2710 	const char *pname = NULL;
2711 
2712 	if (nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM) &&
2713 	    nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY))
2714 		pname = policy_name[nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY)];
2715 
2716 	h->ops->print_header(format, chain_name, pname,
2717 			     &ctrs, refs - entries, entries);
2718 }
2719 
2720 struct nft_rule_list_cb_data {
2721 	struct nft_handle *h;
2722 	unsigned int format;
2723 	int rulenum;
2724 	bool found;
2725 	bool save_fmt;
2726 	void (*cb)(struct nft_handle *h, struct nftnl_rule *r,
2727 		   unsigned int num, unsigned int format);
2728 };
2729 
nft_rule_list_cb(struct nft_chain * c,void * data)2730 static int nft_rule_list_cb(struct nft_chain *c, void *data)
2731 {
2732 	struct nft_rule_list_cb_data *d = data;
2733 
2734 	if (!d->save_fmt) {
2735 		if (d->found)
2736 			printf("\n");
2737 		d->found = true;
2738 
2739 		__nft_print_header(d->h, c, d->format);
2740 	}
2741 
2742 	return __nft_rule_list(d->h, c->nftnl, d->rulenum, d->format, d->cb);
2743 }
2744 
nft_rule_list(struct nft_handle * h,const char * chain,const char * table,int rulenum,unsigned int format)2745 int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
2746 		  int rulenum, unsigned int format)
2747 {
2748 	const struct nft_family_ops *ops = h->ops;
2749 	struct nft_rule_list_cb_data d = {
2750 		.h = h,
2751 		.format = format,
2752 		.rulenum = rulenum,
2753 		.cb = ops->print_rule,
2754 	};
2755 	struct nft_chain *c;
2756 
2757 	nft_xt_fake_builtin_chains(h, table, chain);
2758 	nft_assert_table_compatible(h, table, chain);
2759 
2760 	if (chain) {
2761 		c = nft_chain_find(h, table, chain);
2762 		if (!c)
2763 			return 0;
2764 
2765 		if (rulenum)
2766 			d.save_fmt = true;	/* skip header printing */
2767 		else if (ops->print_table_header)
2768 			ops->print_table_header(table);
2769 
2770 		nft_rule_list_cb(c, &d);
2771 		return 1;
2772 	}
2773 
2774 	nft_cache_sort_chains(h, table);
2775 
2776 	if (ops->print_table_header)
2777 		ops->print_table_header(table);
2778 
2779 	nft_chain_foreach(h, table, nft_rule_list_cb, &d);
2780 	return 1;
2781 }
2782 
2783 static void
list_save(struct nft_handle * h,struct nftnl_rule * r,unsigned int num,unsigned int format)2784 list_save(struct nft_handle *h, struct nftnl_rule *r,
2785 	  unsigned int num, unsigned int format)
2786 {
2787 	nft_rule_print_save(h, r, NFT_RULE_APPEND, format);
2788 }
2789 
nft_chain_foreach(struct nft_handle * h,const char * table,int (* cb)(struct nft_chain * c,void * data),void * data)2790 int nft_chain_foreach(struct nft_handle *h, const char *table,
2791 		      int (*cb)(struct nft_chain *c, void *data),
2792 		      void *data)
2793 {
2794 	const struct builtin_table *t;
2795 	struct nft_chain_list *list;
2796 	struct nft_chain *c, *c_bak;
2797 	int i, ret;
2798 
2799 	t = nft_table_builtin_find(h, table);
2800 	if (!t)
2801 		return -1;
2802 
2803 	for (i = 0; i < NF_INET_NUMHOOKS; i++) {
2804 		c = h->cache->table[t->type].base_chains[i];
2805 		if (!c)
2806 			continue;
2807 
2808 		ret = cb(c, data);
2809 		if (ret < 0)
2810 			return ret;
2811 	}
2812 
2813 	list = h->cache->table[t->type].chains;
2814 	if (!list)
2815 		return -1;
2816 
2817 	list_for_each_entry_safe(c, c_bak, &list->list, head) {
2818 		ret = cb(c, data);
2819 		if (ret < 0)
2820 			return ret;
2821 	}
2822 	return 0;
2823 }
2824 
nft_rule_list_chain_save(struct nft_chain * nc,void * data)2825 static int nft_rule_list_chain_save(struct nft_chain *nc, void *data)
2826 {
2827 	struct nftnl_chain *c = nc->nftnl;
2828 	const char *chain_name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
2829 	uint32_t policy = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
2830 	int *counters = data;
2831 
2832 	if (!nft_chain_builtin(c)) {
2833 		printf("-N %s\n", chain_name);
2834 		return 0;
2835 	}
2836 
2837 	/* this is a base chain */
2838 
2839 	printf("-P %s %s", chain_name, policy_name[policy]);
2840 	if (*counters)
2841 		printf(" -c %"PRIu64" %"PRIu64,
2842 		       nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
2843 		       nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES));
2844 	printf("\n");
2845 	return 0;
2846 }
2847 
nft_rule_list_save(struct nft_handle * h,const char * chain,const char * table,int rulenum,int counters)2848 int nft_rule_list_save(struct nft_handle *h, const char *chain,
2849 		       const char *table, int rulenum, int counters)
2850 {
2851 	struct nft_rule_list_cb_data d = {
2852 		.h = h,
2853 		.rulenum = rulenum,
2854 		.save_fmt = true,
2855 		.cb = list_save,
2856 	};
2857 	struct nft_chain *c;
2858 	int ret = 0;
2859 
2860 	nft_xt_fake_builtin_chains(h, table, chain);
2861 	nft_assert_table_compatible(h, table, chain);
2862 
2863 	if (counters < 0)
2864 		d.format = FMT_C_COUNTS;
2865 	else if (counters == 0)
2866 		d.format = FMT_NOCOUNTS;
2867 
2868 	if (chain) {
2869 		c = nft_chain_find(h, table, chain);
2870 		if (!c)
2871 			return 0;
2872 
2873 		if (!rulenum)
2874 			nft_rule_list_chain_save(c, &counters);
2875 
2876 		return nft_rule_list_cb(c, &d);
2877 	}
2878 
2879 	nft_cache_sort_chains(h, table);
2880 
2881 	/* Dump policies and custom chains first */
2882 	nft_chain_foreach(h, table, nft_rule_list_chain_save, &counters);
2883 
2884 	/* Now dump out rules in this table */
2885 	ret = nft_chain_foreach(h, table, nft_rule_list_cb, &d);
2886 	return ret == 0 ? 1 : 0;
2887 }
2888 
nft_rule_zero_counters(struct nft_handle * h,const char * chain,const char * table,int rulenum)2889 int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
2890 			   const char *table, int rulenum)
2891 {
2892 	struct iptables_command_state cs = {};
2893 	struct nftnl_rule *r, *new_rule;
2894 	struct nft_rule_ctx ctx = {
2895 		.command = NFT_COMPAT_RULE_APPEND,
2896 	};
2897 	struct nft_chain *c;
2898 	int ret = 0;
2899 
2900 	nft_fn = nft_rule_delete;
2901 
2902 	c = nft_chain_find(h, table, chain);
2903 	if (!c)
2904 		return 0;
2905 
2906 	r = nft_rule_find(h, c, NULL, rulenum);
2907 	if (r == NULL) {
2908 		errno = ENOENT;
2909 		ret = 1;
2910 		goto error;
2911 	}
2912 
2913 	h->ops->rule_to_cs(h, r, &cs);
2914 	cs.counters.pcnt = cs.counters.bcnt = 0;
2915 	new_rule = nft_rule_new(h, &ctx, chain, table, &cs);
2916 	h->ops->clear_cs(&cs);
2917 
2918 	if (!new_rule)
2919 		return 1;
2920 
2921 	ret = nft_rule_append(h, chain, table, new_rule, r, false);
2922 
2923 error:
2924 	return ret;
2925 }
2926 
nft_table_print_debug(struct nft_handle * h,struct nftnl_table * t,struct nlmsghdr * nlh)2927 static void nft_table_print_debug(struct nft_handle *h,
2928 				  struct nftnl_table *t, struct nlmsghdr *nlh)
2929 {
2930 	if (h->verbose > 1) {
2931 		nftnl_table_fprintf(stdout, t, 0, 0);
2932 		fprintf(stdout, "\n");
2933 	}
2934 	if (h->verbose > 2)
2935 		mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len,
2936 				  sizeof(struct nfgenmsg));
2937 }
2938 
nft_compat_table_batch_add(struct nft_handle * h,uint16_t type,uint16_t flags,uint32_t seq,struct nftnl_table * table)2939 static void nft_compat_table_batch_add(struct nft_handle *h, uint16_t type,
2940 				       uint16_t flags, uint32_t seq,
2941 				       struct nftnl_table *table)
2942 {
2943 	struct nlmsghdr *nlh;
2944 
2945 	nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
2946 				    type, h->family, flags, seq);
2947 	nftnl_table_nlmsg_build_payload(nlh, table);
2948 	nft_table_print_debug(h, table, nlh);
2949 }
2950 
nft_compat_set_batch_add(struct nft_handle * h,uint16_t type,uint16_t flags,uint32_t seq,struct nftnl_set * set)2951 static void nft_compat_set_batch_add(struct nft_handle *h, uint16_t type,
2952 				     uint16_t flags, uint32_t seq,
2953 				     struct nftnl_set *set)
2954 {
2955 	struct nlmsghdr *nlh;
2956 
2957 	nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
2958 					type, h->family, flags, seq);
2959 	nftnl_set_nlmsg_build_payload(nlh, set);
2960 }
2961 
nft_compat_setelem_batch_add(struct nft_handle * h,uint16_t type,uint16_t flags,uint32_t * seq,struct nftnl_set * set)2962 static void nft_compat_setelem_batch_add(struct nft_handle *h, uint16_t type,
2963 					 uint16_t flags, uint32_t *seq,
2964 					 struct nftnl_set *set)
2965 {
2966 	struct nftnl_set_elems_iter *iter;
2967 	struct nlmsghdr *nlh;
2968 
2969 	iter = nftnl_set_elems_iter_create(set);
2970 	if (!iter)
2971 		return;
2972 
2973 	while (nftnl_set_elems_iter_cur(iter)) {
2974 		(*seq)++;
2975 		mnl_nft_batch_continue(h->batch);
2976 		nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
2977 					    type, h->family, flags, *seq);
2978 		if (nftnl_set_elems_nlmsg_build_payload_iter(nlh, iter) <= 0)
2979 			break;
2980 	}
2981 	nftnl_set_elems_iter_destroy(iter);
2982 
2983 	if (h->verbose > 1) {
2984 		fprintf(stdout, "set ");
2985 		nftnl_set_fprintf(stdout, set, 0, 0);
2986 		fprintf(stdout, "\n");
2987 	}
2988 }
2989 
nft_compat_chain_batch_add(struct nft_handle * h,uint16_t type,uint16_t flags,uint32_t seq,struct nftnl_chain * chain)2990 static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type,
2991 				       uint16_t flags, uint32_t seq,
2992 				       struct nftnl_chain *chain)
2993 {
2994 	struct nlmsghdr *nlh;
2995 
2996 	nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
2997 				    type, h->family, flags, seq);
2998 	nftnl_chain_nlmsg_build_payload(nlh, chain);
2999 	nft_chain_print_debug(h, chain, nlh);
3000 }
3001 
nft_compat_rule_batch_add(struct nft_handle * h,uint16_t type,uint16_t flags,uint32_t seq,struct nftnl_rule * rule)3002 static void nft_compat_rule_batch_add(struct nft_handle *h, uint16_t type,
3003 				      uint16_t flags, uint32_t seq,
3004 				      struct nftnl_rule *rule)
3005 {
3006 	struct nlmsghdr *nlh;
3007 
3008 	nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
3009 				    type, h->family, flags, seq);
3010 	nftnl_rule_nlmsg_build_payload(nlh, rule);
3011 	nft_rule_print_debug(h, rule, nlh);
3012 }
3013 
batch_obj_del(struct nft_handle * h,struct obj_update * o)3014 static void batch_obj_del(struct nft_handle *h, struct obj_update *o)
3015 {
3016 	switch (o->type) {
3017 	case NFT_COMPAT_TABLE_ADD:
3018 	case NFT_COMPAT_TABLE_FLUSH:
3019 		nftnl_table_free(o->table);
3020 		break;
3021 	case NFT_COMPAT_CHAIN_ZERO:
3022 	case NFT_COMPAT_CHAIN_USER_ADD:
3023 	case NFT_COMPAT_CHAIN_ADD:
3024 		break;
3025 	case NFT_COMPAT_CHAIN_DEL:
3026 	case NFT_COMPAT_CHAIN_USER_FLUSH:
3027 	case NFT_COMPAT_CHAIN_UPDATE:
3028 	case NFT_COMPAT_CHAIN_RENAME:
3029 		nftnl_chain_free(o->chain);
3030 		break;
3031 	case NFT_COMPAT_RULE_APPEND:
3032 	case NFT_COMPAT_RULE_INSERT:
3033 	case NFT_COMPAT_RULE_REPLACE:
3034 		break;
3035 	case NFT_COMPAT_RULE_DELETE:
3036 	case NFT_COMPAT_RULE_FLUSH:
3037 		nftnl_rule_free(o->rule);
3038 		break;
3039 	case NFT_COMPAT_SET_ADD:
3040 		nftnl_set_free(o->set);
3041 		break;
3042 	case NFT_COMPAT_RULE_LIST:
3043 	case NFT_COMPAT_RULE_CHECK:
3044 	case NFT_COMPAT_CHAIN_RESTORE:
3045 	case NFT_COMPAT_RULE_SAVE:
3046 	case NFT_COMPAT_RULE_ZERO:
3047 	case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
3048 		assert(0);
3049 		break;
3050 	}
3051 	h->obj_list_num--;
3052 	list_del(&o->head);
3053 	free(o);
3054 }
3055 
nft_refresh_transaction(struct nft_handle * h)3056 static void nft_refresh_transaction(struct nft_handle *h)
3057 {
3058 	const char *tablename, *chainname;
3059 	const struct nft_chain *c;
3060 	struct obj_update *n, *tmp;
3061 	bool exists;
3062 
3063 	h->error.lineno = 0;
3064 
3065 	list_for_each_entry_safe(n, tmp, &h->obj_list, head) {
3066 		switch (n->type) {
3067 		case NFT_COMPAT_TABLE_FLUSH:
3068 			tablename = nftnl_table_get_str(n->table, NFTNL_TABLE_NAME);
3069 			if (!tablename)
3070 				continue;
3071 			exists = nft_table_find(h, tablename);
3072 			if (exists)
3073 				n->skip = 0;
3074 			else
3075 				n->skip = 1;
3076 			break;
3077 		case NFT_COMPAT_CHAIN_USER_ADD:
3078 			tablename = nftnl_chain_get_str(n->chain, NFTNL_CHAIN_TABLE);
3079 			if (!tablename)
3080 				continue;
3081 
3082 			chainname = nftnl_chain_get_str(n->chain, NFTNL_CHAIN_NAME);
3083 			if (!chainname)
3084 				continue;
3085 
3086 			if (!h->noflush)
3087 				break;
3088 
3089 			c = nft_chain_find(h, tablename, chainname);
3090 			if (c) {
3091 				n->skip = 1;
3092 			} else if (!c) {
3093 				n->skip = 0;
3094 			}
3095 			break;
3096 		case NFT_COMPAT_RULE_FLUSH:
3097 			tablename = nftnl_rule_get_str(n->rule, NFTNL_RULE_TABLE);
3098 			if (!tablename)
3099 				continue;
3100 
3101 			chainname = nftnl_rule_get_str(n->rule, NFTNL_RULE_CHAIN);
3102 			if (!chainname)
3103 				continue;
3104 
3105 			n->skip = !nft_chain_find(h, tablename, chainname);
3106 			break;
3107 		case NFT_COMPAT_CHAIN_DEL:
3108 			if (!nftnl_chain_get(n->chain, NFTNL_CHAIN_HOOKNUM))
3109 				break;
3110 			n->skip = !nft_may_delete_chain(n->chain);
3111 			break;
3112 		case NFT_COMPAT_TABLE_ADD:
3113 		case NFT_COMPAT_CHAIN_ADD:
3114 		case NFT_COMPAT_CHAIN_ZERO:
3115 		case NFT_COMPAT_CHAIN_USER_FLUSH:
3116 		case NFT_COMPAT_CHAIN_UPDATE:
3117 		case NFT_COMPAT_CHAIN_RENAME:
3118 		case NFT_COMPAT_RULE_APPEND:
3119 		case NFT_COMPAT_RULE_INSERT:
3120 		case NFT_COMPAT_RULE_REPLACE:
3121 		case NFT_COMPAT_RULE_DELETE:
3122 		case NFT_COMPAT_SET_ADD:
3123 		case NFT_COMPAT_RULE_LIST:
3124 		case NFT_COMPAT_RULE_CHECK:
3125 		case NFT_COMPAT_CHAIN_RESTORE:
3126 		case NFT_COMPAT_RULE_SAVE:
3127 		case NFT_COMPAT_RULE_ZERO:
3128 		case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
3129 			break;
3130 		}
3131 	}
3132 }
3133 
nft_action(struct nft_handle * h,int action)3134 static int nft_action(struct nft_handle *h, int action)
3135 {
3136 	struct obj_update *n, *tmp;
3137 	struct mnl_err *err, *ne;
3138 	unsigned int buflen, i, len;
3139 	bool show_errors = true;
3140 	char errmsg[1024];
3141 	uint32_t seq;
3142 	int ret = 0;
3143 
3144 retry:
3145 	seq = 1;
3146 	h->batch = mnl_batch_init();
3147 
3148 	mnl_batch_begin(h->batch, h->nft_genid, seq++);
3149 	h->nft_genid++;
3150 
3151 	list_for_each_entry(n, &h->obj_list, head) {
3152 		if (n->skip) {
3153 			n->seq = 0;
3154 			continue;
3155 		}
3156 
3157 		n->seq = seq++;
3158 		switch (n->type) {
3159 		case NFT_COMPAT_TABLE_ADD:
3160 			nft_compat_table_batch_add(h, NFT_MSG_NEWTABLE,
3161 						   NLM_F_CREATE, n->seq,
3162 						   n->table);
3163 			break;
3164 		case NFT_COMPAT_TABLE_FLUSH:
3165 			nft_compat_table_batch_add(h, NFT_MSG_DELTABLE,
3166 						   0,
3167 						   n->seq, n->table);
3168 			break;
3169 		case NFT_COMPAT_CHAIN_ADD:
3170 		case NFT_COMPAT_CHAIN_ZERO:
3171 			nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
3172 						   NLM_F_CREATE, n->seq,
3173 						   n->chain);
3174 			break;
3175 		case NFT_COMPAT_CHAIN_USER_ADD:
3176 			nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
3177 						   NLM_F_EXCL, n->seq,
3178 						   n->chain);
3179 			break;
3180 		case NFT_COMPAT_CHAIN_DEL:
3181 			nft_compat_chain_batch_add(h, NFT_MSG_DELCHAIN,
3182 						   NLM_F_NONREC, n->seq,
3183 						   n->chain);
3184 			break;
3185 		case NFT_COMPAT_CHAIN_USER_FLUSH:
3186 			nft_compat_chain_batch_add(h, NFT_MSG_DELCHAIN,
3187 						   0, n->seq,
3188 						   n->chain);
3189 			break;
3190 		case NFT_COMPAT_CHAIN_UPDATE:
3191 			nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
3192 						   h->restore ?
3193 						     NLM_F_CREATE : 0,
3194 						   n->seq, n->chain);
3195 			break;
3196 		case NFT_COMPAT_CHAIN_RENAME:
3197 			nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN, 0,
3198 						   n->seq, n->chain);
3199 			break;
3200 		case NFT_COMPAT_RULE_APPEND:
3201 			nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
3202 						  NLM_F_CREATE | NLM_F_APPEND,
3203 						  n->seq, n->rule);
3204 			break;
3205 		case NFT_COMPAT_RULE_INSERT:
3206 			nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
3207 						  NLM_F_CREATE, n->seq,
3208 						  n->rule);
3209 			break;
3210 		case NFT_COMPAT_RULE_REPLACE:
3211 			nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
3212 						  NLM_F_CREATE | NLM_F_REPLACE,
3213 						  n->seq, n->rule);
3214 			break;
3215 		case NFT_COMPAT_RULE_DELETE:
3216 		case NFT_COMPAT_RULE_FLUSH:
3217 			nft_compat_rule_batch_add(h, NFT_MSG_DELRULE, 0,
3218 						  n->seq, n->rule);
3219 			break;
3220 		case NFT_COMPAT_SET_ADD:
3221 			nft_compat_set_batch_add(h, NFT_MSG_NEWSET,
3222 						 NLM_F_CREATE, n->seq, n->set);
3223 			nft_compat_setelem_batch_add(h, NFT_MSG_NEWSETELEM,
3224 						     NLM_F_CREATE, &n->seq, n->set);
3225 			seq = n->seq;
3226 			break;
3227 		case NFT_COMPAT_RULE_LIST:
3228 		case NFT_COMPAT_RULE_CHECK:
3229 		case NFT_COMPAT_CHAIN_RESTORE:
3230 		case NFT_COMPAT_RULE_SAVE:
3231 		case NFT_COMPAT_RULE_ZERO:
3232 		case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
3233 			assert(0);
3234 			return 0;
3235 		}
3236 
3237 		mnl_nft_batch_continue(h->batch);
3238 	}
3239 
3240 	switch (action) {
3241 	case NFT_COMPAT_COMMIT:
3242 		mnl_batch_end(h->batch, seq++);
3243 		break;
3244 	case NFT_COMPAT_ABORT:
3245 		break;
3246 	}
3247 
3248 	errno = 0;
3249 	ret = mnl_batch_talk(h, seq);
3250 	if (ret && errno == ERESTART) {
3251 		nft_rebuild_cache(h);
3252 
3253 		nft_refresh_transaction(h);
3254 
3255 		list_for_each_entry_safe(err, ne, &h->err_list, head)
3256 			mnl_err_list_free(err);
3257 
3258 		mnl_batch_reset(h->batch);
3259 		goto retry;
3260 	}
3261 
3262 	i = 0;
3263 	buflen = sizeof(errmsg);
3264 
3265 	list_for_each_entry_safe(n, tmp, &h->obj_list, head) {
3266 		list_for_each_entry_safe(err, ne, &h->err_list, head) {
3267 			if (err->seqnum > n->seq)
3268 				break;
3269 
3270 			if (err->seqnum == n->seq && show_errors) {
3271 				if (n->error.lineno == 0)
3272 					show_errors = false;
3273 				len = mnl_append_error(h, n, err, errmsg + i, buflen);
3274 				if (len > 0 && len <= buflen) {
3275 					buflen -= len;
3276 					i += len;
3277 				}
3278 			}
3279 			mnl_err_list_free(err);
3280 		}
3281 		batch_obj_del(h, n);
3282 	}
3283 
3284 	nft_release_cache(h);
3285 	mnl_batch_reset(h->batch);
3286 
3287 	if (i)
3288 		xtables_error(RESOURCE_PROBLEM, "%s", errmsg);
3289 
3290 	return ret == 0 ? 1 : 0;
3291 }
3292 
ebt_add_policy_rule(struct nftnl_chain * c,void * data)3293 static int ebt_add_policy_rule(struct nftnl_chain *c, void *data)
3294 {
3295 	uint32_t policy = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
3296 	struct iptables_command_state cs = {
3297 		.eb.bitmask = EBT_NOPROTO,
3298 	};
3299 	struct nftnl_udata_buf *udata;
3300 	struct nft_rule_ctx ctx = {
3301 		.command	= NFT_COMPAT_RULE_APPEND,
3302 	};
3303 	struct nft_handle *h = data;
3304 	struct nftnl_rule *r;
3305 	const char *pname;
3306 
3307 	if (nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM))
3308 		return 0; /* ignore base chains */
3309 
3310 	if (!nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY))
3311 		return 0;
3312 
3313 	nftnl_chain_unset(c, NFTNL_CHAIN_POLICY);
3314 
3315 	switch (policy) {
3316 	case NFT_RETURN:
3317 		return 0; /* return policy is default for nft chains */
3318 	case NF_ACCEPT:
3319 		pname = "ACCEPT";
3320 		break;
3321 	case NF_DROP:
3322 		pname = "DROP";
3323 		break;
3324 	default:
3325 		return -1;
3326 	}
3327 
3328 	command_jump(&cs, pname);
3329 
3330 	r = nft_rule_new(h, &ctx, nftnl_chain_get_str(c, NFTNL_CHAIN_NAME),
3331 			 nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE), &cs);
3332 	ebt_cs_clean(&cs);
3333 
3334 	if (!r)
3335 		return -1;
3336 
3337 	udata = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
3338 	if (!udata)
3339 		goto err_free_rule;
3340 
3341 	if (!nftnl_udata_put_u32(udata, UDATA_TYPE_EBTABLES_POLICY, 1))
3342 		goto err_free_rule;
3343 
3344 	nftnl_rule_set_data(r, NFTNL_RULE_USERDATA,
3345 			    nftnl_udata_buf_data(udata),
3346 			    nftnl_udata_buf_len(udata));
3347 	nftnl_udata_buf_free(udata);
3348 
3349 	if (!batch_rule_add(h, NFT_COMPAT_RULE_APPEND, r))
3350 		goto err_free_rule;
3351 
3352 	/* add the rule to chain so it is freed later */
3353 	nftnl_chain_rule_add_tail(r, c);
3354 
3355 	return 0;
3356 err_free_rule:
3357 	nftnl_rule_free(r);
3358 	return -1;
3359 }
3360 
ebt_set_user_chain_policy(struct nft_handle * h,const char * table,const char * chain,const char * policy)3361 int ebt_set_user_chain_policy(struct nft_handle *h, const char *table,
3362 			      const char *chain, const char *policy)
3363 {
3364 	struct nft_chain *c = nft_chain_find(h, table, chain);
3365 	int pval;
3366 
3367 	if (!c)
3368 		return 0;
3369 
3370 	if (!strcmp(policy, "DROP"))
3371 		pval = NF_DROP;
3372 	else if (!strcmp(policy, "ACCEPT"))
3373 		pval = NF_ACCEPT;
3374 	else if (!strcmp(policy, "RETURN"))
3375 		pval = NFT_RETURN;
3376 	else
3377 		return 0;
3378 
3379 	nftnl_chain_set_u32(c->nftnl, NFTNL_CHAIN_POLICY, pval);
3380 	return 1;
3381 }
3382 
nft_bridge_commit_prepare(struct nft_handle * h)3383 static void nft_bridge_commit_prepare(struct nft_handle *h)
3384 {
3385 	const struct builtin_table *t;
3386 	struct nft_chain_list *list;
3387 	struct nft_chain *c;
3388 	int i;
3389 
3390 	for (i = 0; i < NFT_TABLE_MAX; i++) {
3391 		t = &h->tables[i];
3392 
3393 		if (!t->name)
3394 			continue;
3395 
3396 		list = h->cache->table[t->type].chains;
3397 		if (!list)
3398 			continue;
3399 
3400 		list_for_each_entry(c, &list->list, head) {
3401 			ebt_add_policy_rule(c->nftnl, h);
3402 		}
3403 	}
3404 }
3405 
assert_chain_exists(struct nft_handle * h,const char * table,const char * chain)3406 static void assert_chain_exists(struct nft_handle *h,
3407 				const char *table, const char *chain)
3408 {
3409 	if (chain && !nft_chain_exists(h, table, chain))
3410 		xtables_error(PARAMETER_PROBLEM,
3411 			      "Chain '%s' does not exist", chain);
3412 }
3413 
nft_prepare(struct nft_handle * h)3414 static int nft_prepare(struct nft_handle *h)
3415 {
3416 	struct nft_cmd *cmd, *next;
3417 	int ret = 1;
3418 
3419 	nft_cache_build(h);
3420 
3421 	list_for_each_entry_safe(cmd, next, &h->cmd_list, head) {
3422 		h->error.lineno = cmd->error.lineno;
3423 
3424 		switch (cmd->command) {
3425 		case NFT_COMPAT_TABLE_FLUSH:
3426 			ret = nft_table_flush(h, cmd->table);
3427 			break;
3428 		case NFT_COMPAT_CHAIN_USER_ADD:
3429 			ret = nft_chain_user_add(h, cmd->chain, cmd->table);
3430 			break;
3431 		case NFT_COMPAT_CHAIN_DEL:
3432 			ret = nft_chain_del(h, cmd->chain, cmd->table,
3433 					    cmd->verbose);
3434 			break;
3435 		case NFT_COMPAT_CHAIN_RESTORE:
3436 			ret = nft_chain_restore(h, cmd->chain, cmd->table);
3437 			break;
3438 		case NFT_COMPAT_CHAIN_UPDATE:
3439 			ret = nft_chain_set(h, cmd->table, cmd->chain,
3440 					    cmd->policy, &cmd->counters);
3441 			break;
3442 		case NFT_COMPAT_CHAIN_RENAME:
3443 			ret = nft_chain_user_rename(h, cmd->chain, cmd->table,
3444 						    cmd->rename);
3445 			break;
3446 		case NFT_COMPAT_CHAIN_ZERO:
3447 			ret = nft_chain_zero_counters(h, cmd->chain, cmd->table,
3448 						      cmd->verbose);
3449 			break;
3450 		case NFT_COMPAT_RULE_APPEND:
3451 			assert_chain_exists(h, cmd->table, cmd->jumpto);
3452 			ret = nft_rule_append(h, cmd->chain, cmd->table,
3453 					      cmd->obj.rule, NULL, cmd->verbose);
3454 			break;
3455 		case NFT_COMPAT_RULE_INSERT:
3456 			assert_chain_exists(h, cmd->table, cmd->jumpto);
3457 			ret = nft_rule_insert(h, cmd->chain, cmd->table,
3458 					      cmd->obj.rule, cmd->rulenum,
3459 					      cmd->verbose);
3460 			break;
3461 		case NFT_COMPAT_RULE_REPLACE:
3462 			assert_chain_exists(h, cmd->table, cmd->jumpto);
3463 			ret = nft_rule_replace(h, cmd->chain, cmd->table,
3464 					      cmd->obj.rule, cmd->rulenum,
3465 					      cmd->verbose);
3466 			break;
3467 		case NFT_COMPAT_RULE_DELETE:
3468 			assert_chain_exists(h, cmd->table, cmd->jumpto);
3469 			if (cmd->rulenum >= 0)
3470 				ret = nft_rule_delete_num(h, cmd->chain,
3471 							  cmd->table,
3472 							  cmd->rulenum,
3473 							  cmd->verbose);
3474 			else
3475 				ret = nft_rule_delete(h, cmd->chain, cmd->table,
3476 						      cmd->obj.rule, cmd->verbose);
3477 			break;
3478 		case NFT_COMPAT_RULE_FLUSH:
3479 			ret = nft_rule_flush(h, cmd->chain, cmd->table,
3480 					     cmd->verbose);
3481 			break;
3482 		case NFT_COMPAT_RULE_LIST:
3483 			ret = nft_rule_list(h, cmd->chain, cmd->table,
3484 					    cmd->rulenum, cmd->format);
3485 			break;
3486 		case NFT_COMPAT_RULE_CHECK:
3487 			assert_chain_exists(h, cmd->table, cmd->jumpto);
3488 			ret = nft_rule_check(h, cmd->chain, cmd->table,
3489 					     cmd->obj.rule, cmd->verbose);
3490 			break;
3491 		case NFT_COMPAT_RULE_ZERO:
3492 			ret = nft_rule_zero_counters(h, cmd->chain, cmd->table,
3493                                                      cmd->rulenum);
3494 			break;
3495 		case NFT_COMPAT_RULE_SAVE:
3496 			ret = nft_rule_list_save(h, cmd->chain, cmd->table,
3497 						 cmd->rulenum,
3498 						 cmd->counters_save);
3499 			break;
3500 		case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
3501 			ret = ebt_set_user_chain_policy(h, cmd->table,
3502 							cmd->chain, cmd->policy);
3503 			break;
3504 		case NFT_COMPAT_SET_ADD:
3505 			nft_xt_builtin_table_init(h, cmd->table);
3506 			batch_set_add(h, NFT_COMPAT_SET_ADD, cmd->obj.set);
3507 			ret = 1;
3508 			break;
3509 		case NFT_COMPAT_TABLE_ADD:
3510 		case NFT_COMPAT_CHAIN_ADD:
3511 			assert(0);
3512 			return 0;
3513 		}
3514 
3515 		nft_cmd_free(cmd);
3516 
3517 		if (ret == 0)
3518 			return 0;
3519 	}
3520 
3521 	return 1;
3522 }
3523 
nft_commit(struct nft_handle * h)3524 int nft_commit(struct nft_handle *h)
3525 {
3526 	if (!nft_prepare(h))
3527 		return 0;
3528 
3529 	return nft_action(h, NFT_COMPAT_COMMIT);
3530 }
3531 
nft_bridge_commit(struct nft_handle * h)3532 int nft_bridge_commit(struct nft_handle *h)
3533 {
3534 	if (!nft_prepare(h))
3535 		return 0;
3536 
3537 	nft_bridge_commit_prepare(h);
3538 
3539 	return nft_action(h, NFT_COMPAT_COMMIT);
3540 }
3541 
nft_abort(struct nft_handle * h)3542 int nft_abort(struct nft_handle *h)
3543 {
3544 	struct nft_cmd *cmd, *next;
3545 
3546 	list_for_each_entry_safe(cmd, next, &h->cmd_list, head)
3547 		nft_cmd_free(cmd);
3548 
3549 	return nft_action(h, NFT_COMPAT_ABORT);
3550 }
3551 
nft_compatible_revision(const char * name,uint8_t rev,int opt)3552 int nft_compatible_revision(const char *name, uint8_t rev, int opt)
3553 {
3554 	struct mnl_socket *nl;
3555 	char buf[16536];
3556 	struct nlmsghdr *nlh;
3557 	uint32_t portid, seq, type = 0;
3558 	uint32_t pf = AF_INET;
3559 	int ret = 0;
3560 
3561 	switch (opt) {
3562 	case IPT_SO_GET_REVISION_MATCH:
3563 		break;
3564 	case IP6T_SO_GET_REVISION_MATCH:
3565 		pf = AF_INET6;
3566 		break;
3567 	case IPT_SO_GET_REVISION_TARGET:
3568 		type = 1;
3569 		break;
3570 	case IP6T_SO_GET_REVISION_TARGET:
3571 		type = 1;
3572 		pf = AF_INET6;
3573 		break;
3574 	default:
3575 		/* No revision support (arp, ebtables), assume latest version ok */
3576 		return 1;
3577 	}
3578 
3579 	nlh = mnl_nlmsg_put_header(buf);
3580 	nlh->nlmsg_type = (NFNL_SUBSYS_NFT_COMPAT << 8) | NFNL_MSG_COMPAT_GET;
3581 	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
3582 	nlh->nlmsg_seq = seq = time(NULL);
3583 
3584 	struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
3585 	nfg->nfgen_family = pf;
3586 	nfg->version = NFNETLINK_V0;
3587 	nfg->res_id = 0;
3588 
3589 	mnl_attr_put_strz(nlh, NFTA_COMPAT_NAME, name);
3590 	mnl_attr_put_u32(nlh, NFTA_COMPAT_REV, htonl(rev));
3591 	mnl_attr_put_u32(nlh, NFTA_COMPAT_TYPE, htonl(type));
3592 
3593 	DEBUGP("requesting `%s' rev=%d type=%d via nft_compat\n",
3594 		name, rev, type);
3595 
3596 	nl = mnl_socket_open(NETLINK_NETFILTER);
3597 	if (nl == NULL)
3598 		return 0;
3599 
3600 	if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
3601 		goto err;
3602 
3603 	portid = mnl_socket_get_portid(nl);
3604 
3605 	if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
3606 		goto err;
3607 
3608 	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
3609 	if (ret == -1)
3610 		goto err;
3611 
3612 	ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
3613 	if (ret == -1)
3614 		goto err;
3615 
3616 err:
3617 	mnl_socket_close(nl);
3618 
3619 	/* ignore EPERM and errors for revision 0 -
3620 	 * this is required for printing extension help texts as user, also
3621 	 * helps error messaging on unavailable kernel extension */
3622 	if (ret < 0) {
3623 		if (errno == EPERM)
3624 			return 1;
3625 		if (rev == 0) {
3626 			fprintf(stderr,
3627 				"Warning: Extension %s revision 0 not supported, missing kernel module?\n",
3628 				name);
3629 			return 1;
3630 		}
3631 	}
3632 
3633 	return ret < 0 ? 0 : 1;
3634 }
3635 
3636 /* Translates errno numbers into more human-readable form than strerror. */
nft_strerror(int err)3637 const char *nft_strerror(int err)
3638 {
3639 	unsigned int i;
3640 	static struct table_struct {
3641 		void *fn;
3642 		int err;
3643 		const char *message;
3644 	} table[] =
3645 	  {
3646 	    { nft_chain_del, ENOTEMPTY, "Chain is not empty" },
3647 	    { nft_chain_del, EBUSY, "Directory not empty" },
3648 	    { nft_chain_del, EMLINK,
3649 	      "Can't delete chain with references left" },
3650 	    { nft_chain_user_add, EEXIST, "Chain already exists" },
3651 	    { nft_chain_user_rename, EEXIST, "File exists" },
3652 	    { nft_rule_insert, E2BIG, "Index of insertion too big" },
3653 	    { nft_rule_check, ENOENT, "Bad rule (does a matching rule exist in that chain?)" },
3654 	    { nft_rule_replace, E2BIG, "Index of replacement too big" },
3655 	    { nft_rule_delete_num, E2BIG, "Index of deletion too big" },
3656 /*	    { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
3657 	    { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" }, */
3658 	    /* ENOENT for DELETE probably means no matching rule */
3659 	    { nft_rule_delete, ENOENT,
3660 	      "Bad rule (does a matching rule exist in that chain?)" },
3661 	    { nft_chain_set, ENOENT, "Bad built-in chain name" },
3662 	    { nft_chain_set, EINVAL, "Bad policy name" },
3663 	    { nft_chain_set, ENXIO, "Bad table name" },
3664 	    { NULL, ELOOP, "Loop found in table" },
3665 	    { NULL, EPERM, "Permission denied (you must be root)" },
3666 	    { NULL, 0, "Incompatible with this kernel" },
3667 	    { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
3668 	    { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
3669 	    { NULL, ENOMEM, "Memory allocation problem" },
3670 	    { NULL, ENOENT, "No chain/target/match by that name" },
3671 	  };
3672 
3673 	for (i = 0; i < ARRAY_SIZE(table); i++) {
3674 		if ((!table[i].fn || table[i].fn == nft_fn)
3675 		    && table[i].err == err)
3676 			return table[i].message;
3677 	}
3678 
3679 	return strerror(err);
3680 }
3681 
recover_rule_compat(struct nftnl_rule * r)3682 static int recover_rule_compat(struct nftnl_rule *r)
3683 {
3684 	struct nftnl_expr_iter *iter;
3685 	struct nftnl_expr *e;
3686 	uint32_t reg;
3687 	int ret = -1;
3688 
3689 	iter = nftnl_expr_iter_create(r);
3690 	if (!iter)
3691 		return -1;
3692 
3693 next_expr:
3694 	e = nftnl_expr_iter_next(iter);
3695 	if (!e)
3696 		goto out;
3697 
3698 	if (strcmp("meta", nftnl_expr_get_str(e, NFTNL_EXPR_NAME)) ||
3699 	    nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY) != NFT_META_L4PROTO)
3700 		goto next_expr;
3701 
3702 	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
3703 
3704 	e = nftnl_expr_iter_next(iter);
3705 	if (!e)
3706 		goto out;
3707 
3708 	if (strcmp("cmp", nftnl_expr_get_str(e, NFTNL_EXPR_NAME)) ||
3709 	    reg != nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG))
3710 		goto next_expr;
3711 
3712 	add_compat(r, nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA),
3713 		   nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ);
3714 	ret = 0;
3715 out:
3716 	nftnl_expr_iter_destroy(iter);
3717 	return ret;
3718 }
3719 
3720 struct chain_zero_data {
3721 	struct nft_handle	*handle;
3722 	bool			verbose;
3723 };
3724 
__nft_chain_zero_counters(struct nft_chain * nc,void * data)3725 static int __nft_chain_zero_counters(struct nft_chain *nc, void *data)
3726 {
3727 	struct nftnl_chain *c = nc->nftnl;
3728 	struct chain_zero_data *d = data;
3729 	struct nft_handle *h = d->handle;
3730 	struct nftnl_rule_iter *iter;
3731 	struct nftnl_rule *r;
3732 
3733 	if (d->verbose)
3734 		fprintf(stdout, "Zeroing chain `%s'\n",
3735 		        nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
3736 
3737 	if (nftnl_chain_is_set(c, NFTNL_CHAIN_HOOKNUM)) {
3738 		/* zero base chain counters. */
3739 		nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, 0);
3740 		nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, 0);
3741 		nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
3742 		if (!batch_chain_add(h, NFT_COMPAT_CHAIN_ZERO, c))
3743 			return -1;
3744 	}
3745 
3746 	iter = nftnl_rule_iter_create(c);
3747 	if (iter == NULL)
3748 		return -1;
3749 
3750 	r = nftnl_rule_iter_next(iter);
3751 	while (r != NULL) {
3752 		struct nftnl_expr_iter *ei;
3753 		struct nftnl_expr *e;
3754 		bool zero_needed;
3755 
3756 		ei = nftnl_expr_iter_create(r);
3757 		if (!ei)
3758 			break;
3759 
3760 		e = nftnl_expr_iter_next(ei);
3761 	        zero_needed = false;
3762 		while (e != NULL) {
3763 			const char *en = nftnl_expr_get_str(e, NFTNL_EXPR_NAME);
3764 
3765 			if (strcmp(en, "counter") == 0 && (
3766 			    nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_PACKETS) ||
3767 			    nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_BYTES))) {
3768 				nftnl_expr_set_u64(e, NFTNL_EXPR_CTR_PACKETS, 0);
3769 				nftnl_expr_set_u64(e, NFTNL_EXPR_CTR_BYTES, 0);
3770 				zero_needed = true;
3771 			}
3772 
3773 			e = nftnl_expr_iter_next(ei);
3774 		}
3775 
3776 		nftnl_expr_iter_destroy(ei);
3777 
3778 		if (zero_needed) {
3779 			/*
3780 			 * Unset RULE_POSITION for older kernels, we want to replace
3781 			 * rule based on its handle only.
3782 			 */
3783 			recover_rule_compat(r);
3784 			nftnl_rule_unset(r, NFTNL_RULE_POSITION);
3785 			if (!batch_rule_add(h, NFT_COMPAT_RULE_REPLACE, r)) {
3786 				nftnl_rule_iter_destroy(iter);
3787 				return -1;
3788 			}
3789 		}
3790 		r = nftnl_rule_iter_next(iter);
3791 	}
3792 
3793 	nftnl_rule_iter_destroy(iter);
3794 	return 0;
3795 }
3796 
nft_chain_zero_counters(struct nft_handle * h,const char * chain,const char * table,bool verbose)3797 int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
3798 			    const char *table, bool verbose)
3799 {
3800 	struct chain_zero_data d = {
3801 		.handle = h,
3802 		.verbose = verbose,
3803 	};
3804 	struct nft_chain *c;
3805 	int ret = 0;
3806 
3807 	if (chain) {
3808 		c = nft_chain_find(h, table, chain);
3809 		if (!c) {
3810 			errno = ENOENT;
3811 			return 0;
3812 		}
3813 
3814 		ret = __nft_chain_zero_counters(c, &d);
3815 		goto err;
3816 	}
3817 
3818 	if (verbose)
3819 		nft_cache_sort_chains(h, table);
3820 
3821 	ret = nft_chain_foreach(h, table, __nft_chain_zero_counters, &d);
3822 err:
3823 	/* the core expects 1 for success and 0 for error */
3824 	return ret == 0 ? 1 : 0;
3825 }
3826 
nft_invflags2cmp(uint32_t invflags,uint32_t flag)3827 uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag)
3828 {
3829 	if (invflags & flag)
3830 		return NFT_CMP_NEQ;
3831 
3832 	return NFT_CMP_EQ;
3833 }
3834 
3835 static const char *supported_exprs[] = {
3836 	"match",
3837 	"target",
3838 	"payload",
3839 	"meta",
3840 	"cmp",
3841 	"bitwise",
3842 	"counter",
3843 	"immediate",
3844 	"lookup",
3845 	"range",
3846 };
3847 
3848 
nft_is_expr_compatible(struct nftnl_expr * expr,void * data)3849 static int nft_is_expr_compatible(struct nftnl_expr *expr, void *data)
3850 {
3851 	const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
3852 	int i;
3853 
3854 	for (i = 0; i < ARRAY_SIZE(supported_exprs); i++) {
3855 		if (strcmp(supported_exprs[i], name) == 0)
3856 			return 0;
3857 	}
3858 
3859 	if (!strcmp(name, "limit") &&
3860 	    nftnl_expr_get_u32(expr, NFTNL_EXPR_LIMIT_TYPE) == NFT_LIMIT_PKTS &&
3861 	    nftnl_expr_get_u32(expr, NFTNL_EXPR_LIMIT_FLAGS) == 0)
3862 		return 0;
3863 
3864 	if (!strcmp(name, "log") &&
3865 	    nftnl_expr_is_set(expr, NFTNL_EXPR_LOG_GROUP))
3866 		return 0;
3867 
3868 	return -1;
3869 }
3870 
nft_is_rule_compatible(struct nftnl_rule * rule,void * data)3871 static int nft_is_rule_compatible(struct nftnl_rule *rule, void *data)
3872 {
3873 	return nftnl_expr_foreach(rule, nft_is_expr_compatible, NULL);
3874 }
3875 
nft_is_chain_compatible(struct nft_chain * nc,void * data)3876 static int nft_is_chain_compatible(struct nft_chain *nc, void *data)
3877 {
3878 	struct nftnl_chain *c = nc->nftnl;
3879 
3880 	return nftnl_rule_foreach(c, nft_is_rule_compatible, NULL);
3881 }
3882 
nft_is_table_compatible(struct nft_handle * h,const char * table,const char * chain)3883 bool nft_is_table_compatible(struct nft_handle *h,
3884 			     const char *table, const char *chain)
3885 {
3886 	if (chain) {
3887 		struct nft_chain *c = nft_chain_find(h, table, chain);
3888 
3889 		return !c || !nft_is_chain_compatible(c, h);
3890 	}
3891 
3892 	return !nft_chain_foreach(h, table, nft_is_chain_compatible, h);
3893 }
3894 
nft_is_table_tainted(struct nft_handle * h,const char * table)3895 bool nft_is_table_tainted(struct nft_handle *h, const char *table)
3896 {
3897 	const struct builtin_table *t = nft_table_builtin_find(h, table);
3898 
3899 	return t ? h->cache->table[t->type].tainted : false;
3900 }
3901 
nft_assert_table_compatible(struct nft_handle * h,const char * table,const char * chain)3902 void nft_assert_table_compatible(struct nft_handle *h,
3903 				 const char *table, const char *chain)
3904 {
3905 	const char *pfx = "", *sfx = "";
3906 
3907 	if (nft_is_table_compatible(h, table, chain)) {
3908 		if (nft_is_table_tainted(h, table))
3909 			printf("# Table `%s' contains incompatible base-chains, use 'nft' tool to list them.\n",
3910 			       table);
3911 		return;
3912 	}
3913 
3914 	if (chain) {
3915 		pfx = "chain `";
3916 		sfx = "' in ";
3917 	} else {
3918 		chain = "";
3919 	}
3920 	xtables_error(OTHER_PROBLEM,
3921 		      "%s%s%stable `%s' is incompatible, use 'nft' tool.",
3922 		      pfx, chain, sfx, table);
3923 }
3924