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