1 /*
2 * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
3 * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
11 */
12
13 #include <stdbool.h>
14 #include <stdlib.h>
15 #include <string.h>
16
17 #include <linux/netfilter/nf_log.h>
18 #include <linux/netfilter/xt_limit.h>
19 #include <linux/netfilter/xt_mark.h>
20 #include <linux/netfilter/xt_NFLOG.h>
21 #include <linux/netfilter/xt_pkttype.h>
22
23 #include <linux/netfilter_ipv6/ip6t_hl.h>
24
25 #include <libnftnl/rule.h>
26 #include <libnftnl/expr.h>
27
28 #include <xtables.h>
29
30 #include "nft-ruleparse.h"
31 #include "nft.h"
32
33 static struct xtables_match *
nft_find_match_in_cs(struct iptables_command_state * cs,const char * name)34 nft_find_match_in_cs(struct iptables_command_state *cs, const char *name)
35 {
36 struct xtables_rule_match *rm;
37 struct ebt_match *ebm;
38
39 for (ebm = cs->match_list; ebm; ebm = ebm->next) {
40 if (ebm->ismatch &&
41 !strcmp(ebm->u.match->m->u.user.name, name))
42 return ebm->u.match;
43 }
44 for (rm = cs->matches; rm; rm = rm->next) {
45 if (!strcmp(rm->match->m->u.user.name, name))
46 return rm->match;
47 }
48 return NULL;
49 }
50
51 void *
nft_create_match(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,const char * name,bool reuse)52 nft_create_match(struct nft_xt_ctx *ctx,
53 struct iptables_command_state *cs,
54 const char *name, bool reuse)
55 {
56 struct xtables_match *match;
57 struct xt_entry_match *m;
58 unsigned int size;
59
60 if (reuse) {
61 match = nft_find_match_in_cs(cs, name);
62 if (match)
63 return match->m->data;
64 }
65
66 match = xtables_find_match(name, XTF_TRY_LOAD,
67 &cs->matches);
68 if (!match)
69 return NULL;
70
71 size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
72 m = xtables_calloc(1, size);
73 m->u.match_size = size;
74 m->u.user.revision = match->revision;
75
76 strcpy(m->u.user.name, match->name);
77 match->m = m;
78
79 xs_init_match(match);
80
81 if (ctx->h->ops->rule_parse->match)
82 ctx->h->ops->rule_parse->match(match, cs);
83
84 return match->m->data;
85 }
86
87 static void *
__nft_create_target(struct nft_xt_ctx * ctx,const char * name,size_t tgsize)88 __nft_create_target(struct nft_xt_ctx *ctx, const char *name, size_t tgsize)
89 {
90 struct xtables_target *target;
91 size_t size;
92
93 target = xtables_find_target(name, XTF_TRY_LOAD);
94 if (!target)
95 return NULL;
96
97 size = XT_ALIGN(sizeof(*target->t)) + (tgsize ?: target->size);
98
99 target->t = xtables_calloc(1, size);
100 target->t->u.target_size = size;
101 target->t->u.user.revision = target->revision;
102 strcpy(target->t->u.user.name, name);
103
104 xs_init_target(target);
105
106 ctx->cs->jumpto = name;
107 ctx->cs->target = target;
108
109 if (ctx->h->ops->rule_parse->target)
110 ctx->h->ops->rule_parse->target(target, ctx->cs);
111
112 return target->t->data;
113 }
114
115 void *
nft_create_target(struct nft_xt_ctx * ctx,const char * name)116 nft_create_target(struct nft_xt_ctx *ctx, const char *name)
117 {
118 return __nft_create_target(ctx, name, 0);
119 }
120
nft_parse_counter(struct nftnl_expr * e,struct xt_counters * counters)121 static void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters)
122 {
123 counters->pcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_PACKETS);
124 counters->bcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_BYTES);
125 }
126
nft_parse_payload(struct nft_xt_ctx * ctx,struct nftnl_expr * e)127 static void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
128 {
129 enum nft_registers regnum = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_DREG);
130 struct nft_xt_ctx_reg *reg = nft_xt_ctx_get_dreg(ctx, regnum);
131
132 if (!reg)
133 return;
134
135 reg->type = NFT_XT_REG_PAYLOAD;
136 reg->payload.base = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE);
137 reg->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET);
138 reg->payload.len = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN);
139 }
140
nft_parse_meta_set_common(struct nft_xt_ctx * ctx,struct nft_xt_ctx_reg * sreg)141 static bool nft_parse_meta_set_common(struct nft_xt_ctx* ctx,
142 struct nft_xt_ctx_reg *sreg)
143 {
144 if ((sreg->type != NFT_XT_REG_IMMEDIATE)) {
145 ctx->errmsg = "meta sreg is not an immediate";
146 return false;
147 }
148
149 return true;
150 }
151
nft_parse_meta_set(struct nft_xt_ctx * ctx,struct nftnl_expr * e)152 static void nft_parse_meta_set(struct nft_xt_ctx *ctx,
153 struct nftnl_expr *e)
154 {
155 struct nft_xt_ctx_reg *sreg;
156 enum nft_registers sregnum;
157
158 sregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_META_SREG);
159 sreg = nft_xt_ctx_get_sreg(ctx, sregnum);
160 if (!sreg)
161 return;
162
163 switch (nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY)) {
164 case NFT_META_NFTRACE:
165 if (!nft_parse_meta_set_common(ctx, sreg))
166 return;
167
168 if (sreg->immediate.data[0] == 0) {
169 ctx->errmsg = "meta sreg immediate is 0";
170 return;
171 }
172
173 if (!nft_create_target(ctx, "TRACE"))
174 ctx->errmsg = "target TRACE not found";
175 break;
176 case NFT_META_BRI_BROUTE:
177 if (!nft_parse_meta_set_common(ctx, sreg))
178 return;
179
180 ctx->cs->jumpto = "DROP";
181 break;
182 case NFT_META_MARK: {
183 struct xt_mark_tginfo2 *mt;
184
185 if (!nft_parse_meta_set_common(ctx, sreg))
186 return;
187
188 mt = nft_create_target(ctx, "MARK");
189 if (!mt) {
190 ctx->errmsg = "target MARK not found";
191 return;
192 }
193
194 mt->mark = sreg->immediate.data[0];
195 if (sreg->bitwise.set)
196 mt->mask = sreg->bitwise.mask[0];
197 else
198 mt->mask = ~0u;
199 break;
200 }
201 default:
202 ctx->errmsg = "meta sreg key not supported";
203 break;
204 }
205 }
206
nft_parse_meta(struct nft_xt_ctx * ctx,struct nftnl_expr * e)207 static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
208 {
209 struct nft_xt_ctx_reg *reg;
210
211 if (nftnl_expr_is_set(e, NFTNL_EXPR_META_SREG)) {
212 nft_parse_meta_set(ctx, e);
213 return;
214 }
215
216 reg = nft_xt_ctx_get_dreg(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG));
217 if (!reg)
218 return;
219
220 reg->meta_dreg.key = nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY);
221 reg->type = NFT_XT_REG_META_DREG;
222 }
223
nft_parse_bitwise(struct nft_xt_ctx * ctx,struct nftnl_expr * e)224 static void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
225 {
226 enum nft_registers sregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_SREG);
227 enum nft_registers dregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_DREG);
228 struct nft_xt_ctx_reg *sreg = nft_xt_ctx_get_sreg(ctx, sregnum);
229 struct nft_xt_ctx_reg *dreg = sreg;
230 const void *data;
231 uint32_t len;
232
233 if (!sreg)
234 return;
235
236 if (sregnum != dregnum) {
237 dreg = nft_xt_ctx_get_sreg(ctx, dregnum); /* sreg, do NOT clear ... */
238 if (!dreg)
239 return;
240
241 *dreg = *sreg; /* .. and copy content instead */
242 }
243
244 data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_XOR, &len);
245
246 if (len > sizeof(dreg->bitwise.xor)) {
247 ctx->errmsg = "bitwise xor too large";
248 return;
249 }
250
251 memcpy(dreg->bitwise.xor, data, len);
252
253 data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_MASK, &len);
254
255 if (len > sizeof(dreg->bitwise.mask)) {
256 ctx->errmsg = "bitwise mask too large";
257 return;
258 }
259
260 memcpy(dreg->bitwise.mask, data, len);
261
262 dreg->bitwise.set = true;
263 }
264
nft_parse_icmp(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,struct nft_xt_ctx_reg * sreg,uint8_t op,const char * data,size_t dlen)265 static void nft_parse_icmp(struct nft_xt_ctx *ctx,
266 struct iptables_command_state *cs,
267 struct nft_xt_ctx_reg *sreg,
268 uint8_t op, const char *data, size_t dlen)
269 {
270 struct ipt_icmp icmp = {
271 .type = UINT8_MAX,
272 .code = { 0, UINT8_MAX },
273 }, *icmpp;
274
275 if (dlen < 1)
276 goto out_err_len;
277
278 switch (sreg->payload.offset) {
279 case 0:
280 icmp.type = data[0];
281 if (dlen == 1)
282 break;
283 dlen--;
284 data++;
285 /* fall through */
286 case 1:
287 if (dlen > 1)
288 goto out_err_len;
289 icmp.code[0] = icmp.code[1] = data[0];
290 break;
291 default:
292 ctx->errmsg = "unexpected payload offset";
293 return;
294 }
295
296 switch (ctx->h->family) {
297 case NFPROTO_IPV4:
298 icmpp = nft_create_match(ctx, cs, "icmp", false);
299 break;
300 case NFPROTO_IPV6:
301 if (icmp.type == UINT8_MAX) {
302 ctx->errmsg = "icmp6 code with any type match not supported";
303 return;
304 }
305 icmpp = nft_create_match(ctx, cs, "icmp6", false);
306 break;
307 default:
308 ctx->errmsg = "unexpected family for icmp match";
309 return;
310 }
311
312 if (!icmpp) {
313 ctx->errmsg = "icmp match extension not found";
314 return;
315 }
316 memcpy(icmpp, &icmp, sizeof(icmp));
317 return;
318
319 out_err_len:
320 ctx->errmsg = "unexpected RHS data length";
321 }
322
port_match_single_to_range(__u16 * ports,__u8 * invflags,uint8_t op,int port,__u8 invflag)323 static void port_match_single_to_range(__u16 *ports, __u8 *invflags,
324 uint8_t op, int port, __u8 invflag)
325 {
326 if (port < 0)
327 return;
328
329 switch (op) {
330 case NFT_CMP_NEQ:
331 *invflags |= invflag;
332 /* fallthrough */
333 case NFT_CMP_EQ:
334 ports[0] = port;
335 ports[1] = port;
336 break;
337 case NFT_CMP_LT:
338 ports[1] = max(port - 1, 1);
339 break;
340 case NFT_CMP_LTE:
341 ports[1] = port;
342 break;
343 case NFT_CMP_GT:
344 ports[0] = min(port + 1, UINT16_MAX);
345 break;
346 case NFT_CMP_GTE:
347 ports[0] = port;
348 break;
349 }
350 }
351
nft_parse_udp(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,int sport,int dport,uint8_t op)352 static void nft_parse_udp(struct nft_xt_ctx *ctx,
353 struct iptables_command_state *cs,
354 int sport, int dport,
355 uint8_t op)
356 {
357 struct xt_udp *udp = nft_create_match(ctx, cs, "udp", true);
358
359 if (!udp) {
360 ctx->errmsg = "udp match extension not found";
361 return;
362 }
363
364 port_match_single_to_range(udp->spts, &udp->invflags,
365 op, sport, XT_UDP_INV_SRCPT);
366 port_match_single_to_range(udp->dpts, &udp->invflags,
367 op, dport, XT_UDP_INV_DSTPT);
368 }
369
nft_parse_tcp(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,int sport,int dport,uint8_t op)370 static void nft_parse_tcp(struct nft_xt_ctx *ctx,
371 struct iptables_command_state *cs,
372 int sport, int dport,
373 uint8_t op)
374 {
375 struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
376
377 if (!tcp) {
378 ctx->errmsg = "tcp match extension not found";
379 return;
380 }
381
382 port_match_single_to_range(tcp->spts, &tcp->invflags,
383 op, sport, XT_TCP_INV_SRCPT);
384 port_match_single_to_range(tcp->dpts, &tcp->invflags,
385 op, dport, XT_TCP_INV_DSTPT);
386 }
387
nft_parse_th_port(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,uint8_t proto,int sport,int dport,uint8_t op)388 static void nft_parse_th_port(struct nft_xt_ctx *ctx,
389 struct iptables_command_state *cs,
390 uint8_t proto,
391 int sport, int dport, uint8_t op)
392 {
393 switch (proto) {
394 case IPPROTO_UDP:
395 nft_parse_udp(ctx, cs, sport, dport, op);
396 break;
397 case IPPROTO_TCP:
398 nft_parse_tcp(ctx, cs, sport, dport, op);
399 break;
400 default:
401 ctx->errmsg = "unknown layer 4 protocol for TH match";
402 }
403 }
404
nft_parse_tcp_flags(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,uint8_t op,uint8_t flags,uint8_t mask)405 static void nft_parse_tcp_flags(struct nft_xt_ctx *ctx,
406 struct iptables_command_state *cs,
407 uint8_t op, uint8_t flags, uint8_t mask)
408 {
409 struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
410
411 if (!tcp) {
412 ctx->errmsg = "tcp match extension not found";
413 return;
414 }
415
416 if (op == NFT_CMP_NEQ)
417 tcp->invflags |= XT_TCP_INV_FLAGS;
418 tcp->flg_cmp = flags;
419 tcp->flg_mask = mask;
420 }
421
nft_parse_transport(struct nft_xt_ctx * ctx,struct nftnl_expr * e,struct iptables_command_state * cs)422 static void nft_parse_transport(struct nft_xt_ctx *ctx,
423 struct nftnl_expr *e,
424 struct iptables_command_state *cs)
425 {
426 struct nft_xt_ctx_reg *sreg;
427 enum nft_registers reg;
428 uint32_t sdport;
429 uint16_t port;
430 uint8_t proto, op;
431 unsigned int len;
432
433 switch (ctx->h->family) {
434 case NFPROTO_IPV4:
435 proto = ctx->cs->fw.ip.proto;
436 break;
437 case NFPROTO_IPV6:
438 proto = ctx->cs->fw6.ipv6.proto;
439 break;
440 default:
441 ctx->errmsg = "invalid family for TH match";
442 return;
443 }
444
445 nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
446 op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
447
448 reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
449 sreg = nft_xt_ctx_get_sreg(ctx, reg);
450 if (!sreg)
451 return;
452
453 if (sreg->type != NFT_XT_REG_PAYLOAD) {
454 ctx->errmsg = "sgreg not payload";
455 return;
456 }
457
458 switch (proto) {
459 case IPPROTO_UDP:
460 case IPPROTO_TCP:
461 break;
462 case IPPROTO_ICMP:
463 case IPPROTO_ICMPV6:
464 nft_parse_icmp(ctx, cs, sreg, op,
465 nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len),
466 len);
467 return;
468 default:
469 ctx->errmsg = "unsupported layer 4 protocol value";
470 return;
471 }
472
473 switch(sreg->payload.offset) {
474 case 0: /* th->sport */
475 switch (len) {
476 case 2: /* load sport only */
477 port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
478 nft_parse_th_port(ctx, cs, proto, port, -1, op);
479 return;
480 case 4: /* load both src and dst port */
481 sdport = ntohl(nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA));
482 nft_parse_th_port(ctx, cs, proto, sdport >> 16, sdport & 0xffff, op);
483 return;
484 }
485 break;
486 case 2: /* th->dport */
487 switch (len) {
488 case 2:
489 port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
490 nft_parse_th_port(ctx, cs, proto, -1, port, op);
491 return;
492 }
493 break;
494 case 13: /* th->flags */
495 if (len == 1 && proto == IPPROTO_TCP) {
496 uint8_t flags = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
497 uint8_t mask = ~0;
498
499 if (sreg->bitwise.set)
500 memcpy(&mask, &sreg->bitwise.mask, sizeof(mask));
501
502 nft_parse_tcp_flags(ctx, cs, op, flags, mask);
503 }
504 return;
505 }
506 }
507
nft_parse_cmp(struct nft_xt_ctx * ctx,struct nftnl_expr * e)508 static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
509 {
510 struct nft_xt_ctx_reg *sreg;
511 uint32_t reg;
512
513 reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
514
515 sreg = nft_xt_ctx_get_sreg(ctx, reg);
516 if (!sreg)
517 return;
518
519 switch (sreg->type) {
520 case NFT_XT_REG_UNDEF:
521 ctx->errmsg = "cmp sreg undef";
522 break;
523 case NFT_XT_REG_META_DREG:
524 ctx->h->ops->rule_parse->meta(ctx, sreg, e, ctx->cs);
525 break;
526 case NFT_XT_REG_PAYLOAD:
527 switch (sreg->payload.base) {
528 case NFT_PAYLOAD_LL_HEADER:
529 if (ctx->h->family == NFPROTO_BRIDGE)
530 ctx->h->ops->rule_parse->payload(ctx, sreg, e, ctx->cs);
531 break;
532 case NFT_PAYLOAD_NETWORK_HEADER:
533 ctx->h->ops->rule_parse->payload(ctx, sreg, e, ctx->cs);
534 break;
535 case NFT_PAYLOAD_TRANSPORT_HEADER:
536 nft_parse_transport(ctx, e, ctx->cs);
537 break;
538 }
539
540 break;
541 default:
542 ctx->errmsg = "cmp sreg has unknown type";
543 break;
544 }
545 }
546
nft_parse_immediate(struct nft_xt_ctx * ctx,struct nftnl_expr * e)547 static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
548 {
549 const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN);
550 struct iptables_command_state *cs = ctx->cs;
551 int verdict;
552
553 if (nftnl_expr_is_set(e, NFTNL_EXPR_IMM_DATA)) {
554 struct nft_xt_ctx_reg *dreg;
555 const void *imm_data;
556 uint32_t len;
557
558 imm_data = nftnl_expr_get(e, NFTNL_EXPR_IMM_DATA, &len);
559 dreg = nft_xt_ctx_get_dreg(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_DREG));
560 if (!dreg)
561 return;
562
563 if (len > sizeof(dreg->immediate.data)) {
564 ctx->errmsg = "oversized immediate data";
565 return;
566 }
567
568 memcpy(dreg->immediate.data, imm_data, len);
569 dreg->immediate.len = len;
570 dreg->type = NFT_XT_REG_IMMEDIATE;
571
572 return;
573 }
574
575 verdict = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_VERDICT);
576 /* Standard target? */
577 switch(verdict) {
578 case NF_ACCEPT:
579 if (cs->jumpto && strcmp(ctx->table, "broute") == 0)
580 break;
581 cs->jumpto = "ACCEPT";
582 break;
583 case NF_DROP:
584 cs->jumpto = "DROP";
585 break;
586 case NFT_RETURN:
587 cs->jumpto = "RETURN";
588 break;;
589 case NFT_GOTO:
590 if (ctx->h->ops->set_goto_flag)
591 ctx->h->ops->set_goto_flag(cs);
592 /* fall through */
593 case NFT_JUMP:
594 cs->jumpto = chain;
595 /* fall through */
596 default:
597 return;
598 }
599
600 if (!nft_create_target(ctx, cs->jumpto))
601 ctx->errmsg = "verdict extension not found";
602 }
603
nft_parse_match(struct nft_xt_ctx * ctx,struct nftnl_expr * e)604 static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
605 {
606 uint32_t mt_len;
607 const char *mt_name = nftnl_expr_get_str(e, NFTNL_EXPR_MT_NAME);
608 const void *mt_info = nftnl_expr_get(e, NFTNL_EXPR_MT_INFO, &mt_len);
609 struct xtables_match *match;
610 struct xtables_rule_match **matches;
611 struct xt_entry_match *m;
612
613 switch (ctx->h->family) {
614 case NFPROTO_IPV4:
615 case NFPROTO_IPV6:
616 case NFPROTO_BRIDGE:
617 matches = &ctx->cs->matches;
618 break;
619 default:
620 fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
621 ctx->h->family);
622 exit(EXIT_FAILURE);
623 }
624
625 match = xtables_find_match(mt_name, XTF_TRY_LOAD, matches);
626 if (match == NULL) {
627 ctx->errmsg = "match extension not found";
628 return;
629 }
630
631 m = xtables_calloc(1, sizeof(struct xt_entry_match) + mt_len);
632 memcpy(&m->data, mt_info, mt_len);
633 m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
634 m->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
635 strcpy(m->u.user.name, match->name);
636
637 match->m = m;
638
639 if (ctx->h->ops->rule_parse->match != NULL)
640 ctx->h->ops->rule_parse->match(match, ctx->cs);
641 }
642
nft_parse_target(struct nft_xt_ctx * ctx,struct nftnl_expr * e)643 static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
644 {
645 uint32_t tg_len;
646 const char *targname = nftnl_expr_get_str(e, NFTNL_EXPR_TG_NAME);
647 const void *targinfo = nftnl_expr_get(e, NFTNL_EXPR_TG_INFO, &tg_len);
648 void *data;
649
650 data = __nft_create_target(ctx, targname, tg_len);
651 if (!data)
652 ctx->errmsg = "target extension not found";
653 else
654 memcpy(data, targinfo, tg_len);
655 }
656
nft_parse_limit(struct nft_xt_ctx * ctx,struct nftnl_expr * e)657 static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
658 {
659 __u32 burst = nftnl_expr_get_u32(e, NFTNL_EXPR_LIMIT_BURST);
660 __u64 unit = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_UNIT);
661 __u64 rate = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_RATE);
662 struct xt_rateinfo *rinfo;
663
664 switch (ctx->h->family) {
665 case NFPROTO_IPV4:
666 case NFPROTO_IPV6:
667 case NFPROTO_BRIDGE:
668 break;
669 default:
670 fprintf(stderr, "BUG: nft_parse_limit() unknown family %d\n",
671 ctx->h->family);
672 exit(EXIT_FAILURE);
673 }
674
675 rinfo = nft_create_match(ctx, ctx->cs, "limit", false);
676 if (!rinfo) {
677 ctx->errmsg = "limit match extension not found";
678 return;
679 }
680
681 rinfo->avg = XT_LIMIT_SCALE * unit / rate;
682 rinfo->burst = burst;
683 }
684
nft_parse_lookup(struct nft_xt_ctx * ctx,struct nft_handle * h,struct nftnl_expr * e)685 static void nft_parse_lookup(struct nft_xt_ctx *ctx, struct nft_handle *h,
686 struct nftnl_expr *e)
687 {
688 if (ctx->h->ops->rule_parse->lookup)
689 ctx->h->ops->rule_parse->lookup(ctx, e);
690 }
691
nft_parse_log(struct nft_xt_ctx * ctx,struct nftnl_expr * e)692 static void nft_parse_log(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
693 {
694 /*
695 * In order to handle the longer log-prefix supported by nft, instead of
696 * using struct xt_nflog_info, we use a struct with a compatible layout, but
697 * a larger buffer for the prefix.
698 */
699 struct xt_nflog_info_nft {
700 __u32 len;
701 __u16 group;
702 __u16 threshold;
703 __u16 flags;
704 __u16 pad;
705 char prefix[NF_LOG_PREFIXLEN];
706 } info = {
707 .group = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_GROUP),
708 .threshold = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_QTHRESHOLD),
709 };
710 void *data;
711
712 if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_SNAPLEN)) {
713 info.len = nftnl_expr_get_u32(e, NFTNL_EXPR_LOG_SNAPLEN);
714 info.flags = XT_NFLOG_F_COPY_LEN;
715 }
716 if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_PREFIX))
717 snprintf(info.prefix, sizeof(info.prefix), "%s",
718 nftnl_expr_get_str(e, NFTNL_EXPR_LOG_PREFIX));
719
720 data = __nft_create_target(ctx, "NFLOG",
721 XT_ALIGN(sizeof(struct xt_nflog_info_nft)));
722 if (!data)
723 ctx->errmsg = "NFLOG target extension not found";
724 else
725 memcpy(data, &info, sizeof(info));
726 }
727
nft_parse_udp_range(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,int sport_from,int sport_to,int dport_from,int dport_to,uint8_t op)728 static void nft_parse_udp_range(struct nft_xt_ctx *ctx,
729 struct iptables_command_state *cs,
730 int sport_from, int sport_to,
731 int dport_from, int dport_to,
732 uint8_t op)
733 {
734 struct xt_udp *udp = nft_create_match(ctx, cs, "udp", true);
735
736 if (!udp) {
737 ctx->errmsg = "udp match extension not found";
738 return;
739 }
740
741 if (sport_from >= 0) {
742 switch (op) {
743 case NFT_RANGE_NEQ:
744 udp->invflags |= XT_UDP_INV_SRCPT;
745 /* fallthrough */
746 case NFT_RANGE_EQ:
747 udp->spts[0] = sport_from;
748 udp->spts[1] = sport_to;
749 break;
750 }
751 }
752
753 if (dport_to >= 0) {
754 switch (op) {
755 case NFT_CMP_NEQ:
756 udp->invflags |= XT_UDP_INV_DSTPT;
757 /* fallthrough */
758 case NFT_CMP_EQ:
759 udp->dpts[0] = dport_from;
760 udp->dpts[1] = dport_to;
761 break;
762 }
763 }
764 }
765
nft_parse_tcp_range(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,int sport_from,int sport_to,int dport_from,int dport_to,uint8_t op)766 static void nft_parse_tcp_range(struct nft_xt_ctx *ctx,
767 struct iptables_command_state *cs,
768 int sport_from, int sport_to,
769 int dport_from, int dport_to,
770 uint8_t op)
771 {
772 struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
773
774 if (!tcp) {
775 ctx->errmsg = "tcp match extension not found";
776 return;
777 }
778
779 if (sport_from >= 0) {
780 switch (op) {
781 case NFT_RANGE_NEQ:
782 tcp->invflags |= XT_TCP_INV_SRCPT;
783 /* fallthrough */
784 case NFT_RANGE_EQ:
785 tcp->spts[0] = sport_from;
786 tcp->spts[1] = sport_to;
787 break;
788 }
789 }
790
791 if (dport_to >= 0) {
792 switch (op) {
793 case NFT_CMP_NEQ:
794 tcp->invflags |= XT_TCP_INV_DSTPT;
795 /* fallthrough */
796 case NFT_CMP_EQ:
797 tcp->dpts[0] = dport_from;
798 tcp->dpts[1] = dport_to;
799 break;
800 }
801 }
802 }
803
nft_parse_th_port_range(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,uint8_t proto,int sport_from,int sport_to,int dport_from,int dport_to,uint8_t op)804 static void nft_parse_th_port_range(struct nft_xt_ctx *ctx,
805 struct iptables_command_state *cs,
806 uint8_t proto,
807 int sport_from, int sport_to,
808 int dport_from, int dport_to, uint8_t op)
809 {
810 switch (proto) {
811 case IPPROTO_UDP:
812 nft_parse_udp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
813 break;
814 case IPPROTO_TCP:
815 nft_parse_tcp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
816 break;
817 }
818 }
819
nft_parse_transport_range(struct nft_xt_ctx * ctx,const struct nft_xt_ctx_reg * sreg,struct nftnl_expr * e,struct iptables_command_state * cs)820 static void nft_parse_transport_range(struct nft_xt_ctx *ctx,
821 const struct nft_xt_ctx_reg *sreg,
822 struct nftnl_expr *e,
823 struct iptables_command_state *cs)
824 {
825 unsigned int len_from, len_to;
826 uint8_t proto, op;
827 uint16_t from, to;
828
829 switch (ctx->h->family) {
830 case NFPROTO_IPV4:
831 proto = ctx->cs->fw.ip.proto;
832 break;
833 case NFPROTO_IPV6:
834 proto = ctx->cs->fw6.ipv6.proto;
835 break;
836 default:
837 proto = 0;
838 break;
839 }
840
841 nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_from);
842 nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_to);
843 if (len_to != len_from || len_to != 2)
844 return;
845
846 op = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_OP);
847
848 from = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_FROM_DATA));
849 to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
850
851 switch (sreg->payload.offset) {
852 case 0:
853 nft_parse_th_port_range(ctx, cs, proto, from, to, -1, -1, op);
854 return;
855 case 2:
856 to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
857 nft_parse_th_port_range(ctx, cs, proto, -1, -1, from, to, op);
858 return;
859 }
860 }
861
nft_parse_range(struct nft_xt_ctx * ctx,struct nftnl_expr * e)862 static void nft_parse_range(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
863 {
864 struct nft_xt_ctx_reg *sreg;
865 uint32_t reg;
866
867 reg = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_SREG);
868 sreg = nft_xt_ctx_get_sreg(ctx, reg);
869
870 switch (sreg->type) {
871 case NFT_XT_REG_UNDEF:
872 ctx->errmsg = "range sreg undef";
873 break;
874 case NFT_XT_REG_PAYLOAD:
875 switch (sreg->payload.base) {
876 case NFT_PAYLOAD_TRANSPORT_HEADER:
877 nft_parse_transport_range(ctx, sreg, e, ctx->cs);
878 break;
879 default:
880 ctx->errmsg = "range with unknown payload base";
881 break;
882 }
883 break;
884 default:
885 ctx->errmsg = "range sreg type unsupported";
886 break;
887 }
888 }
889
nft_rule_to_iptables_command_state(struct nft_handle * h,const struct nftnl_rule * r,struct iptables_command_state * cs)890 bool nft_rule_to_iptables_command_state(struct nft_handle *h,
891 const struct nftnl_rule *r,
892 struct iptables_command_state *cs)
893 {
894 struct nftnl_expr *expr;
895 struct nft_xt_ctx ctx = {
896 .cs = cs,
897 .h = h,
898 .table = nftnl_rule_get_str(r, NFTNL_RULE_TABLE),
899 };
900 bool ret = true;
901
902 ctx.iter = nftnl_expr_iter_create(r);
903 if (ctx.iter == NULL)
904 return false;
905
906 expr = nftnl_expr_iter_next(ctx.iter);
907 while (expr != NULL) {
908 const char *name =
909 nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
910
911 if (strcmp(name, "counter") == 0)
912 nft_parse_counter(expr, &ctx.cs->counters);
913 else if (strcmp(name, "payload") == 0)
914 nft_parse_payload(&ctx, expr);
915 else if (strcmp(name, "meta") == 0)
916 nft_parse_meta(&ctx, expr);
917 else if (strcmp(name, "bitwise") == 0)
918 nft_parse_bitwise(&ctx, expr);
919 else if (strcmp(name, "cmp") == 0)
920 nft_parse_cmp(&ctx, expr);
921 else if (strcmp(name, "immediate") == 0)
922 nft_parse_immediate(&ctx, expr);
923 else if (strcmp(name, "match") == 0)
924 nft_parse_match(&ctx, expr);
925 else if (strcmp(name, "target") == 0)
926 nft_parse_target(&ctx, expr);
927 else if (strcmp(name, "limit") == 0)
928 nft_parse_limit(&ctx, expr);
929 else if (strcmp(name, "lookup") == 0)
930 nft_parse_lookup(&ctx, h, expr);
931 else if (strcmp(name, "log") == 0)
932 nft_parse_log(&ctx, expr);
933 else if (strcmp(name, "range") == 0)
934 nft_parse_range(&ctx, expr);
935
936 if (ctx.errmsg) {
937 fprintf(stderr, "Error: %s\n", ctx.errmsg);
938 ctx.errmsg = NULL;
939 ret = false;
940 }
941
942 expr = nftnl_expr_iter_next(ctx.iter);
943 }
944
945 nftnl_expr_iter_destroy(ctx.iter);
946
947 if (nftnl_rule_is_set(r, NFTNL_RULE_USERDATA)) {
948 const void *data;
949 uint32_t len, size;
950 const char *comment;
951
952 data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len);
953 comment = get_comment(data, len);
954 if (comment) {
955 struct xtables_match *match;
956 struct xt_entry_match *m;
957
958 match = xtables_find_match("comment", XTF_TRY_LOAD,
959 &cs->matches);
960 if (match == NULL)
961 return false;
962
963 size = XT_ALIGN(sizeof(struct xt_entry_match))
964 + match->size;
965 m = xtables_calloc(1, size);
966
967 strncpy((char *)m->data, comment, match->size - 1);
968 m->u.match_size = size;
969 m->u.user.revision = 0;
970 strcpy(m->u.user.name, match->name);
971
972 match->m = m;
973 }
974 }
975
976 if (!cs->jumpto)
977 cs->jumpto = "";
978
979 if (!ret)
980 xtables_error(VERSION_PROBLEM, "Parsing nftables rule failed");
981 return ret;
982 }
983
parse_ifname(const char * name,unsigned int len,char * dst)984 static void parse_ifname(const char *name, unsigned int len, char *dst)
985 {
986 if (len == 0)
987 return;
988
989 memcpy(dst, name, len);
990 if (name[len - 1] == '\0')
991 return;
992
993 if (len >= IFNAMSIZ)
994 return;
995
996 /* wildcard */
997 dst[len++] = '+';
998 if (len >= IFNAMSIZ)
999 return;
1000 dst[len++] = 0;
1001 }
1002
parse_invalid_iface(char * iface,uint8_t * invflags,uint8_t invbit)1003 static void parse_invalid_iface(char *iface, uint8_t *invflags, uint8_t invbit)
1004 {
1005 if (*invflags & invbit || strcmp(iface, "INVAL/D"))
1006 return;
1007
1008 /* nft's poor "! -o +" excuse */
1009 *invflags |= invbit;
1010 iface[0] = '+';
1011 iface[1] = '\0';
1012 }
1013
get_meta_mask(struct nft_xt_ctx * ctx,enum nft_registers sreg)1014 static uint32_t get_meta_mask(struct nft_xt_ctx *ctx, enum nft_registers sreg)
1015 {
1016 struct nft_xt_ctx_reg *reg = nft_xt_ctx_get_sreg(ctx, sreg);
1017
1018 if (reg->bitwise.set)
1019 return reg->bitwise.mask[0];
1020
1021 return ~0u;
1022 }
1023
parse_meta_mark(struct nft_xt_ctx * ctx,struct nftnl_expr * e)1024 static int parse_meta_mark(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
1025 {
1026 struct xt_mark_mtinfo1 *mark;
1027 uint32_t value;
1028
1029 mark = nft_create_match(ctx, ctx->cs, "mark", false);
1030 if (!mark)
1031 return -1;
1032
1033 if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
1034 mark->invert = 1;
1035
1036 value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
1037 mark->mark = value;
1038 mark->mask = get_meta_mask(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG));
1039
1040 return 0;
1041 }
1042
parse_meta_pkttype(struct nft_xt_ctx * ctx,struct nftnl_expr * e)1043 static int parse_meta_pkttype(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
1044 {
1045 struct xt_pkttype_info *pkttype;
1046 uint8_t value;
1047
1048 pkttype = nft_create_match(ctx, ctx->cs, "pkttype", false);
1049 if (!pkttype)
1050 return -1;
1051
1052 if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
1053 pkttype->invert = 1;
1054
1055 value = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
1056 pkttype->pkttype = value;
1057
1058 return 0;
1059 }
1060
parse_meta(struct nft_xt_ctx * ctx,struct nftnl_expr * e,uint8_t key,char * iniface,char * outiface,uint8_t * invflags)1061 int parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, uint8_t key,
1062 char *iniface, char *outiface, uint8_t *invflags)
1063 {
1064 uint32_t value;
1065 const void *ifname;
1066 uint32_t len;
1067
1068 switch(key) {
1069 case NFT_META_IIF:
1070 value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
1071 if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
1072 *invflags |= IPT_INV_VIA_IN;
1073
1074 if_indextoname(value, iniface);
1075 break;
1076 case NFT_META_OIF:
1077 value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
1078 if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
1079 *invflags |= IPT_INV_VIA_OUT;
1080
1081 if_indextoname(value, outiface);
1082 break;
1083 case NFT_META_BRI_IIFNAME:
1084 case NFT_META_IIFNAME:
1085 ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
1086 if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
1087 *invflags |= IPT_INV_VIA_IN;
1088
1089 parse_ifname(ifname, len, iniface);
1090 parse_invalid_iface(iniface, invflags, IPT_INV_VIA_IN);
1091 break;
1092 case NFT_META_BRI_OIFNAME:
1093 case NFT_META_OIFNAME:
1094 ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
1095 if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
1096 *invflags |= IPT_INV_VIA_OUT;
1097
1098 parse_ifname(ifname, len, outiface);
1099 parse_invalid_iface(outiface, invflags, IPT_INV_VIA_OUT);
1100 break;
1101 case NFT_META_MARK:
1102 parse_meta_mark(ctx, e);
1103 break;
1104 case NFT_META_PKTTYPE:
1105 parse_meta_pkttype(ctx, e);
1106 break;
1107 default:
1108 return -1;
1109 }
1110
1111 return 0;
1112 }
1113
nft_parse_hl(struct nft_xt_ctx * ctx,struct nftnl_expr * e,struct iptables_command_state * cs)1114 int nft_parse_hl(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
1115 struct iptables_command_state *cs)
1116 {
1117 struct ip6t_hl_info *info;
1118 uint8_t hl, mode;
1119 int op;
1120
1121 hl = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
1122 op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
1123
1124 switch (op) {
1125 case NFT_CMP_NEQ:
1126 mode = IP6T_HL_NE;
1127 break;
1128 case NFT_CMP_EQ:
1129 mode = IP6T_HL_EQ;
1130 break;
1131 case NFT_CMP_LT:
1132 mode = IP6T_HL_LT;
1133 break;
1134 case NFT_CMP_GT:
1135 mode = IP6T_HL_GT;
1136 break;
1137 case NFT_CMP_LTE:
1138 mode = IP6T_HL_LT;
1139 if (hl == 255)
1140 return -1;
1141 hl++;
1142 break;
1143 case NFT_CMP_GTE:
1144 mode = IP6T_HL_GT;
1145 if (hl == 0)
1146 return -1;
1147 hl--;
1148 break;
1149 default:
1150 return -1;
1151 }
1152
1153 /* ipt_ttl_info and ip6t_hl_info have same layout,
1154 * IPT_TTL_x and IP6T_HL_x are aliases as well, so
1155 * just use HL for both ipv4 and ipv6.
1156 */
1157 switch (ctx->h->family) {
1158 case NFPROTO_IPV4:
1159 info = nft_create_match(ctx, ctx->cs, "ttl", false);
1160 break;
1161 case NFPROTO_IPV6:
1162 info = nft_create_match(ctx, ctx->cs, "hl", false);
1163 break;
1164 default:
1165 return -1;
1166 }
1167
1168 if (!info)
1169 return -1;
1170
1171 info->hop_limit = hl;
1172 info->mode = mode;
1173
1174 return 0;
1175 }
1176