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