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