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