• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
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 
23 #include <xtables.h>
24 #include <libiptc/libxtc.h>
25 #include <libiptc/xtcshared.h>
26 
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include <linux/netfilter/x_tables.h>
31 #include <linux/netfilter_ipv4/ip_tables.h>
32 #include <linux/netfilter_ipv6/ip6_tables.h>
33 #include <netinet/ip6.h>
34 
35 #include <linux/netlink.h>
36 #include <linux/netfilter/nfnetlink.h>
37 #include <linux/netfilter/nf_tables.h>
38 #include <linux/netfilter/nf_tables_compat.h>
39 
40 #include <libmnl/libmnl.h>
41 #include <libnftnl/table.h>
42 #include <libnftnl/chain.h>
43 #include <libnftnl/rule.h>
44 #include <libnftnl/expr.h>
45 #include <libnftnl/set.h>
46 #include <libnftnl/udata.h>
47 
48 #include <netinet/in.h>	/* inet_ntoa */
49 #include <arpa/inet.h>
50 
51 #include "nft.h"
52 #include "xshared.h" /* proto_to_name */
53 #include "nft-shared.h"
54 #include "xtables-config-parser.h"
55 
56 static void *nft_fn;
57 
mnl_talk(struct nft_handle * h,struct nlmsghdr * nlh,int (* cb)(const struct nlmsghdr * nlh,void * data),void * data)58 int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
59 	     int (*cb)(const struct nlmsghdr *nlh, void *data),
60 	     void *data)
61 {
62 	int ret;
63 	char buf[MNL_SOCKET_BUFFER_SIZE];
64 
65 	if (mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len) < 0)
66 		return -1;
67 
68 	ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
69 	while (ret > 0) {
70 		ret = mnl_cb_run(buf, ret, h->seq, h->portid, cb, data);
71 		if (ret <= 0)
72 			break;
73 
74 		ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
75 	}
76 	if (ret == -1) {
77 		return -1;
78 	}
79 
80 	return 0;
81 }
82 
83 static LIST_HEAD(batch_page_list);
84 static int batch_num_pages;
85 
86 struct batch_page {
87 	struct list_head	head;
88 	struct mnl_nlmsg_batch	*batch;
89 };
90 
91 /* selected batch page is 256 Kbytes long to load ruleset of
92  * half a million rules without hitting -EMSGSIZE due to large
93  * iovec.
94  */
95 #define BATCH_PAGE_SIZE getpagesize() * 32
96 
mnl_nftnl_batch_alloc(void)97 static struct mnl_nlmsg_batch *mnl_nftnl_batch_alloc(void)
98 {
99 	static char *buf;
100 
101 	/* libmnl needs higher buffer to handle batch overflows */
102 	buf = malloc(BATCH_PAGE_SIZE + getpagesize());
103 	if (buf == NULL)
104 		return NULL;
105 
106 	return mnl_nlmsg_batch_start(buf, BATCH_PAGE_SIZE);
107 }
108 
109 static struct mnl_nlmsg_batch *
mnl_nftnl_batch_page_add(struct mnl_nlmsg_batch * batch)110 mnl_nftnl_batch_page_add(struct mnl_nlmsg_batch *batch)
111 {
112 	struct batch_page *batch_page;
113 
114 	batch_page = malloc(sizeof(struct batch_page));
115 	if (batch_page == NULL)
116 		return NULL;
117 
118 	batch_page->batch = batch;
119 	list_add_tail(&batch_page->head, &batch_page_list);
120 	batch_num_pages++;
121 
122 	return mnl_nftnl_batch_alloc();
123 }
124 
125 static int nlbuffsiz;
126 
mnl_nft_set_sndbuffer(const struct mnl_socket * nl)127 static void mnl_nft_set_sndbuffer(const struct mnl_socket *nl)
128 {
129 	int newbuffsiz;
130 
131 	if (batch_num_pages * BATCH_PAGE_SIZE <= nlbuffsiz)
132 		return;
133 
134 	newbuffsiz = batch_num_pages * BATCH_PAGE_SIZE;
135 
136 	/* Rise sender buffer length to avoid hitting -EMSGSIZE */
137 	if (setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUFFORCE,
138 		       &newbuffsiz, sizeof(socklen_t)) < 0)
139 		return;
140 
141 	nlbuffsiz = newbuffsiz;
142 }
143 
mnl_nftnl_batch_reset(void)144 static void mnl_nftnl_batch_reset(void)
145 {
146 	struct batch_page *batch_page, *next;
147 
148 	list_for_each_entry_safe(batch_page, next, &batch_page_list, head) {
149 		list_del(&batch_page->head);
150 		free(batch_page->batch);
151 		free(batch_page);
152 		batch_num_pages--;
153 	}
154 }
155 
mnl_nft_socket_sendmsg(const struct mnl_socket * nl)156 static ssize_t mnl_nft_socket_sendmsg(const struct mnl_socket *nl)
157 {
158 	static const struct sockaddr_nl snl = {
159 		.nl_family = AF_NETLINK
160 	};
161 	struct iovec iov[batch_num_pages];
162 	struct msghdr msg = {
163 		.msg_name	= (struct sockaddr *) &snl,
164 		.msg_namelen	= sizeof(snl),
165 		.msg_iov	= iov,
166 		.msg_iovlen	= batch_num_pages,
167 	};
168 	struct batch_page *batch_page;
169 	int i = 0, ret;
170 
171 	mnl_nft_set_sndbuffer(nl);
172 
173 	list_for_each_entry(batch_page, &batch_page_list, head) {
174 		iov[i].iov_base = mnl_nlmsg_batch_head(batch_page->batch);
175 		iov[i].iov_len = mnl_nlmsg_batch_size(batch_page->batch);
176 		i++;
177 #ifdef NL_DEBUG
178 		mnl_nlmsg_fprintf(stdout,
179 				  mnl_nlmsg_batch_head(batch_page->batch),
180 				  mnl_nlmsg_batch_size(batch_page->batch),
181 				  sizeof(struct nfgenmsg));
182 #endif
183 	}
184 
185 	ret = sendmsg(mnl_socket_get_fd(nl), &msg, 0);
186 	mnl_nftnl_batch_reset();
187 
188 	return ret;
189 }
190 
mnl_nftnl_batch_talk(struct nft_handle * h)191 static int mnl_nftnl_batch_talk(struct nft_handle *h)
192 {
193 	int ret, fd = mnl_socket_get_fd(h->nl);
194 	char rcv_buf[MNL_SOCKET_BUFFER_SIZE];
195 	fd_set readfds;
196 	struct timeval tv = {
197 		.tv_sec		= 0,
198 		.tv_usec	= 0
199 	};
200 	int err = 0;
201 
202 	ret = mnl_nft_socket_sendmsg(h->nl);
203 	if (ret == -1)
204 		return -1;
205 
206 	FD_ZERO(&readfds);
207 	FD_SET(fd, &readfds);
208 
209 	/* receive and digest all the acknowledgments from the kernel. */
210 	ret = select(fd+1, &readfds, NULL, NULL, &tv);
211 	if (ret == -1)
212 		return -1;
213 
214 	while (ret > 0 && FD_ISSET(fd, &readfds)) {
215 		ret = mnl_socket_recvfrom(h->nl, rcv_buf, sizeof(rcv_buf));
216 		if (ret == -1)
217 			return -1;
218 
219 		ret = mnl_cb_run(rcv_buf, ret, 0, h->portid, NULL, NULL);
220 		/* Annotate first error and continue, make sure we get all
221 		 * acknoledgments.
222 		 */
223 		if (!err && ret == -1)
224 			err = errno;
225 
226 		ret = select(fd+1, &readfds, NULL, NULL, &tv);
227 		if (ret == -1)
228 			return -1;
229 
230 		FD_ZERO(&readfds);
231 		FD_SET(fd, &readfds);
232 	}
233 	errno = err;
234 	return err ? -1 : 0;
235 }
236 
mnl_nftnl_batch_begin(struct mnl_nlmsg_batch * batch,uint32_t seq)237 static void mnl_nftnl_batch_begin(struct mnl_nlmsg_batch *batch, uint32_t seq)
238 {
239 	nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq);
240 	if (!mnl_nlmsg_batch_next(batch))
241 		mnl_nftnl_batch_page_add(batch);
242 }
243 
mnl_nftnl_batch_end(struct mnl_nlmsg_batch * batch,uint32_t seq)244 static void mnl_nftnl_batch_end(struct mnl_nlmsg_batch *batch, uint32_t seq)
245 {
246 	nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq);
247 	if (!mnl_nlmsg_batch_next(batch))
248 		mnl_nftnl_batch_page_add(batch);
249 }
250 
251 enum obj_update_type {
252 	NFT_COMPAT_TABLE_ADD,
253 	NFT_COMPAT_CHAIN_ADD,
254 	NFT_COMPAT_CHAIN_USER_ADD,
255 	NFT_COMPAT_CHAIN_USER_DEL,
256 	NFT_COMPAT_CHAIN_UPDATE,
257 	NFT_COMPAT_CHAIN_RENAME,
258 	NFT_COMPAT_RULE_APPEND,
259 	NFT_COMPAT_RULE_INSERT,
260 	NFT_COMPAT_RULE_REPLACE,
261 	NFT_COMPAT_RULE_DELETE,
262 	NFT_COMPAT_RULE_FLUSH,
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;
273 	union {
274 		struct nftnl_table	*table;
275 		struct nftnl_chain	*chain;
276 		struct nftnl_rule		*rule;
277 		void			*ptr;
278 	};
279 };
280 
batch_add(struct nft_handle * h,enum obj_update_type type,void * ptr)281 static int batch_add(struct nft_handle *h, enum obj_update_type type, void *ptr)
282 {
283 	struct obj_update *obj;
284 
285 	obj = calloc(1, sizeof(struct obj_update));
286 	if (obj == NULL)
287 		return -1;
288 
289 	obj->ptr = ptr;
290 	obj->type = type;
291 	list_add_tail(&obj->head, &h->obj_list);
292 	h->obj_list_num++;
293 
294 	return 0;
295 }
296 
batch_table_add(struct nft_handle * h,enum obj_update_type type,struct nftnl_table * t)297 static int batch_table_add(struct nft_handle *h, enum obj_update_type type,
298 			   struct nftnl_table *t)
299 {
300 	return batch_add(h, type, t);
301 }
302 
batch_chain_add(struct nft_handle * h,enum obj_update_type type,struct nftnl_chain * c)303 static int batch_chain_add(struct nft_handle *h, enum obj_update_type type,
304 			   struct nftnl_chain *c)
305 {
306 	return batch_add(h, type, c);
307 }
308 
batch_rule_add(struct nft_handle * h,enum obj_update_type type,struct nftnl_rule * r)309 static int batch_rule_add(struct nft_handle *h, enum obj_update_type type,
310 			  struct nftnl_rule *r)
311 {
312 	return batch_add(h, type, r);
313 }
314 
315 struct builtin_table xtables_ipv4[TABLES_MAX] = {
316 	[RAW] = {
317 		.name	= "raw",
318 		.chains = {
319 			{
320 				.name	= "PREROUTING",
321 				.type	= "filter",
322 				.prio	= -300,	/* NF_IP_PRI_RAW */
323 				.hook	= NF_INET_PRE_ROUTING,
324 			},
325 			{
326 				.name	= "OUTPUT",
327 				.type	= "filter",
328 				.prio	= -300,	/* NF_IP_PRI_RAW */
329 				.hook	= NF_INET_LOCAL_OUT,
330 			},
331 		},
332 	},
333 	[MANGLE] = {
334 		.name	= "mangle",
335 		.chains = {
336 			{
337 				.name	= "PREROUTING",
338 				.type	= "filter",
339 				.prio	= -150,	/* NF_IP_PRI_MANGLE */
340 				.hook	= NF_INET_PRE_ROUTING,
341 			},
342 			{
343 				.name	= "INPUT",
344 				.type	= "filter",
345 				.prio	= -150,	/* NF_IP_PRI_MANGLE */
346 				.hook	= NF_INET_LOCAL_IN,
347 			},
348 			{
349 				.name	= "FORWARD",
350 				.type	= "filter",
351 				.prio	= -150,	/* NF_IP_PRI_MANGLE */
352 				.hook	= NF_INET_FORWARD,
353 			},
354 			{
355 				.name	= "OUTPUT",
356 				.type	= "route",
357 				.prio	= -150,	/* NF_IP_PRI_MANGLE */
358 				.hook	= NF_INET_LOCAL_OUT,
359 			},
360 			{
361 				.name	= "POSTROUTING",
362 				.type	= "filter",
363 				.prio	= -150,	/* NF_IP_PRI_MANGLE */
364 				.hook	= NF_INET_POST_ROUTING,
365 			},
366 		},
367 	},
368 	[FILTER] = {
369 		.name	= "filter",
370 		.chains = {
371 			{
372 				.name	= "INPUT",
373 				.type	= "filter",
374 				.prio	= 0,	/* NF_IP_PRI_FILTER */
375 				.hook	= NF_INET_LOCAL_IN,
376 			},
377 			{
378 				.name	= "FORWARD",
379 				.type	= "filter",
380 				.prio	= 0,	/* NF_IP_PRI_FILTER */
381 				.hook	= NF_INET_FORWARD,
382 			},
383 			{
384 				.name	= "OUTPUT",
385 				.type	= "filter",
386 				.prio	= 0,	/* NF_IP_PRI_FILTER */
387 				.hook	= NF_INET_LOCAL_OUT,
388 			},
389 		},
390 	},
391 	[SECURITY] = {
392 		.name	= "security",
393 		.chains = {
394 			{
395 				.name	= "INPUT",
396 				.type	= "filter",
397 				.prio	= 150,	/* NF_IP_PRI_SECURITY */
398 				.hook	= NF_INET_LOCAL_IN,
399 			},
400 			{
401 				.name	= "FORWARD",
402 				.type	= "filter",
403 				.prio	= 150,	/* NF_IP_PRI_SECURITY */
404 				.hook	= NF_INET_FORWARD,
405 			},
406 			{
407 				.name	= "OUTPUT",
408 				.type	= "filter",
409 				.prio	= 150,	/* NF_IP_PRI_SECURITY */
410 				.hook	= NF_INET_LOCAL_OUT,
411 			},
412 		},
413 	},
414 	[NAT] = {
415 		.name	= "nat",
416 		.chains = {
417 			{
418 				.name	= "PREROUTING",
419 				.type	= "nat",
420 				.prio	= -100, /* NF_IP_PRI_NAT_DST */
421 				.hook	= NF_INET_PRE_ROUTING,
422 			},
423 			{
424 				.name	= "INPUT",
425 				.type	= "nat",
426 				.prio	= 100, /* NF_IP_PRI_NAT_SRC */
427 				.hook	= NF_INET_LOCAL_IN,
428 			},
429 			{
430 				.name	= "POSTROUTING",
431 				.type	= "nat",
432 				.prio	= 100, /* NF_IP_PRI_NAT_SRC */
433 				.hook	= NF_INET_POST_ROUTING,
434 			},
435 			{
436 				.name	= "OUTPUT",
437 				.type	= "nat",
438 				.prio	= -100, /* NF_IP_PRI_NAT_DST */
439 				.hook	= NF_INET_LOCAL_OUT,
440 			},
441 		},
442 	},
443 };
444 
445 #include <linux/netfilter_arp.h>
446 
447 struct builtin_table xtables_arp[TABLES_MAX] = {
448 	[FILTER] = {
449 	.name   = "filter",
450 	.chains = {
451 			{
452 				.name   = "INPUT",
453 				.type   = "filter",
454 				.prio   = NF_IP_PRI_FILTER,
455 				.hook   = NF_ARP_IN,
456 			},
457 			{
458 				.name   = "FORWARD",
459 				.type   = "filter",
460 				.prio   = NF_IP_PRI_FILTER,
461 				.hook   = NF_ARP_FORWARD,
462 			},
463 			{
464 				.name   = "OUTPUT",
465 				.type   = "filter",
466 				.prio   = NF_IP_PRI_FILTER,
467 				.hook   = NF_ARP_OUT,
468 			},
469 		},
470 	},
471 };
472 
473 #include <linux/netfilter_bridge.h>
474 
475 struct builtin_table xtables_bridge[TABLES_MAX] = {
476 	[FILTER] = {
477 		.name = "filter",
478 		.chains = {
479 			{
480 				.name   = "INPUT",
481 				.type   = "filter",
482 				.prio   = NF_BR_PRI_FILTER_BRIDGED,
483 				.hook   = NF_BR_LOCAL_IN,
484 			},
485 			{
486 				.name   = "FORWARD",
487 				.type   = "filter",
488 				.prio   = NF_BR_PRI_FILTER_BRIDGED,
489 				.hook   = NF_BR_FORWARD,
490 			},
491 			{
492 				.name   = "OUTPUT",
493 				.type   = "filter",
494 				.prio   = NF_BR_PRI_FILTER_BRIDGED,
495 				.hook   = NF_BR_LOCAL_OUT,
496 			},
497 		},
498 	},
499 	[NAT] = {
500 		.name = "nat",
501 		.chains = {
502 			{
503 				.name   = "PREROUTING",
504 				.type   = "filter",
505 				.prio   = NF_BR_PRI_NAT_DST_BRIDGED,
506 				.hook   = NF_BR_PRE_ROUTING,
507 			},
508 			{
509 				.name   = "OUTPUT",
510 				.type   = "filter",
511 				.prio   = NF_BR_PRI_NAT_DST_OTHER,
512 				.hook   = NF_BR_LOCAL_OUT,
513 			},
514 			{
515 				.name   = "POSTROUTING",
516 				.type   = "filter",
517 				.prio   = NF_BR_PRI_NAT_SRC,
518 				.hook   = NF_BR_POST_ROUTING,
519 			},
520 		},
521 	},
522 };
523 
nft_table_add(struct nft_handle * h,struct nftnl_table * t,uint16_t flags)524 int nft_table_add(struct nft_handle *h, struct nftnl_table *t, uint16_t flags)
525 {
526 	char buf[MNL_SOCKET_BUFFER_SIZE];
527 	struct nlmsghdr *nlh;
528 	int ret;
529 
530 	nlh = nftnl_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family,
531 					NLM_F_ACK|flags, h->seq);
532 	nftnl_table_nlmsg_build_payload(nlh, t);
533 	nftnl_table_free(t);
534 
535 #ifdef NLDEBUG
536 	char tmp[1024];
537 
538 	nft_table_snprintf(tmp, sizeof(tmp), t, 0, 0);
539 	printf("DEBUG: table: %s\n", tmp);
540 	mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
541 #endif
542 
543 	ret = mnl_talk(h, nlh, NULL, NULL);
544 
545 	return (ret == 0 || (ret == -1 && errno == EEXIST)) ? 0 : -1;
546 }
547 
nft_table_builtin_add(struct nft_handle * h,struct builtin_table * _t)548 static int nft_table_builtin_add(struct nft_handle *h,
549 				 struct builtin_table *_t)
550 {
551 	struct nftnl_table *t;
552 	int ret;
553 
554 	if (_t->initialized)
555 		return 0;
556 
557 	t = nftnl_table_alloc();
558 	if (t == NULL)
559 		return -1;
560 
561 	nftnl_table_set(t, NFTNL_TABLE_NAME, (char *)_t->name);
562 
563 	if (h->batch_support)
564 		ret = batch_table_add(h, NFT_COMPAT_TABLE_ADD, t);
565 	else
566 		ret = nft_table_add(h, t, NLM_F_EXCL);
567 
568 	if (ret == 0)
569 		_t->initialized = true;
570 
571 	return ret;
572 }
573 
574 static struct nftnl_chain *
nft_chain_builtin_alloc(struct builtin_table * table,struct builtin_chain * chain,int policy)575 nft_chain_builtin_alloc(struct builtin_table *table,
576 			struct builtin_chain *chain, int policy)
577 {
578 	struct nftnl_chain *c;
579 
580 	c = nftnl_chain_alloc();
581 	if (c == NULL)
582 		return NULL;
583 
584 	nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table->name);
585 	nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)chain->name);
586 	nftnl_chain_set_u32(c, NFTNL_CHAIN_HOOKNUM, chain->hook);
587 	nftnl_chain_set_u32(c, NFTNL_CHAIN_PRIO, chain->prio);
588 	nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, policy);
589 	nftnl_chain_set(c, NFTNL_CHAIN_TYPE, (char *)chain->type);
590 
591 	return c;
592 }
593 
nft_chain_add(struct nft_handle * h,struct nftnl_chain * c,uint16_t flags)594 int nft_chain_add(struct nft_handle *h, struct nftnl_chain *c, uint16_t flags)
595 {
596 	char buf[MNL_SOCKET_BUFFER_SIZE];
597 	struct nlmsghdr *nlh;
598 
599 	/* NLM_F_CREATE requests module autoloading */
600 	nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family,
601 					NLM_F_ACK|flags|NLM_F_CREATE,
602 					h->seq);
603 	nftnl_chain_nlmsg_build_payload(nlh, c);
604 	nftnl_chain_free(c);
605 
606 #ifdef NLDEBUG
607 	char tmp[1024];
608 
609 	nft_chain_snprintf(tmp, sizeof(tmp), c, 0, 0);
610 	printf("DEBUG: chain: %s\n", tmp);
611 	mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
612 #endif
613 
614 	return mnl_talk(h, nlh, NULL, NULL);
615 }
616 
nft_chain_builtin_add(struct nft_handle * h,struct builtin_table * table,struct builtin_chain * chain)617 static void nft_chain_builtin_add(struct nft_handle *h,
618 				  struct builtin_table *table,
619 				  struct builtin_chain *chain)
620 {
621 	struct nftnl_chain *c;
622 
623 	c = nft_chain_builtin_alloc(table, chain, NF_ACCEPT);
624 	if (c == NULL)
625 		return;
626 
627 	if (h->batch_support)
628 		batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, c);
629 	else
630 		nft_chain_add(h, c, NLM_F_EXCL);
631 }
632 
633 /* find if built-in table already exists */
634 static struct builtin_table *
nft_table_builtin_find(struct nft_handle * h,const char * table)635 nft_table_builtin_find(struct nft_handle *h, const char *table)
636 {
637 	int i;
638 	bool found = false;
639 
640 	for (i=0; i<TABLES_MAX; i++) {
641 		if (h->tables[i].name == NULL)
642 			continue;
643 
644 		if (strcmp(h->tables[i].name, table) != 0)
645 			continue;
646 
647 		found = true;
648 		break;
649 	}
650 
651 	return found ? &h->tables[i] : NULL;
652 }
653 
654 /* find if built-in chain already exists */
655 static struct builtin_chain *
nft_chain_builtin_find(struct builtin_table * t,const char * chain)656 nft_chain_builtin_find(struct builtin_table *t, const char *chain)
657 {
658 	int i;
659 	bool found = false;
660 
661 	for (i=0; i<NF_IP_NUMHOOKS && t->chains[i].name != NULL; i++) {
662 		if (strcmp(t->chains[i].name, chain) != 0)
663 			continue;
664 
665 		found = true;
666 		break;
667 	}
668 	return found ? &t->chains[i] : NULL;
669 }
670 
nft_chain_builtin_init(struct nft_handle * h,struct builtin_table * table)671 static void nft_chain_builtin_init(struct nft_handle *h,
672 				   struct builtin_table *table)
673 {
674 	int i;
675 	struct nftnl_chain_list *list = nft_chain_dump(h);
676 	struct nftnl_chain *c;
677 
678 	/* Initialize built-in chains if they don't exist yet */
679 	for (i=0; i<NF_IP_NUMHOOKS && table->chains[i].name != NULL; i++) {
680 
681 		c = nft_chain_list_find(list, table->name,
682 					table->chains[i].name);
683 		if (c != NULL)
684 			continue;
685 
686 		nft_chain_builtin_add(h, table, &table->chains[i]);
687 	}
688 
689 	nftnl_chain_list_free(list);
690 }
691 
nft_xt_builtin_init(struct nft_handle * h,const char * table)692 static int nft_xt_builtin_init(struct nft_handle *h, const char *table)
693 {
694 	int ret = 0;
695 	struct builtin_table *t;
696 
697 	t = nft_table_builtin_find(h, table);
698 	if (t == NULL) {
699 		ret = -1;
700 		goto out;
701 	}
702 	if (nft_table_builtin_add(h, t) < 0) {
703 		/* Built-in table already initialized, skip. */
704 		if (errno == EEXIST)
705 			goto out;
706 	}
707 	nft_chain_builtin_init(h, t);
708 out:
709 	return ret;
710 }
711 
nft_chain_builtin(struct nftnl_chain * c)712 static bool nft_chain_builtin(struct nftnl_chain *c)
713 {
714 	/* Check if this chain has hook number, in that case is built-in.
715 	 * Should we better export the flags to user-space via nf_tables?
716 	 */
717 	return nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM) != NULL;
718 }
719 
mnl_batch_supported(struct nft_handle * h)720 static bool mnl_batch_supported(struct nft_handle *h)
721 {
722 	char buf[MNL_SOCKET_BUFFER_SIZE];
723 	uint32_t seq = 1;
724 	int ret;
725 
726 	mnl_nftnl_batch_begin(h->batch, seq++);
727 
728 	nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
729 				NFT_MSG_NEWSET, AF_INET,
730 				NLM_F_ACK, seq++);
731 	mnl_nlmsg_batch_next(h->batch);
732 
733 	mnl_nftnl_batch_end(h->batch, seq++);
734 
735 	ret = mnl_socket_sendto(h->nl, mnl_nlmsg_batch_head(h->batch),
736 				mnl_nlmsg_batch_size(h->batch));
737 	if (ret < 0)
738 		goto err;
739 
740 	mnl_nlmsg_batch_reset(h->batch);
741 
742 	ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
743 	while (ret > 0) {
744 		ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(h->nl),
745 				 NULL, NULL);
746 		if (ret <= 0)
747 			break;
748 
749 		ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
750 	}
751 
752 	/* We're sending an incomplete message to see if the kernel supports
753 	 * set messages in batches. EINVAL means that we sent an incomplete
754 	 * message with missing attributes. The kernel just ignores messages
755 	 * that we cannot include in the batch.
756 	 */
757 	return (ret == -1 && errno == EINVAL) ? true : false;
758 err:
759 	mnl_nlmsg_batch_reset(h->batch);
760 	return ret;
761 }
762 
nft_init(struct nft_handle * h,struct builtin_table * t)763 int nft_init(struct nft_handle *h, struct builtin_table *t)
764 {
765 	h->nl = mnl_socket_open(NETLINK_NETFILTER);
766 	if (h->nl == NULL)
767 		return -1;
768 
769 	if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0)
770 		return -1;
771 
772 	h->portid = mnl_socket_get_portid(h->nl);
773 	h->tables = t;
774 
775 	INIT_LIST_HEAD(&h->obj_list);
776 
777 	h->batch = mnl_nftnl_batch_alloc();
778 	h->batch_support = mnl_batch_supported(h);
779 
780 	return 0;
781 }
782 
flush_rule_cache(struct nft_handle * h)783 static void flush_rule_cache(struct nft_handle *h)
784 {
785 	if (!h->rule_cache)
786 		return;
787 
788 	nftnl_rule_list_free(h->rule_cache);
789 	h->rule_cache = NULL;
790 }
791 
nft_fini(struct nft_handle * h)792 void nft_fini(struct nft_handle *h)
793 {
794 	flush_rule_cache(h);
795 	mnl_socket_close(h->nl);
796 	free(mnl_nlmsg_batch_head(h->batch));
797 	mnl_nlmsg_batch_stop(h->batch);
798 }
799 
nft_chain_print_debug(struct nftnl_chain * c,struct nlmsghdr * nlh)800 static void nft_chain_print_debug(struct nftnl_chain *c, struct nlmsghdr *nlh)
801 {
802 #ifdef NLDEBUG
803 	char tmp[1024];
804 
805 	nft_chain_snprintf(tmp, sizeof(tmp), c, 0, 0);
806 	printf("DEBUG: chain: %s\n", tmp);
807 	mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
808 #endif
809 }
810 
nft_chain_new(struct nft_handle * h,const char * table,const char * chain,int policy,const struct xt_counters * counters)811 static struct nftnl_chain *nft_chain_new(struct nft_handle *h,
812 				       const char *table, const char *chain,
813 				       int policy,
814 				       const struct xt_counters *counters)
815 {
816 	struct nftnl_chain *c;
817 	struct builtin_table *_t;
818 	struct builtin_chain *_c;
819 
820 	_t = nft_table_builtin_find(h, table);
821 	/* if this built-in table does not exists, create it */
822 	if (_t != NULL)
823 		nft_table_builtin_add(h, _t);
824 
825 	_c = nft_chain_builtin_find(_t, chain);
826 	if (_c != NULL) {
827 		/* This is a built-in chain */
828 		c = nft_chain_builtin_alloc(_t, _c, policy);
829 		if (c == NULL)
830 			return NULL;
831 	} else {
832 		errno = ENOENT;
833 		return NULL;
834 	}
835 
836 	if (counters) {
837 		nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES,
838 					counters->bcnt);
839 		nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS,
840 					counters->pcnt);
841 	}
842 
843 	return c;
844 }
845 
nft_chain_set(struct nft_handle * h,const char * table,const char * chain,const char * policy,const struct xt_counters * counters)846 int nft_chain_set(struct nft_handle *h, const char *table,
847 		  const char *chain, const char *policy,
848 		  const struct xt_counters *counters)
849 {
850 	struct nftnl_chain *c = NULL;
851 	int ret;
852 
853 	nft_fn = nft_chain_set;
854 
855 	if (strcmp(policy, "DROP") == 0)
856 		c = nft_chain_new(h, table, chain, NF_DROP, counters);
857 	else if (strcmp(policy, "ACCEPT") == 0)
858 		c = nft_chain_new(h, table, chain, NF_ACCEPT, counters);
859 
860 	if (c == NULL)
861 		return 0;
862 
863 	if (h->batch_support)
864 		ret = batch_chain_add(h, NFT_COMPAT_CHAIN_UPDATE, c);
865 	else
866 		ret = nft_chain_add(h, c, 0);
867 
868 	/* the core expects 1 for success and 0 for error */
869 	return ret == 0 ? 1 : 0;
870 }
871 
__add_match(struct nftnl_expr * e,struct xt_entry_match * m)872 static int __add_match(struct nftnl_expr *e, struct xt_entry_match *m)
873 {
874 	void *info;
875 
876 	nftnl_expr_set(e, NFTNL_EXPR_MT_NAME, m->u.user.name, strlen(m->u.user.name));
877 	nftnl_expr_set_u32(e, NFTNL_EXPR_MT_REV, m->u.user.revision);
878 
879 	info = calloc(1, m->u.match_size);
880 	if (info == NULL)
881 		return -ENOMEM;
882 
883 	memcpy(info, m->data, m->u.match_size - sizeof(*m));
884 	nftnl_expr_set(e, NFTNL_EXPR_MT_INFO, info, m->u.match_size - sizeof(*m));
885 
886 	return 0;
887 }
888 
add_match(struct nftnl_rule * r,struct xt_entry_match * m)889 int add_match(struct nftnl_rule *r, struct xt_entry_match *m)
890 {
891 	struct nftnl_expr *expr;
892 	int ret;
893 
894 	expr = nftnl_expr_alloc("match");
895 	if (expr == NULL)
896 		return -ENOMEM;
897 
898 	ret = __add_match(expr, m);
899 	nftnl_rule_add_expr(r, expr);
900 
901 	return ret;
902 }
903 
__add_target(struct nftnl_expr * e,struct xt_entry_target * t)904 static int __add_target(struct nftnl_expr *e, struct xt_entry_target *t)
905 {
906 	void *info;
907 
908 	nftnl_expr_set(e, NFTNL_EXPR_TG_NAME, t->u.user.name,
909 			  strlen(t->u.user.name));
910 	nftnl_expr_set_u32(e, NFTNL_EXPR_TG_REV, t->u.user.revision);
911 
912 	info = calloc(1, t->u.target_size);
913 	if (info == NULL)
914 		return -ENOMEM;
915 
916 	memcpy(info, t->data, t->u.target_size - sizeof(*t));
917 	nftnl_expr_set(e, NFTNL_EXPR_TG_INFO, info, t->u.target_size - sizeof(*t));
918 
919 	return 0;
920 }
921 
add_target(struct nftnl_rule * r,struct xt_entry_target * t)922 int add_target(struct nftnl_rule *r, struct xt_entry_target *t)
923 {
924 	struct nftnl_expr *expr;
925 	int ret;
926 
927 	expr = nftnl_expr_alloc("target");
928 	if (expr == NULL)
929 		return -ENOMEM;
930 
931 	ret = __add_target(expr, t);
932 	nftnl_rule_add_expr(r, expr);
933 
934 	return ret;
935 }
936 
add_jumpto(struct nftnl_rule * r,const char * name,int verdict)937 int add_jumpto(struct nftnl_rule *r, const char *name, int verdict)
938 {
939 	struct nftnl_expr *expr;
940 
941 	expr = nftnl_expr_alloc("immediate");
942 	if (expr == NULL)
943 		return -ENOMEM;
944 
945 	nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT);
946 	nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_VERDICT, verdict);
947 	nftnl_expr_set_str(expr, NFTNL_EXPR_IMM_CHAIN, (char *)name);
948 	nftnl_rule_add_expr(r, expr);
949 
950 	return 0;
951 }
952 
add_verdict(struct nftnl_rule * r,int verdict)953 int add_verdict(struct nftnl_rule *r, int verdict)
954 {
955 	struct nftnl_expr *expr;
956 
957 	expr = nftnl_expr_alloc("immediate");
958 	if (expr == NULL)
959 		return -ENOMEM;
960 
961 	nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT);
962 	nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_VERDICT, verdict);
963 	nftnl_rule_add_expr(r, expr);
964 
965 	return 0;
966 }
967 
add_action(struct nftnl_rule * r,struct iptables_command_state * cs,bool goto_set)968 int add_action(struct nftnl_rule *r, struct iptables_command_state *cs,
969 	       bool goto_set)
970 {
971        int ret = 0;
972 
973        /* If no target at all, add nothing (default to continue) */
974        if (cs->target != NULL) {
975 	       /* Standard target? */
976 	       if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
977 		       ret = add_verdict(r, NF_ACCEPT);
978 	       else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
979 		       ret = add_verdict(r, NF_DROP);
980 	       else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
981 		       ret = add_verdict(r, NFT_RETURN);
982 	       else
983 		       ret = add_target(r, cs->target->t);
984        } else if (strlen(cs->jumpto) > 0) {
985 	       /* Not standard, then it's a go / jump to chain */
986 	       if (goto_set)
987 		       ret = add_jumpto(r, cs->jumpto, NFT_GOTO);
988 	       else
989 		       ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
990        }
991        return ret;
992 }
993 
nft_rule_print_debug(struct nftnl_rule * r,struct nlmsghdr * nlh)994 static void nft_rule_print_debug(struct nftnl_rule *r, struct nlmsghdr *nlh)
995 {
996 #ifdef NLDEBUG
997 	char tmp[1024];
998 
999 	nft_rule_snprintf(tmp, sizeof(tmp), r, 0, 0);
1000 	printf("DEBUG: rule: %s\n", tmp);
1001 	mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
1002 #endif
1003 }
1004 
add_counters(struct nftnl_rule * r,uint64_t packets,uint64_t bytes)1005 int add_counters(struct nftnl_rule *r, uint64_t packets, uint64_t bytes)
1006 {
1007 	struct nftnl_expr *expr;
1008 
1009 	expr = nftnl_expr_alloc("counter");
1010 	if (expr == NULL)
1011 		return -ENOMEM;
1012 
1013 	nftnl_expr_set_u64(expr, NFTNL_EXPR_CTR_PACKETS, packets);
1014 	nftnl_expr_set_u64(expr, NFTNL_EXPR_CTR_BYTES, bytes);
1015 
1016 	nftnl_rule_add_expr(r, expr);
1017 
1018 	return 0;
1019 }
1020 
1021 enum udata_type {
1022 	UDATA_TYPE_COMMENT,
1023 	__UDATA_TYPE_MAX,
1024 };
1025 #define UDATA_TYPE_MAX (__UDATA_TYPE_MAX - 1)
1026 
add_comment(struct nftnl_rule * r,const char * comment)1027 int add_comment(struct nftnl_rule *r, const char *comment)
1028 {
1029 	struct nftnl_udata_buf *udata;
1030 
1031 	udata = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
1032 	if (!udata)
1033 		return -ENOMEM;
1034 
1035 	if (!nftnl_udata_put_strz(udata, UDATA_TYPE_COMMENT, comment))
1036 		return -ENOMEM;
1037 	nftnl_rule_set_data(r, NFTNL_RULE_USERDATA,
1038 			    nftnl_udata_buf_data(udata),
1039 			    nftnl_udata_buf_len(udata));
1040 
1041 	nftnl_udata_buf_free(udata);
1042 
1043 	return 0;
1044 }
1045 
parse_udata_cb(const struct nftnl_udata * attr,void * data)1046 static int parse_udata_cb(const struct nftnl_udata *attr, void *data)
1047 {
1048 	unsigned char *value = nftnl_udata_get(attr);
1049 	uint8_t type = nftnl_udata_type(attr);
1050 	uint8_t len = nftnl_udata_len(attr);
1051 	const struct nftnl_udata **tb = data;
1052 
1053 	switch (type) {
1054 	case UDATA_TYPE_COMMENT:
1055 		if (value[len - 1] != '\0')
1056 			return -1;
1057 		break;
1058 	default:
1059 		return 0;
1060 	}
1061 	tb[type] = attr;
1062 	return 0;
1063 }
1064 
get_comment(const void * data,uint32_t data_len)1065 char *get_comment(const void *data, uint32_t data_len)
1066 {
1067 	const struct nftnl_udata *tb[UDATA_TYPE_MAX + 1] = {};
1068 
1069 	if (nftnl_udata_parse(data, data_len, parse_udata_cb, tb) < 0)
1070 		return NULL;
1071 
1072 	if (!tb[UDATA_TYPE_COMMENT])
1073 		return NULL;
1074 
1075 	return nftnl_udata_get(tb[UDATA_TYPE_COMMENT]);
1076 }
1077 
add_compat(struct nftnl_rule * r,uint32_t proto,bool inv)1078 void add_compat(struct nftnl_rule *r, uint32_t proto, bool inv)
1079 {
1080 	nftnl_rule_set_u32(r, NFTNL_RULE_COMPAT_PROTO, proto);
1081 	nftnl_rule_set_u32(r, NFTNL_RULE_COMPAT_FLAGS,
1082 			      inv ? NFT_RULE_COMPAT_F_INV : 0);
1083 }
1084 
1085 static struct nftnl_rule *
nft_rule_new(struct nft_handle * h,const char * chain,const char * table,void * data)1086 nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
1087 	     void *data)
1088 {
1089 	struct nftnl_rule *r;
1090 
1091 	r = nftnl_rule_alloc();
1092 	if (r == NULL)
1093 		return NULL;
1094 
1095 	nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, h->family);
1096 	nftnl_rule_set(r, NFTNL_RULE_TABLE, (char *)table);
1097 	nftnl_rule_set(r, NFTNL_RULE_CHAIN, (char *)chain);
1098 
1099 	if (h->ops->add(r, data) < 0)
1100 		goto err;
1101 
1102 	return r;
1103 err:
1104 	nftnl_rule_free(r);
1105 	return NULL;
1106 }
1107 
1108 int
nft_rule_append(struct nft_handle * h,const char * chain,const char * table,void * data,uint64_t handle,bool verbose)1109 nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
1110 		void *data, uint64_t handle, bool verbose)
1111 {
1112 	struct nftnl_rule *r;
1113 	int type;
1114 
1115 	/* If built-in chains don't exist for this table, create them */
1116 	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
1117 		nft_xt_builtin_init(h, table);
1118 
1119 	nft_fn = nft_rule_append;
1120 
1121 	r = nft_rule_new(h, chain, table, data);
1122 	if (r == NULL)
1123 		return 0;
1124 
1125 	if (handle > 0) {
1126 		nftnl_rule_set(r, NFTNL_RULE_HANDLE, &handle);
1127 		type = NFT_COMPAT_RULE_REPLACE;
1128 	} else
1129 		type = NFT_COMPAT_RULE_APPEND;
1130 
1131 	if (batch_rule_add(h, type, r) < 0)
1132 		nftnl_rule_free(r);
1133 
1134 	flush_rule_cache(h);
1135 	return 1;
1136 }
1137 
1138 void
nft_rule_print_save(const void * data,struct nftnl_rule * r,enum nft_rule_print type,unsigned int format)1139 nft_rule_print_save(const void *data,
1140 		    struct nftnl_rule *r, enum nft_rule_print type,
1141 		    unsigned int format)
1142 {
1143 	const char *chain = nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
1144 	int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
1145 	struct nft_family_ops *ops;
1146 
1147 	ops = nft_family_ops_lookup(family);
1148 
1149 	if (!(format & FMT_NOCOUNTS) && ops->save_counters)
1150 		ops->save_counters(data);
1151 
1152 	/* print chain name */
1153 	switch(type) {
1154 	case NFT_RULE_APPEND:
1155 		printf("-A %s ", chain);
1156 		break;
1157 	case NFT_RULE_DEL:
1158 		printf("-D %s ", chain);
1159 		break;
1160 	}
1161 
1162 	if (ops->save_firewall)
1163 		ops->save_firewall(data, format);
1164 
1165 }
1166 
nftnl_chain_list_cb(const struct nlmsghdr * nlh,void * data)1167 static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
1168 {
1169 	struct nftnl_chain *c;
1170 	struct nftnl_chain_list *list = data;
1171 
1172 	c = nftnl_chain_alloc();
1173 	if (c == NULL)
1174 		goto err;
1175 
1176 	if (nftnl_chain_nlmsg_parse(nlh, c) < 0)
1177 		goto out;
1178 
1179 	nftnl_chain_list_add_tail(c, list);
1180 
1181 	return MNL_CB_OK;
1182 out:
1183 	nftnl_chain_free(c);
1184 err:
1185 	return MNL_CB_OK;
1186 }
1187 
nftnl_chain_list_get(struct nft_handle * h)1188 static struct nftnl_chain_list *nftnl_chain_list_get(struct nft_handle *h)
1189 {
1190 	char buf[MNL_SOCKET_BUFFER_SIZE];
1191 	struct nlmsghdr *nlh;
1192 	struct nftnl_chain_list *list;
1193 
1194 	list = nftnl_chain_list_alloc();
1195 	if (list == NULL) {
1196 		errno = ENOMEM;
1197 		return NULL;
1198 	}
1199 
1200 	nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
1201 					NLM_F_DUMP, h->seq);
1202 
1203 	mnl_talk(h, nlh, nftnl_chain_list_cb, list);
1204 
1205 	return list;
1206 }
1207 
nft_chain_dump(struct nft_handle * h)1208 struct nftnl_chain_list *nft_chain_dump(struct nft_handle *h)
1209 {
1210 	return nftnl_chain_list_get(h);
1211 }
1212 
1213 static const char *policy_name[NF_ACCEPT+1] = {
1214 	[NF_DROP] = "DROP",
1215 	[NF_ACCEPT] = "ACCEPT",
1216 };
1217 
nft_chain_print_save(struct nftnl_chain * c,bool basechain)1218 static void nft_chain_print_save(struct nftnl_chain *c, bool basechain)
1219 {
1220 	const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
1221 	uint64_t pkts = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS);
1222 	uint64_t bytes = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES);
1223 
1224 	/* print chain name */
1225 	if (basechain) {
1226 		uint32_t pol = NF_ACCEPT;
1227 
1228 		/* no default chain policy? don't crash, display accept */
1229 		if (nftnl_chain_get(c, NFTNL_CHAIN_POLICY))
1230 			pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
1231 
1232 		printf(":%s %s [%"PRIu64":%"PRIu64"]\n", chain, policy_name[pol],
1233 					     pkts, bytes);
1234 	} else
1235 		printf(":%s - [%"PRIu64":%"PRIu64"]\n", chain, pkts, bytes);
1236 }
1237 
nft_chain_save(struct nft_handle * h,struct nftnl_chain_list * list,const char * table)1238 int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list,
1239 		   const char *table)
1240 {
1241 	struct nftnl_chain_list_iter *iter;
1242 	struct nftnl_chain *c;
1243 
1244 	iter = nftnl_chain_list_iter_create(list);
1245 	if (iter == NULL)
1246 		return 0;
1247 
1248 	c = nftnl_chain_list_iter_next(iter);
1249 	while (c != NULL) {
1250 		const char *chain_table =
1251 			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
1252 		bool basechain = false;
1253 
1254 		if (strcmp(table, chain_table) != 0)
1255 			goto next;
1256 
1257 		basechain = nft_chain_builtin(c);
1258 		nft_chain_print_save(c, basechain);
1259 next:
1260 		c = nftnl_chain_list_iter_next(iter);
1261 	}
1262 
1263 	nftnl_chain_list_iter_destroy(iter);
1264 	nftnl_chain_list_free(list);
1265 
1266 	return 1;
1267 }
1268 
nftnl_rule_list_cb(const struct nlmsghdr * nlh,void * data)1269 static int nftnl_rule_list_cb(const struct nlmsghdr *nlh, void *data)
1270 {
1271 	struct nftnl_rule *r;
1272 	struct nftnl_rule_list *list = data;
1273 
1274 	r = nftnl_rule_alloc();
1275 	if (r == NULL)
1276 		goto err;
1277 
1278 	if (nftnl_rule_nlmsg_parse(nlh, r) < 0)
1279 		goto out;
1280 
1281 	nftnl_rule_list_add_tail(r, list);
1282 
1283 	return MNL_CB_OK;
1284 out:
1285 	nftnl_rule_free(r);
1286 	nftnl_rule_list_free(list);
1287 err:
1288 	return MNL_CB_OK;
1289 }
1290 
nft_rule_list_get(struct nft_handle * h)1291 static struct nftnl_rule_list *nft_rule_list_get(struct nft_handle *h)
1292 {
1293 	char buf[MNL_SOCKET_BUFFER_SIZE];
1294 	struct nlmsghdr *nlh;
1295 	struct nftnl_rule_list *list;
1296 	int ret;
1297 
1298 	if (h->rule_cache)
1299 		return h->rule_cache;
1300 
1301 	list = nftnl_rule_list_alloc();
1302 	if (list == NULL)
1303 		return 0;
1304 
1305 	nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family,
1306 					NLM_F_DUMP, h->seq);
1307 
1308 	ret = mnl_talk(h, nlh, nftnl_rule_list_cb, list);
1309 	if (ret < 0) {
1310 		nftnl_rule_list_free(list);
1311 		return NULL;
1312 	}
1313 
1314 	h->rule_cache = list;
1315 	return list;
1316 }
1317 
nft_rule_save(struct nft_handle * h,const char * table,bool counters)1318 int nft_rule_save(struct nft_handle *h, const char *table, bool counters)
1319 {
1320 	struct nftnl_rule_list *list;
1321 	struct nftnl_rule_list_iter *iter;
1322 	struct nftnl_rule *r;
1323 
1324 	list = nft_rule_list_get(h);
1325 	if (list == NULL)
1326 		return 0;
1327 
1328 	iter = nftnl_rule_list_iter_create(list);
1329 	if (iter == NULL)
1330 		return 0;
1331 
1332 	r = nftnl_rule_list_iter_next(iter);
1333 	while (r != NULL) {
1334 		const char *rule_table =
1335 			nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
1336 		struct iptables_command_state cs = {};
1337 
1338 		if (strcmp(table, rule_table) != 0)
1339 			goto next;
1340 
1341 		nft_rule_to_iptables_command_state(r, &cs);
1342 
1343 		nft_rule_print_save(&cs, r, NFT_RULE_APPEND,
1344 				    counters ? 0 : FMT_NOCOUNTS);
1345 
1346 next:
1347 		r = nftnl_rule_list_iter_next(iter);
1348 	}
1349 
1350 	nftnl_rule_list_iter_destroy(iter);
1351 
1352 	/* the core expects 1 for success and 0 for error */
1353 	return 1;
1354 }
1355 
1356 static void
__nft_rule_flush(struct nft_handle * h,const char * table,const char * chain)1357 __nft_rule_flush(struct nft_handle *h, const char *table, const char *chain)
1358 {
1359 	struct nftnl_rule *r;
1360 
1361 	r = nftnl_rule_alloc();
1362 	if (r == NULL)
1363 		return;
1364 
1365 	nftnl_rule_set(r, NFTNL_RULE_TABLE, (char *)table);
1366 	nftnl_rule_set(r, NFTNL_RULE_CHAIN, (char *)chain);
1367 
1368 	if (batch_rule_add(h, NFT_COMPAT_RULE_FLUSH, r) < 0)
1369 		nftnl_rule_free(r);
1370 }
1371 
nft_rule_flush(struct nft_handle * h,const char * chain,const char * table)1372 int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table)
1373 {
1374 	int ret;
1375 	struct nftnl_chain_list *list;
1376 	struct nftnl_chain_list_iter *iter;
1377 	struct nftnl_chain *c;
1378 
1379 	nft_fn = nft_rule_flush;
1380 
1381 	list = nftnl_chain_list_get(h);
1382 	if (list == NULL) {
1383 		ret = 0;
1384 		goto err;
1385 	}
1386 
1387 	iter = nftnl_chain_list_iter_create(list);
1388 	if (iter == NULL)
1389 		goto err;
1390 
1391 	c = nftnl_chain_list_iter_next(iter);
1392 	while (c != NULL) {
1393 		const char *table_name =
1394 			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
1395 		const char *chain_name =
1396 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
1397 
1398 		if (strcmp(table, table_name) != 0)
1399 			goto next;
1400 
1401 		if (chain != NULL && strcmp(chain, chain_name) != 0)
1402 			goto next;
1403 
1404 		__nft_rule_flush(h, table_name, chain_name);
1405 
1406 		if (chain != NULL)
1407 			break;
1408 next:
1409 		c = nftnl_chain_list_iter_next(iter);
1410 	}
1411 
1412 	nftnl_chain_list_iter_destroy(iter);
1413 	flush_rule_cache(h);
1414 err:
1415 	nftnl_chain_list_free(list);
1416 
1417 	/* the core expects 1 for success and 0 for error */
1418 	return ret == 0 ? 1 : 0;
1419 }
1420 
nft_chain_user_add(struct nft_handle * h,const char * chain,const char * table)1421 int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table)
1422 {
1423 	struct nftnl_chain *c;
1424 	int ret;
1425 
1426 	nft_fn = nft_chain_user_add;
1427 
1428 	/* If built-in chains don't exist for this table, create them */
1429 	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
1430 		nft_xt_builtin_init(h, table);
1431 
1432 	c = nftnl_chain_alloc();
1433 	if (c == NULL)
1434 		return 0;
1435 
1436 	nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table);
1437 	nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)chain);
1438 
1439 	if (h->batch_support) {
1440 		ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
1441 	} else {
1442 		char buf[MNL_SOCKET_BUFFER_SIZE];
1443 		struct nlmsghdr *nlh;
1444 
1445 		nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN,
1446 						h->family,
1447 						NLM_F_ACK|NLM_F_EXCL, h->seq);
1448 		nftnl_chain_nlmsg_build_payload(nlh, c);
1449 		nftnl_chain_free(c);
1450 		ret = mnl_talk(h, nlh, NULL, NULL);
1451 	}
1452 
1453 	/* the core expects 1 for success and 0 for error */
1454 	return ret == 0 ? 1 : 0;
1455 }
1456 
__nft_chain_del(struct nft_handle * h,struct nftnl_chain * c)1457 static int __nft_chain_del(struct nft_handle *h, struct nftnl_chain *c)
1458 {
1459 	char buf[MNL_SOCKET_BUFFER_SIZE];
1460 	struct nlmsghdr *nlh;
1461 
1462 	nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN, h->family,
1463 					NLM_F_ACK, h->seq);
1464 	nftnl_chain_nlmsg_build_payload(nlh, c);
1465 
1466 	return mnl_talk(h, nlh, NULL, NULL);
1467 }
1468 
nft_chain_user_del(struct nft_handle * h,const char * chain,const char * table)1469 int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table)
1470 {
1471 	struct nftnl_chain_list *list;
1472 	struct nftnl_chain_list_iter *iter;
1473 	struct nftnl_chain *c;
1474 	int ret = 0;
1475 	int deleted_ctr = 0;
1476 
1477 	list = nftnl_chain_list_get(h);
1478 	if (list == NULL)
1479 		goto err;
1480 
1481 	iter = nftnl_chain_list_iter_create(list);
1482 	if (iter == NULL)
1483 		goto err;
1484 
1485 	c = nftnl_chain_list_iter_next(iter);
1486 	while (c != NULL) {
1487 		const char *table_name =
1488 			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
1489 		const char *chain_name =
1490 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
1491 
1492 		/* don't delete built-in chain */
1493 		if (nft_chain_builtin(c))
1494 			goto next;
1495 
1496 		if (strcmp(table, table_name) != 0)
1497 			goto next;
1498 
1499 		if (chain != NULL && strcmp(chain, chain_name) != 0)
1500 			goto next;
1501 
1502 		if (h->batch_support)
1503 			ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_DEL, c);
1504 		else
1505 			ret = __nft_chain_del(h, c);
1506 
1507 		if (ret < 0)
1508 			break;
1509 
1510 		deleted_ctr++;
1511 
1512 		if (chain != NULL)
1513 			break;
1514 next:
1515 		c = nftnl_chain_list_iter_next(iter);
1516 	}
1517 
1518 	nftnl_chain_list_iter_destroy(iter);
1519 err:
1520 	if (!h->batch_support)
1521 		nftnl_chain_list_free(list);
1522 
1523 	/* chain not found */
1524 	if (deleted_ctr == 0) {
1525 		ret = -1;
1526 		errno = ENOENT;
1527 	}
1528 
1529 	/* the core expects 1 for success and 0 for error */
1530 	return ret == 0 ? 1 : 0;
1531 }
1532 
1533 struct nftnl_chain *
nft_chain_list_find(struct nftnl_chain_list * list,const char * table,const char * chain)1534 nft_chain_list_find(struct nftnl_chain_list *list,
1535 		    const char *table, const char *chain)
1536 {
1537 	struct nftnl_chain_list_iter *iter;
1538 	struct nftnl_chain *c;
1539 
1540 	iter = nftnl_chain_list_iter_create(list);
1541 	if (iter == NULL)
1542 		return NULL;
1543 
1544 	c = nftnl_chain_list_iter_next(iter);
1545 	while (c != NULL) {
1546 		const char *table_name =
1547 			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
1548 		const char *chain_name =
1549 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
1550 
1551 		if (strcmp(table, table_name) != 0)
1552 			goto next;
1553 
1554 		if (strcmp(chain, chain_name) != 0)
1555 			goto next;
1556 
1557 		nftnl_chain_list_iter_destroy(iter);
1558 		return c;
1559 next:
1560 		c = nftnl_chain_list_iter_next(iter);
1561 	}
1562 	nftnl_chain_list_iter_destroy(iter);
1563 	return NULL;
1564 }
1565 
1566 static struct nftnl_chain *
nft_chain_find(struct nft_handle * h,const char * table,const char * chain)1567 nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
1568 {
1569 	struct nftnl_chain_list *list;
1570 
1571 	list = nftnl_chain_list_get(h);
1572 	if (list == NULL)
1573 		return NULL;
1574 
1575 	return nft_chain_list_find(list, table, chain);
1576 }
1577 
nft_chain_user_rename(struct nft_handle * h,const char * chain,const char * table,const char * newname)1578 int nft_chain_user_rename(struct nft_handle *h,const char *chain,
1579 			  const char *table, const char *newname)
1580 {
1581 	struct nftnl_chain *c;
1582 	uint64_t handle;
1583 	int ret;
1584 
1585 	nft_fn = nft_chain_user_add;
1586 
1587 	/* If built-in chains don't exist for this table, create them */
1588 	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
1589 		nft_xt_builtin_init(h, table);
1590 
1591 	/* Config load changed errno. Ensure genuine info for our callers. */
1592 	errno = 0;
1593 
1594 	/* Find the old chain to be renamed */
1595 	c = nft_chain_find(h, table, chain);
1596 	if (c == NULL) {
1597 		errno = ENOENT;
1598 		return -1;
1599 	}
1600 	handle = nftnl_chain_get_u64(c, NFTNL_CHAIN_HANDLE);
1601 
1602 	/* Now prepare the new name for the chain */
1603 	c = nftnl_chain_alloc();
1604 	if (c == NULL)
1605 		return -1;
1606 
1607 	nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table);
1608 	nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)newname);
1609 	nftnl_chain_set_u64(c, NFTNL_CHAIN_HANDLE, handle);
1610 
1611 	if (h->batch_support) {
1612 		ret = batch_chain_add(h, NFT_COMPAT_CHAIN_RENAME, c);
1613 	} else {
1614 		char buf[MNL_SOCKET_BUFFER_SIZE];
1615 		struct nlmsghdr *nlh;
1616 
1617 		nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN,
1618 						h->family, NLM_F_ACK, h->seq);
1619 		nftnl_chain_nlmsg_build_payload(nlh, c);
1620 		nftnl_chain_free(c);
1621 
1622 		ret = mnl_talk(h, nlh, NULL, NULL);
1623 	}
1624 
1625 	/* the core expects 1 for success and 0 for error */
1626 	return ret == 0 ? 1 : 0;
1627 }
1628 
nftnl_table_list_cb(const struct nlmsghdr * nlh,void * data)1629 static int nftnl_table_list_cb(const struct nlmsghdr *nlh, void *data)
1630 {
1631 	struct nftnl_table *t;
1632 	struct nftnl_table_list *list = data;
1633 
1634 	t = nftnl_table_alloc();
1635 	if (t == NULL)
1636 		goto err;
1637 
1638 	if (nftnl_table_nlmsg_parse(nlh, t) < 0)
1639 		goto out;
1640 
1641 	nftnl_table_list_add_tail(t, list);
1642 
1643 	return MNL_CB_OK;
1644 out:
1645 	nftnl_table_free(t);
1646 err:
1647 	return MNL_CB_OK;
1648 }
1649 
nftnl_table_list_get(struct nft_handle * h)1650 static struct nftnl_table_list *nftnl_table_list_get(struct nft_handle *h)
1651 {
1652 	char buf[MNL_SOCKET_BUFFER_SIZE];
1653 	struct nlmsghdr *nlh;
1654 	struct nftnl_table_list *list;
1655 
1656 	list = nftnl_table_list_alloc();
1657 	if (list == NULL)
1658 		return 0;
1659 
1660 	nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family,
1661 					NLM_F_DUMP, h->seq);
1662 
1663 	mnl_talk(h, nlh, nftnl_table_list_cb, list);
1664 
1665 	return list;
1666 }
1667 
nft_table_find(struct nft_handle * h,const char * tablename)1668 bool nft_table_find(struct nft_handle *h, const char *tablename)
1669 {
1670 	struct nftnl_table_list *list;
1671 	struct nftnl_table_list_iter *iter;
1672 	struct nftnl_table *t;
1673 	bool ret = false;
1674 
1675 	list = nftnl_table_list_get(h);
1676 	if (list == NULL)
1677 		goto err;
1678 
1679 	iter = nftnl_table_list_iter_create(list);
1680 	if (iter == NULL)
1681 		goto err;
1682 
1683 	t = nftnl_table_list_iter_next(iter);
1684 	while (t != NULL) {
1685 		const char *this_tablename =
1686 			nftnl_table_get(t, NFTNL_TABLE_NAME);
1687 
1688 		if (strcmp(tablename, this_tablename) == 0)
1689 			return true;
1690 
1691 		t = nftnl_table_list_iter_next(iter);
1692 	}
1693 
1694 	nftnl_table_list_free(list);
1695 
1696 err:
1697 	return ret;
1698 }
1699 
nft_for_each_table(struct nft_handle * h,int (* func)(struct nft_handle * h,const char * tablename,bool counters),bool counters)1700 int nft_for_each_table(struct nft_handle *h,
1701 		       int (*func)(struct nft_handle *h, const char *tablename, bool counters),
1702 		       bool counters)
1703 {
1704 	int ret = 1;
1705 	struct nftnl_table_list *list;
1706 	struct nftnl_table_list_iter *iter;
1707 	struct nftnl_table *t;
1708 
1709 	list = nftnl_table_list_get(h);
1710 	if (list == NULL) {
1711 		ret = 0;
1712 		goto err;
1713 	}
1714 
1715 	iter = nftnl_table_list_iter_create(list);
1716 	if (iter == NULL)
1717 		return 0;
1718 
1719 	t = nftnl_table_list_iter_next(iter);
1720 	while (t != NULL) {
1721 		const char *tablename =
1722 			nftnl_table_get(t, NFTNL_TABLE_NAME);
1723 
1724 		func(h, tablename, counters);
1725 
1726 		t = nftnl_table_list_iter_next(iter);
1727 	}
1728 
1729 	nftnl_table_list_free(list);
1730 
1731 err:
1732 	/* the core expects 1 for success and 0 for error */
1733 	return ret == 0 ? 1 : 0;
1734 }
1735 
nft_table_purge_chains(struct nft_handle * h,const char * this_table,struct nftnl_chain_list * chain_list)1736 int nft_table_purge_chains(struct nft_handle *h, const char *this_table,
1737 			   struct nftnl_chain_list *chain_list)
1738 {
1739 	struct nftnl_chain_list_iter *iter;
1740 	struct nftnl_chain *chain_obj;
1741 
1742 	iter = nftnl_chain_list_iter_create(chain_list);
1743 	if (iter == NULL)
1744 		return 0;
1745 
1746 	chain_obj = nftnl_chain_list_iter_next(iter);
1747 	while (chain_obj != NULL) {
1748 		const char *table =
1749 			nftnl_chain_get_str(chain_obj, NFTNL_CHAIN_TABLE);
1750 
1751 		if (strcmp(this_table, table) != 0)
1752 			goto next;
1753 
1754 		if (nft_chain_builtin(chain_obj))
1755 			goto next;
1756 
1757 		if ( __nft_chain_del(h, chain_obj) < 0) {
1758 			if (errno != EBUSY)
1759 				return -1;
1760 		}
1761 next:
1762 		chain_obj = nftnl_chain_list_iter_next(iter);
1763 	}
1764 	nftnl_chain_list_iter_destroy(iter);
1765 
1766 	return 0;
1767 }
1768 
__nft_rule_del(struct nft_handle * h,struct nftnl_rule_list * list,struct nftnl_rule * r)1769 static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule_list *list,
1770 			  struct nftnl_rule *r)
1771 {
1772 	int ret;
1773 
1774 	nftnl_rule_list_del(r);
1775 
1776 	ret = batch_rule_add(h, NFT_COMPAT_RULE_DELETE, r);
1777 	if (ret < 0) {
1778 		nftnl_rule_free(r);
1779 		return -1;
1780 	}
1781 	return 1;
1782 }
1783 
1784 static struct nftnl_rule *
nft_rule_find(struct nft_handle * h,struct nftnl_rule_list * list,const char * chain,const char * table,void * data,int rulenum)1785 nft_rule_find(struct nft_handle *h, struct nftnl_rule_list *list,
1786 	      const char *chain, const char *table, void *data, int rulenum)
1787 {
1788 	struct nftnl_rule *r;
1789 	struct nftnl_rule_list_iter *iter;
1790 	int rule_ctr = 0;
1791 	bool found = false;
1792 
1793 	iter = nftnl_rule_list_iter_create(list);
1794 	if (iter == NULL)
1795 		return 0;
1796 
1797 	r = nftnl_rule_list_iter_next(iter);
1798 	while (r != NULL) {
1799 		const char *rule_table =
1800 			nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
1801 		const char *rule_chain =
1802 			nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
1803 
1804 		if (strcmp(table, rule_table) != 0 ||
1805 		    strcmp(chain, rule_chain) != 0) {
1806 			DEBUGP("different chain / table\n");
1807 			goto next;
1808 		}
1809 
1810 		if (rulenum >= 0) {
1811 			/* Delete by rule number case */
1812 			if (rule_ctr == rulenum) {
1813 			    found = true;
1814 			    break;
1815 			}
1816 		} else {
1817 			found = h->ops->rule_find(h->ops, r, data);
1818 			if (found)
1819 				break;
1820 		}
1821 		rule_ctr++;
1822 next:
1823 		r = nftnl_rule_list_iter_next(iter);
1824 	}
1825 
1826 	nftnl_rule_list_iter_destroy(iter);
1827 
1828 	return found ? r : NULL;
1829 }
1830 
nft_rule_check(struct nft_handle * h,const char * chain,const char * table,void * data,bool verbose)1831 int nft_rule_check(struct nft_handle *h, const char *chain,
1832 		   const char *table, void *data, bool verbose)
1833 {
1834 	struct nftnl_rule_list *list;
1835 	int ret;
1836 
1837 	nft_fn = nft_rule_check;
1838 
1839 	list = nft_rule_list_get(h);
1840 	if (list == NULL)
1841 		return 0;
1842 
1843 	ret = nft_rule_find(h, list, chain, table, data, -1) ? 1 : 0;
1844 	if (ret == 0)
1845 		errno = ENOENT;
1846 
1847 	return ret;
1848 }
1849 
nft_rule_delete(struct nft_handle * h,const char * chain,const char * table,void * data,bool verbose)1850 int nft_rule_delete(struct nft_handle *h, const char *chain,
1851 		    const char *table, void *data, bool verbose)
1852 {
1853 	int ret = 0;
1854 	struct nftnl_rule *r;
1855 	struct nftnl_rule_list *list;
1856 
1857 	nft_fn = nft_rule_delete;
1858 
1859 	list = nft_rule_list_get(h);
1860 	if (list == NULL)
1861 		return 0;
1862 
1863 	r = nft_rule_find(h, list, chain, table, data, -1);
1864 	if (r != NULL) {
1865 		ret =__nft_rule_del(h, list, r);
1866 		if (ret < 0)
1867 			errno = ENOMEM;
1868 	} else
1869 		errno = ENOENT;
1870 
1871 	flush_rule_cache(h);
1872 
1873 	return ret;
1874 }
1875 
1876 static int
nft_rule_add(struct nft_handle * h,const char * chain,const char * table,struct iptables_command_state * cs,uint64_t handle,bool verbose)1877 nft_rule_add(struct nft_handle *h, const char *chain,
1878 	     const char *table, struct iptables_command_state *cs,
1879 	     uint64_t handle, bool verbose)
1880 {
1881 	struct nftnl_rule *r;
1882 
1883 	r = nft_rule_new(h, chain, table, cs);
1884 	if (r == NULL)
1885 		return 0;
1886 
1887 	if (handle > 0)
1888 		nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle);
1889 
1890 	if (batch_rule_add(h, NFT_COMPAT_RULE_INSERT, r) < 0) {
1891 		nftnl_rule_free(r);
1892 		return 0;
1893 	}
1894 
1895 	flush_rule_cache(h);
1896 	return 1;
1897 }
1898 
nft_rule_insert(struct nft_handle * h,const char * chain,const char * table,void * data,int rulenum,bool verbose)1899 int nft_rule_insert(struct nft_handle *h, const char *chain,
1900 		    const char *table, void *data, int rulenum, bool verbose)
1901 {
1902 	struct nftnl_rule_list *list;
1903 	struct nftnl_rule *r;
1904 	uint64_t handle = 0;
1905 
1906 	/* If built-in chains don't exist for this table, create them */
1907 	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
1908 		nft_xt_builtin_init(h, table);
1909 
1910 	nft_fn = nft_rule_insert;
1911 
1912 	if (rulenum > 0) {
1913 		list = nft_rule_list_get(h);
1914 		if (list == NULL)
1915 			goto err;
1916 
1917 		r = nft_rule_find(h, list, chain, table, data, rulenum);
1918 		if (r == NULL) {
1919 			/* special case: iptables allows to insert into
1920 			 * rule_count + 1 position.
1921 			 */
1922 			r = nft_rule_find(h, list, chain, table, data,
1923 					  rulenum - 1);
1924 			if (r != NULL) {
1925 				flush_rule_cache(h);
1926 				return nft_rule_append(h, chain, table, data,
1927 						       0, verbose);
1928 			}
1929 
1930 			errno = ENOENT;
1931 			goto err;
1932 		}
1933 
1934 		handle = nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE);
1935 		DEBUGP("adding after rule handle %"PRIu64"\n", handle);
1936 
1937 		flush_rule_cache(h);
1938 	}
1939 
1940 	return nft_rule_add(h, chain, table, data, handle, verbose);
1941 err:
1942 	flush_rule_cache(h);
1943 	return 0;
1944 }
1945 
nft_rule_delete_num(struct nft_handle * h,const char * chain,const char * table,int rulenum,bool verbose)1946 int nft_rule_delete_num(struct nft_handle *h, const char *chain,
1947 			const char *table, int rulenum, bool verbose)
1948 {
1949 	int ret = 0;
1950 	struct nftnl_rule *r;
1951 	struct nftnl_rule_list *list;
1952 
1953 	nft_fn = nft_rule_delete_num;
1954 
1955 	list = nft_rule_list_get(h);
1956 	if (list == NULL)
1957 		return 0;
1958 
1959 	r = nft_rule_find(h, list, chain, table, NULL, rulenum);
1960 	if (r != NULL) {
1961 		ret = 1;
1962 
1963 		DEBUGP("deleting rule by number %d\n", rulenum);
1964 		ret = __nft_rule_del(h, list, r);
1965 		if (ret < 0)
1966 			errno = ENOMEM;
1967 	} else
1968 		errno = ENOENT;
1969 
1970 	flush_rule_cache(h);
1971 
1972 	return ret;
1973 }
1974 
nft_rule_replace(struct nft_handle * h,const char * chain,const char * table,void * data,int rulenum,bool verbose)1975 int nft_rule_replace(struct nft_handle *h, const char *chain,
1976 		     const char *table, void *data, int rulenum, bool verbose)
1977 {
1978 	int ret = 0;
1979 	struct nftnl_rule *r;
1980 	struct nftnl_rule_list *list;
1981 
1982 	nft_fn = nft_rule_replace;
1983 
1984 	list = nft_rule_list_get(h);
1985 	if (list == NULL)
1986 		return 0;
1987 
1988 	r = nft_rule_find(h, list, chain, table, data, rulenum);
1989 	if (r != NULL) {
1990 		DEBUGP("replacing rule with handle=%llu\n",
1991 			(unsigned long long)
1992 			nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE));
1993 
1994 		ret = nft_rule_append(h, chain, table, data,
1995 				      nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE),
1996 				      verbose);
1997 	} else
1998 		errno = ENOENT;
1999 
2000 	flush_rule_cache(h);
2001 
2002 	return ret;
2003 }
2004 
2005 static int
__nft_rule_list(struct nft_handle * h,const char * chain,const char * table,int rulenum,unsigned int format,void (* cb)(struct nftnl_rule * r,unsigned int num,unsigned int format))2006 __nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
2007 		int rulenum, unsigned int format,
2008 		void (*cb)(struct nftnl_rule *r, unsigned int num,
2009 			   unsigned int format))
2010 {
2011 	struct nftnl_rule_list *list;
2012 	struct nftnl_rule_list_iter *iter;
2013 	struct nftnl_rule *r;
2014 	int rule_ctr = 0, ret = 0;
2015 
2016 	list = nft_rule_list_get(h);
2017 	if (list == NULL)
2018 		return 0;
2019 
2020 	iter = nftnl_rule_list_iter_create(list);
2021 	if (iter == NULL)
2022 		goto err;
2023 
2024 	r = nftnl_rule_list_iter_next(iter);
2025 	while (r != NULL) {
2026 		const char *rule_table =
2027 			nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
2028 		const char *rule_chain =
2029 			nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
2030 
2031 		if (strcmp(table, rule_table) != 0 ||
2032 		    strcmp(chain, rule_chain) != 0)
2033 			goto next;
2034 
2035 		rule_ctr++;
2036 
2037 		if (rulenum > 0 && rule_ctr != rulenum) {
2038 			/* List by rule number case */
2039 			goto next;
2040 		}
2041 
2042 		cb(r, rule_ctr, format);
2043 		if (rulenum > 0 && rule_ctr == rulenum) {
2044 			ret = 1;
2045 			break;
2046 		}
2047 
2048 next:
2049 		r = nftnl_rule_list_iter_next(iter);
2050 	}
2051 
2052 	nftnl_rule_list_iter_destroy(iter);
2053 err:
2054 	if (ret == 0)
2055 		errno = ENOENT;
2056 
2057 	return ret;
2058 }
2059 
nft_rule_list(struct nft_handle * h,const char * chain,const char * table,int rulenum,unsigned int format)2060 int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
2061 		  int rulenum, unsigned int format)
2062 {
2063 	const struct nft_family_ops *ops;
2064 	struct nftnl_chain_list *list;
2065 	struct nftnl_chain_list_iter *iter;
2066 	struct nftnl_chain *c;
2067 	bool found = false;
2068 
2069 	/* If built-in chains don't exist for this table, create them */
2070 	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) {
2071 		nft_xt_builtin_init(h, table);
2072 		/* Force table and chain creation, otherwise first iptables -L
2073 		 * lists no table/chains.
2074 		 */
2075 		if (!list_empty(&h->obj_list))
2076 			nft_commit(h);
2077 	}
2078 
2079 	ops = nft_family_ops_lookup(h->family);
2080 
2081 	if (chain && rulenum) {
2082 		__nft_rule_list(h, chain, table,
2083 				rulenum, format, ops->print_firewall);
2084 		return 1;
2085 	}
2086 
2087 	list = nft_chain_dump(h);
2088 
2089 	iter = nftnl_chain_list_iter_create(list);
2090 	if (iter == NULL)
2091 		goto err;
2092 
2093 	if (ops->print_table_header)
2094 		ops->print_table_header(table);
2095 
2096 	c = nftnl_chain_list_iter_next(iter);
2097 	while (c != NULL) {
2098 		const char *chain_table =
2099 			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
2100 		const char *chain_name =
2101 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
2102 		uint32_t policy =
2103 			nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
2104 		uint32_t refs =
2105 			nftnl_chain_get_u32(c, NFTNL_CHAIN_USE);
2106 		struct xt_counters ctrs = {
2107 			.pcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
2108 			.bcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES),
2109 		};
2110 		bool basechain = false;
2111 
2112 		if (nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM))
2113 			basechain = true;
2114 
2115 		if (strcmp(table, chain_table) != 0)
2116 			goto next;
2117 		if (chain && strcmp(chain, chain_name) != 0)
2118 			goto next;
2119 
2120 		if (found)
2121 			printf("\n");
2122 
2123 		ops->print_header(format, chain_name, policy_name[policy],
2124 				  &ctrs, basechain, refs);
2125 
2126 		__nft_rule_list(h, chain_name, table,
2127 				rulenum, format, ops->print_firewall);
2128 
2129 		/* we printed the chain we wanted, stop processing. */
2130 		if (chain)
2131 			break;
2132 
2133 		found = true;
2134 
2135 next:
2136 		c = nftnl_chain_list_iter_next(iter);
2137 	}
2138 
2139 	nftnl_chain_list_iter_destroy(iter);
2140 err:
2141 	nftnl_chain_list_free(list);
2142 
2143 	return 1;
2144 }
2145 
2146 static void
list_save(struct nftnl_rule * r,unsigned int num,unsigned int format)2147 list_save(struct nftnl_rule *r, unsigned int num, unsigned int format)
2148 {
2149 	struct iptables_command_state cs = {};
2150 
2151 	nft_rule_to_iptables_command_state(r, &cs);
2152 
2153 	nft_rule_print_save(&cs, r, NFT_RULE_APPEND, !(format & FMT_NOCOUNTS));
2154 }
2155 
2156 static int
nftnl_rule_list_chain_save(struct nft_handle * h,const char * chain,const char * table,struct nftnl_chain_list * list,int counters)2157 nftnl_rule_list_chain_save(struct nft_handle *h, const char *chain,
2158 			 const char *table, struct nftnl_chain_list *list,
2159 			 int counters)
2160 {
2161 	struct nftnl_chain_list_iter *iter;
2162 	struct nftnl_chain *c;
2163 
2164 	iter = nftnl_chain_list_iter_create(list);
2165 	if (iter == NULL)
2166 		return 0;
2167 
2168 	c = nftnl_chain_list_iter_next(iter);
2169 	while (c != NULL) {
2170 		const char *chain_table =
2171 			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
2172 		const char *chain_name =
2173 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
2174 		uint32_t policy =
2175 			nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
2176 
2177 		if (strcmp(table, chain_table) != 0 ||
2178 		    (chain && strcmp(chain, chain_name) != 0))
2179 			goto next;
2180 
2181 		/* this is a base chain */
2182 		if (nft_chain_builtin(c)) {
2183 			printf("-P %s %s", chain_name, policy_name[policy]);
2184 
2185 			if (counters) {
2186 				printf(" -c %"PRIu64" %"PRIu64"\n",
2187 					nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
2188 					nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES));
2189 			} else
2190 				printf("\n");
2191 		} else {
2192 			printf("-N %s\n", chain_name);
2193 		}
2194 next:
2195 		c = nftnl_chain_list_iter_next(iter);
2196 	}
2197 
2198 	nftnl_chain_list_iter_destroy(iter);
2199 
2200 	return 1;
2201 }
2202 
nft_rule_list_save(struct nft_handle * h,const char * chain,const char * table,int rulenum,int counters)2203 int nft_rule_list_save(struct nft_handle *h, const char *chain,
2204 		       const char *table, int rulenum, int counters)
2205 {
2206 	struct nftnl_chain_list *list;
2207 	struct nftnl_chain_list_iter *iter;
2208 	struct nftnl_chain *c;
2209 	int ret = 1;
2210 
2211 	list = nft_chain_dump(h);
2212 
2213 	/* Dump policies and custom chains first */
2214 	if (!rulenum)
2215 		nftnl_rule_list_chain_save(h, chain, table, list, counters);
2216 
2217 	/* Now dump out rules in this table */
2218 	iter = nftnl_chain_list_iter_create(list);
2219 	if (iter == NULL)
2220 		goto err;
2221 
2222 	c = nftnl_chain_list_iter_next(iter);
2223 	while (c != NULL) {
2224 		const char *chain_table =
2225 			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
2226 		const char *chain_name =
2227 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
2228 
2229 		if (strcmp(table, chain_table) != 0)
2230 			goto next;
2231 		if (chain && strcmp(chain, chain_name) != 0)
2232 			goto next;
2233 
2234 		ret = __nft_rule_list(h, chain_name, table, rulenum,
2235 				      counters ? 0 : FMT_NOCOUNTS, list_save);
2236 
2237 		/* we printed the chain we wanted, stop processing. */
2238 		if (chain)
2239 			break;
2240 next:
2241 		c = nftnl_chain_list_iter_next(iter);
2242 	}
2243 
2244 	nftnl_chain_list_iter_destroy(iter);
2245 err:
2246 	nftnl_chain_list_free(list);
2247 
2248 	return ret;
2249 }
2250 
nft_rule_zero_counters(struct nft_handle * h,const char * chain,const char * table,int rulenum)2251 int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
2252 			   const char *table, int rulenum)
2253 {
2254 	struct iptables_command_state cs = {};
2255 	struct nftnl_rule_list *list;
2256 	struct nftnl_rule *r;
2257 	int ret = 0;
2258 
2259 	nft_fn = nft_rule_delete;
2260 
2261 	list = nft_rule_list_get(h);
2262 	if (list == NULL)
2263 		return 0;
2264 
2265 	r = nft_rule_find(h, list, chain, table, NULL, rulenum);
2266 	if (r == NULL) {
2267 		errno = ENOENT;
2268 		ret = 1;
2269 		goto error;
2270 	}
2271 
2272 	nft_rule_to_iptables_command_state(r, &cs);
2273 
2274 	cs.counters.pcnt = cs.counters.bcnt = 0;
2275 
2276 	ret =  nft_rule_append(h, chain, table, &cs,
2277 			       nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE),
2278 			       false);
2279 
2280 error:
2281 	flush_rule_cache(h);
2282 
2283 	return ret;
2284 }
2285 
nft_compat_table_batch_add(struct nft_handle * h,uint16_t type,uint16_t flags,uint32_t seq,struct nftnl_table * table)2286 static void nft_compat_table_batch_add(struct nft_handle *h, uint16_t type,
2287 				       uint16_t flags, uint32_t seq,
2288 				       struct nftnl_table *table)
2289 {
2290 	struct nlmsghdr *nlh;
2291 
2292 	nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
2293 					type, h->family, flags, seq);
2294 	nftnl_table_nlmsg_build_payload(nlh, table);
2295 	nftnl_table_free(table);
2296 }
2297 
nft_compat_chain_batch_add(struct nft_handle * h,uint16_t type,uint16_t flags,uint32_t seq,struct nftnl_chain * chain)2298 static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type,
2299 				       uint16_t flags, uint32_t seq,
2300 				       struct nftnl_chain *chain)
2301 {
2302 	struct nlmsghdr *nlh;
2303 
2304 	nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
2305 					type, h->family, flags, seq);
2306 	nftnl_chain_nlmsg_build_payload(nlh, chain);
2307 	nft_chain_print_debug(chain, nlh);
2308 	nftnl_chain_free(chain);
2309 }
2310 
nft_compat_rule_batch_add(struct nft_handle * h,uint16_t type,uint16_t flags,uint32_t seq,struct nftnl_rule * rule)2311 static void nft_compat_rule_batch_add(struct nft_handle *h, uint16_t type,
2312 				      uint16_t flags, uint32_t seq,
2313 				      struct nftnl_rule *rule)
2314 {
2315 	struct nlmsghdr *nlh;
2316 
2317 	nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
2318 				       type, h->family, flags, seq);
2319 	nftnl_rule_nlmsg_build_payload(nlh, rule);
2320 	nft_rule_print_debug(rule, nlh);
2321 	nftnl_rule_free(rule);
2322 }
2323 
nft_action(struct nft_handle * h,int action)2324 static int nft_action(struct nft_handle *h, int action)
2325 {
2326 	struct obj_update *n, *tmp;
2327 	uint32_t seq = 1;
2328 	int ret = 0;
2329 
2330 	mnl_nftnl_batch_begin(h->batch, seq++);
2331 
2332 	list_for_each_entry_safe(n, tmp, &h->obj_list, head) {
2333 		switch (n->type) {
2334 		case NFT_COMPAT_TABLE_ADD:
2335 			nft_compat_table_batch_add(h, NFT_MSG_NEWTABLE,
2336 						   NLM_F_CREATE, seq++,
2337 						   n->table);
2338 			break;
2339 		case NFT_COMPAT_CHAIN_ADD:
2340 			nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
2341 						   NLM_F_CREATE, seq++,
2342 						   n->chain);
2343 			break;
2344 		case NFT_COMPAT_CHAIN_USER_ADD:
2345 			nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
2346 						   NLM_F_EXCL, seq++,
2347 						   n->chain);
2348 			break;
2349 		case NFT_COMPAT_CHAIN_USER_DEL:
2350 			nft_compat_chain_batch_add(h, NFT_MSG_DELCHAIN,
2351 						   0, seq++, n->chain);
2352 			break;
2353 		case NFT_COMPAT_CHAIN_UPDATE:
2354 			nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
2355 						   h->restore ?
2356 						     NLM_F_CREATE : 0,
2357 						   seq++, n->chain);
2358 			break;
2359 		case NFT_COMPAT_CHAIN_RENAME:
2360 			nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN, 0,
2361 						   seq++, n->chain);
2362 			break;
2363 		case NFT_COMPAT_RULE_APPEND:
2364 			nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
2365 						  NLM_F_CREATE | NLM_F_APPEND,
2366 						  seq++, n->rule);
2367 			break;
2368 		case NFT_COMPAT_RULE_INSERT:
2369 			nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
2370 						  NLM_F_CREATE, seq++,
2371 						  n->rule);
2372 			break;
2373 		case NFT_COMPAT_RULE_REPLACE:
2374 			nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
2375 						  NLM_F_CREATE | NLM_F_REPLACE,
2376 						  seq++, n->rule);
2377 			break;
2378 		case NFT_COMPAT_RULE_DELETE:
2379 		case NFT_COMPAT_RULE_FLUSH:
2380 			nft_compat_rule_batch_add(h, NFT_MSG_DELRULE, 0,
2381 						  seq++, n->rule);
2382 			break;
2383 		}
2384 
2385 		h->obj_list_num--;
2386 		list_del(&n->head);
2387 		free(n);
2388 
2389 		if (!mnl_nlmsg_batch_next(h->batch))
2390 			h->batch = mnl_nftnl_batch_page_add(h->batch);
2391 	}
2392 
2393 	switch (action) {
2394 	case NFT_COMPAT_COMMIT:
2395 		mnl_nftnl_batch_end(h->batch, seq++);
2396 		break;
2397 	case NFT_COMPAT_ABORT:
2398 		break;
2399 	}
2400 
2401 	if (!mnl_nlmsg_batch_is_empty(h->batch))
2402 		h->batch = mnl_nftnl_batch_page_add(h->batch);
2403 
2404 	ret = mnl_nftnl_batch_talk(h);
2405 
2406 	mnl_nlmsg_batch_reset(h->batch);
2407 
2408 	return ret == 0 ? 1 : 0;
2409 }
2410 
nft_commit(struct nft_handle * h)2411 int nft_commit(struct nft_handle *h)
2412 {
2413 	return nft_action(h, NFT_COMPAT_COMMIT);
2414 }
2415 
nft_abort(struct nft_handle * h)2416 int nft_abort(struct nft_handle *h)
2417 {
2418 	return nft_action(h, NFT_COMPAT_ABORT);
2419 }
2420 
nft_compatible_revision(const char * name,uint8_t rev,int opt)2421 int nft_compatible_revision(const char *name, uint8_t rev, int opt)
2422 {
2423 	struct mnl_socket *nl;
2424 	char buf[MNL_SOCKET_BUFFER_SIZE];
2425 	struct nlmsghdr *nlh;
2426 	uint32_t portid, seq, type;
2427 	int ret = 0;
2428 
2429 	if (opt == IPT_SO_GET_REVISION_MATCH ||
2430 	    opt == IP6T_SO_GET_REVISION_MATCH)
2431 		type = 0;
2432 	else
2433 		type = 1;
2434 
2435 	nlh = mnl_nlmsg_put_header(buf);
2436 	nlh->nlmsg_type = (NFNL_SUBSYS_NFT_COMPAT << 8) | NFNL_MSG_COMPAT_GET;
2437 	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
2438 	nlh->nlmsg_seq = seq = time(NULL);
2439 
2440 	struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
2441 	nfg->nfgen_family = AF_INET;
2442 	nfg->version = NFNETLINK_V0;
2443 	nfg->res_id = 0;
2444 
2445 	mnl_attr_put_strz(nlh, NFTA_COMPAT_NAME, name);
2446 	mnl_attr_put_u32(nlh, NFTA_COMPAT_REV, htonl(rev));
2447 	mnl_attr_put_u32(nlh, NFTA_COMPAT_TYPE, htonl(type));
2448 
2449 	DEBUGP("requesting `%s' rev=%d type=%d via nft_compat\n",
2450 		name, rev, type);
2451 
2452 	nl = mnl_socket_open(NETLINK_NETFILTER);
2453 	if (nl == NULL)
2454 		return 0;
2455 
2456 	if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
2457 		goto err;
2458 
2459 	portid = mnl_socket_get_portid(nl);
2460 
2461 	if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
2462 		goto err;
2463 
2464 	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
2465 	if (ret == -1)
2466 		goto err;
2467 
2468 	ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
2469 	if (ret == -1)
2470 		goto err;
2471 
2472 err:
2473 	mnl_socket_close(nl);
2474 
2475 	return ret < 0 ? 0 : 1;
2476 }
2477 
2478 /* Translates errno numbers into more human-readable form than strerror. */
nft_strerror(int err)2479 const char *nft_strerror(int err)
2480 {
2481 	unsigned int i;
2482 	static struct table_struct {
2483 		void *fn;
2484 		int err;
2485 		const char *message;
2486 	} table[] =
2487 	  {
2488 	    { nft_chain_user_del, ENOTEMPTY, "Chain is not empty" },
2489 	    { nft_chain_user_del, EINVAL, "Can't delete built-in chain" },
2490 	    { nft_chain_user_del, EBUSY, "Directory not empty" },
2491 	    { nft_chain_user_del, EMLINK,
2492 	      "Can't delete chain with references left" },
2493 	    { nft_chain_user_add, EEXIST, "Chain already exists" },
2494 	    { nft_rule_add, E2BIG, "Index of insertion too big" },
2495 	    { nft_rule_check, ENOENT, "Bad rule (does a matching rule exist in that chain?)" },
2496 	    { nft_rule_replace, ENOENT, "Index of replacement too big" },
2497 	    { nft_rule_delete_num, E2BIG, "Index of deletion too big" },
2498 /*	    { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
2499 	    { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" }, */
2500 	    { nft_rule_add, ELOOP, "Loop found in table" },
2501 	    { nft_rule_add, EINVAL, "Target problem" },
2502 	    /* ENOENT for DELETE probably means no matching rule */
2503 	    { nft_rule_delete, ENOENT,
2504 	      "Bad rule (does a matching rule exist in that chain?)" },
2505 	    { nft_chain_set, ENOENT, "Bad built-in chain name" },
2506 	    { nft_chain_set, EINVAL, "Bad policy name" },
2507 	    { NULL, EPERM, "Permission denied (you must be root)" },
2508 	    { NULL, 0, "Incompatible with this kernel" },
2509 	    { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
2510 	    { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
2511 	    { NULL, ENOMEM, "Memory allocation problem" },
2512 	    { NULL, ENOENT, "No chain/target/match by that name" },
2513 	  };
2514 
2515 	for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
2516 		if ((!table[i].fn || table[i].fn == nft_fn)
2517 		    && table[i].err == err)
2518 			return table[i].message;
2519 	}
2520 
2521 	return strerror(err);
2522 }
2523 
xtables_config_perror(uint32_t flags,const char * fmt,...)2524 static void xtables_config_perror(uint32_t flags, const char *fmt, ...)
2525 {
2526 	va_list args;
2527 
2528 	va_start(args, fmt);
2529 
2530 	if (flags & NFT_LOAD_VERBOSE)
2531 		vfprintf(stderr, fmt, args);
2532 
2533 	va_end(args);
2534 }
2535 
nft_xtables_config_load(struct nft_handle * h,const char * filename,uint32_t flags)2536 int nft_xtables_config_load(struct nft_handle *h, const char *filename,
2537 			    uint32_t flags)
2538 {
2539 	struct nftnl_table_list *table_list = nftnl_table_list_alloc();
2540 	struct nftnl_chain_list *chain_list = nftnl_chain_list_alloc();
2541 	struct nftnl_table_list_iter *titer = NULL;
2542 	struct nftnl_chain_list_iter *citer = NULL;
2543 	struct nftnl_table *table;
2544 	struct nftnl_chain *chain;
2545 	uint32_t table_family, chain_family;
2546 	bool found = false;
2547 
2548 	if (h->restore)
2549 		return 0;
2550 
2551 	if (xtables_config_parse(filename, table_list, chain_list) < 0) {
2552 		if (errno == ENOENT) {
2553 			xtables_config_perror(flags,
2554 				"configuration file `%s' does not exists\n",
2555 				filename);
2556 		} else {
2557 			xtables_config_perror(flags,
2558 				"Fatal error parsing config file: %s\n",
2559 				 strerror(errno));
2560 		}
2561 		goto err;
2562 	}
2563 
2564 	/* Stage 1) create tables */
2565 	titer = nftnl_table_list_iter_create(table_list);
2566 	while ((table = nftnl_table_list_iter_next(titer)) != NULL) {
2567 		table_family = nftnl_table_get_u32(table,
2568 						      NFTNL_TABLE_FAMILY);
2569 		if (h->family != table_family)
2570 			continue;
2571 
2572 		found = true;
2573 
2574 		if (batch_table_add(h, NFT_COMPAT_TABLE_ADD, table) < 0) {
2575 			if (errno == EEXIST) {
2576 				xtables_config_perror(flags,
2577 					"table `%s' already exists, skipping\n",
2578 					(char *)nftnl_table_get(table, NFTNL_TABLE_NAME));
2579 			} else {
2580 				xtables_config_perror(flags,
2581 					"table `%s' cannot be create, reason `%s'. Exitting\n",
2582 					(char *)nftnl_table_get(table, NFTNL_TABLE_NAME),
2583 					strerror(errno));
2584 				goto err;
2585 			}
2586 			continue;
2587 		}
2588 		xtables_config_perror(flags, "table `%s' has been created\n",
2589 			(char *)nftnl_table_get(table, NFTNL_TABLE_NAME));
2590 	}
2591 	nftnl_table_list_iter_destroy(titer);
2592 	nftnl_table_list_free(table_list);
2593 
2594 	if (!found)
2595 		goto err;
2596 
2597 	/* Stage 2) create chains */
2598 	citer = nftnl_chain_list_iter_create(chain_list);
2599 	while ((chain = nftnl_chain_list_iter_next(citer)) != NULL) {
2600 		chain_family = nftnl_chain_get_u32(chain,
2601 						      NFTNL_CHAIN_TABLE);
2602 		if (h->family != chain_family)
2603 			continue;
2604 
2605 		if (batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, chain) < 0) {
2606 			if (errno == EEXIST) {
2607 				xtables_config_perror(flags,
2608 					"chain `%s' already exists in table `%s', skipping\n",
2609 					(char *)nftnl_chain_get(chain, NFTNL_CHAIN_NAME),
2610 					(char *)nftnl_chain_get(chain, NFTNL_CHAIN_TABLE));
2611 			} else {
2612 				xtables_config_perror(flags,
2613 					"chain `%s' cannot be create, reason `%s'. Exitting\n",
2614 					(char *)nftnl_chain_get(chain, NFTNL_CHAIN_NAME),
2615 					strerror(errno));
2616 				goto err;
2617 			}
2618 			continue;
2619 		}
2620 
2621 		xtables_config_perror(flags,
2622 			"chain `%s' in table `%s' has been created\n",
2623 			(char *)nftnl_chain_get(chain, NFTNL_CHAIN_NAME),
2624 			(char *)nftnl_chain_get(chain, NFTNL_CHAIN_TABLE));
2625 	}
2626 	nftnl_chain_list_iter_destroy(citer);
2627 	nftnl_chain_list_free(chain_list);
2628 
2629 	return 0;
2630 
2631 err:
2632 	nftnl_table_list_free(table_list);
2633 	nftnl_chain_list_free(chain_list);
2634 
2635 	if (titer != NULL)
2636 		nftnl_table_list_iter_destroy(titer);
2637 	if (citer != NULL)
2638 		nftnl_chain_list_iter_destroy(citer);
2639 
2640 	return -1;
2641 }
2642 
nft_chain_zero_counters(struct nft_handle * h,const char * chain,const char * table)2643 int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
2644 			    const char *table)
2645 {
2646 	struct nftnl_chain_list *list;
2647 	struct nftnl_chain_list_iter *iter;
2648 	struct nftnl_chain *c;
2649 	int ret = 0;
2650 
2651 	list = nftnl_chain_list_get(h);
2652 	if (list == NULL)
2653 		goto err;
2654 
2655 	iter = nftnl_chain_list_iter_create(list);
2656 	if (iter == NULL)
2657 		goto err;
2658 
2659 	c = nftnl_chain_list_iter_next(iter);
2660 	while (c != NULL) {
2661 		const char *chain_name =
2662 			nftnl_chain_get(c, NFTNL_CHAIN_NAME);
2663 		const char *chain_table =
2664 			nftnl_chain_get(c, NFTNL_CHAIN_TABLE);
2665 
2666 		if (strcmp(table, chain_table) != 0)
2667 			goto next;
2668 
2669 		if (chain != NULL && strcmp(chain, chain_name) != 0)
2670 			goto next;
2671 
2672 		nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, 0);
2673 		nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, 0);
2674 
2675 		nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
2676 
2677 		if (h->batch_support) {
2678 			ret = batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, c);
2679 		} else {
2680 			struct nlmsghdr *nlh;
2681 			char buf[MNL_SOCKET_BUFFER_SIZE];
2682 
2683 			nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN,
2684 							h->family, NLM_F_ACK,
2685 							h->seq);
2686 			nftnl_chain_nlmsg_build_payload(nlh, c);
2687 			ret = mnl_talk(h, nlh, NULL, NULL);
2688 		}
2689 
2690 		if (chain != NULL)
2691 			break;
2692 next:
2693 		c = nftnl_chain_list_iter_next(iter);
2694 	}
2695 
2696 	if (!h->batch_support)
2697 		nftnl_chain_list_free(list);
2698 
2699 	nftnl_chain_list_iter_destroy(iter);
2700 
2701 err:
2702 	/* the core expects 1 for success and 0 for error */
2703 	return ret == 0 ? 1 : 0;
2704 }
2705 
nft_invflags2cmp(uint32_t invflags,uint32_t flag)2706 uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag)
2707 {
2708 	if (invflags & flag)
2709 		return NFT_CMP_NEQ;
2710 
2711 	return NFT_CMP_EQ;
2712 }
2713 
2714 #define NFT_COMPAT_EXPR_MAX     8
2715 
2716 static const char *supported_exprs[NFT_COMPAT_EXPR_MAX] = {
2717 	"match",
2718 	"target",
2719 	"payload",
2720 	"meta",
2721 	"cmp",
2722 	"bitwise",
2723 	"counter",
2724 	"immediate"
2725 };
2726 
2727 
nft_is_expr_compatible(const char * name)2728 static int nft_is_expr_compatible(const char *name)
2729 {
2730 	int i;
2731 
2732 	for (i = 0; i < NFT_COMPAT_EXPR_MAX; i++) {
2733 		if (strcmp(supported_exprs[i], name) == 0)
2734 			return 0;
2735 	}
2736 
2737 	return 1;
2738 }
2739 
nft_is_rule_compatible(struct nftnl_rule * rule)2740 static int nft_is_rule_compatible(struct nftnl_rule *rule)
2741 {
2742 	struct nftnl_expr_iter *iter;
2743 	struct nftnl_expr *expr;
2744 	int ret = 0;
2745 
2746 	iter = nftnl_expr_iter_create(rule);
2747 	if (iter == NULL)
2748 		return -1;
2749 
2750 	expr = nftnl_expr_iter_next(iter);
2751 	while (expr != NULL) {
2752 		const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
2753 
2754 		if (nft_is_expr_compatible(name) == 0) {
2755 			expr = nftnl_expr_iter_next(iter);
2756 			continue;
2757 		}
2758 
2759 		ret = 1;
2760 		break;
2761 	}
2762 
2763 	nftnl_expr_iter_destroy(iter);
2764 	return ret;
2765 }
2766 
nft_is_chain_compatible(const char * table,const char * chain)2767 static int nft_is_chain_compatible(const char *table, const char *chain)
2768 {
2769 	const char *cur_table;
2770 	struct builtin_chain *chains;
2771 	int i, j;
2772 
2773 	for (i = 0; i < TABLES_MAX; i++) {
2774 		cur_table = xtables_ipv4[i].name;
2775 		chains = xtables_ipv4[i].chains;
2776 
2777 		if (strcmp(table, cur_table) != 0)
2778 			continue;
2779 
2780 		for (j = 0; j < NF_INET_NUMHOOKS && chains[j].name; j++) {
2781 			if (strcmp(chain, chains[j].name) == 0)
2782 				return 0;
2783 		}
2784 	}
2785 
2786 	return 1;
2787 }
2788 
nft_are_chains_compatible(struct nft_handle * h)2789 static int nft_are_chains_compatible(struct nft_handle *h)
2790 {
2791 	struct nftnl_chain_list *list;
2792 	struct nftnl_chain_list_iter *iter;
2793 	struct nftnl_chain *chain;
2794 	int ret = 0;
2795 
2796 	list = nftnl_chain_list_get(h);
2797 	if (list == NULL)
2798 		return -1;
2799 
2800 	iter = nftnl_chain_list_iter_create(list);
2801 	if (iter == NULL)
2802 		return -1;
2803 
2804 	chain = nftnl_chain_list_iter_next(iter);
2805 	while (chain != NULL) {
2806 		if (!nft_chain_builtin(chain))
2807 			goto next;
2808 
2809 		const char *table = nftnl_chain_get(chain, NFTNL_CHAIN_TABLE);
2810 		const char *name = nftnl_chain_get(chain, NFTNL_CHAIN_NAME);
2811 
2812 		if (nft_is_chain_compatible(table, name) == 1) {
2813 			ret = 1;
2814 			break;
2815 		}
2816 
2817 next:
2818 		chain = nftnl_chain_list_iter_next(iter);
2819 	}
2820 
2821 	nftnl_chain_list_iter_destroy(iter);
2822 	nftnl_chain_list_free(list);
2823 	return ret;
2824 }
2825 
nft_is_table_compatible(const char * name)2826 static int nft_is_table_compatible(const char *name)
2827 {
2828 	int i;
2829 
2830 	for (i = 0; i < TABLES_MAX; i++) {
2831 		if (strcmp(xtables_ipv4[i].name, name) == 0)
2832 			return 0;
2833 	}
2834 
2835 	return 1;
2836 }
2837 
nft_are_tables_compatible(struct nft_handle * h)2838 static int nft_are_tables_compatible(struct nft_handle *h)
2839 {
2840 	struct nftnl_table_list *list;
2841 	struct nftnl_table_list_iter *iter;
2842 	struct nftnl_table *table;
2843 	int ret = 0;
2844 
2845 	list = nftnl_table_list_get(h);
2846 	if (list == NULL)
2847 		return -1;
2848 
2849 	iter = nftnl_table_list_iter_create(list);
2850 	if (iter == NULL)
2851 		return -1;
2852 
2853 	table = nftnl_table_list_iter_next(iter);
2854 	while (table != NULL) {
2855 		const char *name = nftnl_table_get(table, NFTNL_TABLE_NAME);
2856 
2857 		if (nft_is_table_compatible(name) == 0) {
2858 			table = nftnl_table_list_iter_next(iter);
2859 			continue;
2860 		}
2861 
2862 		ret = 1;
2863 		break;
2864 	}
2865 
2866 	nftnl_table_list_iter_destroy(iter);
2867 	nftnl_table_list_free(list);
2868 	return ret;
2869 }
2870 
nft_is_ruleset_compatible(struct nft_handle * h)2871 int nft_is_ruleset_compatible(struct nft_handle *h)
2872 {
2873 
2874 	struct nftnl_rule_list *list;
2875 	struct nftnl_rule_list_iter *iter;
2876 	struct nftnl_rule *rule;
2877 	int ret = 0;
2878 
2879 	ret = nft_are_tables_compatible(h);
2880 	if (ret != 0)
2881 		return ret;
2882 
2883 	ret = nft_are_chains_compatible(h);
2884 	if (ret != 0)
2885 		return ret;
2886 
2887 	list = nft_rule_list_get(h);
2888 	if (list == NULL)
2889 		return -1;
2890 
2891 	iter = nftnl_rule_list_iter_create(list);
2892 	if (iter == NULL)
2893 		return -1;
2894 
2895 	rule = nftnl_rule_list_iter_next(iter);
2896 	while (rule != NULL) {
2897 		ret = nft_is_rule_compatible(rule);
2898 		if (ret != 0)
2899 			break;
2900 
2901 		rule = nftnl_rule_list_iter_next(iter);
2902 	}
2903 
2904 	nftnl_rule_list_iter_destroy(iter);
2905 	return ret;
2906 }
2907