1 /*
2 * (C) 2012-2014 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 <string.h>
14 #include <stdio.h>
15
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19 #include <netinet/ip.h>
20 #include <netdb.h>
21
22 #include <xtables.h>
23
24 #include <linux/netfilter/nf_tables.h>
25
26 #include "nft.h"
27 #include "nft-shared.h"
28
nft_ipv4_add(struct nftnl_rule * r,void * data)29 static int nft_ipv4_add(struct nftnl_rule *r, void *data)
30 {
31 struct iptables_command_state *cs = data;
32 struct xtables_rule_match *matchp;
33 uint32_t op;
34 int ret;
35
36 if (cs->fw.ip.iniface[0] != '\0') {
37 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_IN);
38 add_iniface(r, cs->fw.ip.iniface, op);
39 }
40
41 if (cs->fw.ip.outiface[0] != '\0') {
42 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_OUT);
43 add_outiface(r, cs->fw.ip.outiface, op);
44 }
45
46 if (cs->fw.ip.proto != 0) {
47 op = nft_invflags2cmp(cs->fw.ip.invflags, XT_INV_PROTO);
48 add_proto(r, offsetof(struct iphdr, protocol), 1,
49 cs->fw.ip.proto, op);
50 }
51
52 if (cs->fw.ip.src.s_addr != 0) {
53 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_SRCIP);
54 add_addr(r, offsetof(struct iphdr, saddr),
55 &cs->fw.ip.src.s_addr, &cs->fw.ip.smsk.s_addr,
56 sizeof(struct in_addr), op);
57 }
58 if (cs->fw.ip.dst.s_addr != 0) {
59 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_DSTIP);
60 add_addr(r, offsetof(struct iphdr, daddr),
61 &cs->fw.ip.dst.s_addr, &cs->fw.ip.dmsk.s_addr,
62 sizeof(struct in_addr), op);
63 }
64 if (cs->fw.ip.flags & IPT_F_FRAG) {
65 add_payload(r, offsetof(struct iphdr, frag_off), 2,
66 NFT_PAYLOAD_NETWORK_HEADER);
67 /* get the 13 bits that contain the fragment offset */
68 add_bitwise_u16(r, 0x1fff, !0x1fff);
69
70 /* if offset is non-zero, this is a fragment */
71 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_FRAG);
72 add_cmp_u16(r, 0, op);
73 }
74
75 add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags);
76
77 for (matchp = cs->matches; matchp; matchp = matchp->next) {
78 /* Use nft built-in comments support instead of comment match */
79 if (strcmp(matchp->match->name, "comment") == 0) {
80 ret = add_comment(r, (char *)matchp->match->m->data);
81 if (ret < 0)
82 return ret;
83 } else {
84 ret = add_match(r, matchp->match->m);
85 if (ret < 0)
86 return ret;
87 }
88 }
89
90 /* Counters need to me added before the target, otherwise they are
91 * increased for each rule because of the way nf_tables works.
92 */
93 if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0)
94 return -1;
95
96 return add_action(r, cs, !!(cs->fw.ip.flags & IPT_F_GOTO));
97 }
98
nft_ipv4_is_same(const void * data_a,const void * data_b)99 static bool nft_ipv4_is_same(const void *data_a,
100 const void *data_b)
101 {
102 const struct iptables_command_state *a = data_a;
103 const struct iptables_command_state *b = data_b;
104
105 if (a->fw.ip.src.s_addr != b->fw.ip.src.s_addr
106 || a->fw.ip.dst.s_addr != b->fw.ip.dst.s_addr
107 || a->fw.ip.smsk.s_addr != b->fw.ip.smsk.s_addr
108 || a->fw.ip.dmsk.s_addr != b->fw.ip.dmsk.s_addr
109 || a->fw.ip.proto != b->fw.ip.proto
110 || a->fw.ip.flags != b->fw.ip.flags
111 || a->fw.ip.invflags != b->fw.ip.invflags) {
112 DEBUGP("different src/dst/proto/flags/invflags\n");
113 return false;
114 }
115
116 return is_same_interfaces(a->fw.ip.iniface, a->fw.ip.outiface,
117 a->fw.ip.iniface_mask, a->fw.ip.outiface_mask,
118 b->fw.ip.iniface, b->fw.ip.outiface,
119 b->fw.ip.iniface_mask, b->fw.ip.outiface_mask);
120 }
121
get_frag(struct nft_xt_ctx * ctx,struct nftnl_expr * e,bool * inv)122 static void get_frag(struct nft_xt_ctx *ctx, struct nftnl_expr *e, bool *inv)
123 {
124 uint8_t op;
125
126 /* we assume correct mask and xor */
127 if (!(ctx->flags & NFT_XT_CTX_BITWISE))
128 return;
129
130 /* we assume correct data */
131 op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
132 if (op == NFT_CMP_EQ)
133 *inv = true;
134 else
135 *inv = false;
136
137 ctx->flags &= ~NFT_XT_CTX_BITWISE;
138 }
139
mask_to_str(uint32_t mask)140 static const char *mask_to_str(uint32_t mask)
141 {
142 static char mask_str[sizeof("255.255.255.255")];
143 uint32_t bits, hmask = ntohl(mask);
144 struct in_addr mask_addr = {
145 .s_addr = mask,
146 };
147 int i;
148
149 if (mask == 0xFFFFFFFFU) {
150 sprintf(mask_str, "32");
151 return mask_str;
152 }
153
154 i = 32;
155 bits = 0xFFFFFFFEU;
156 while (--i >= 0 && hmask != bits)
157 bits <<= 1;
158 if (i >= 0)
159 sprintf(mask_str, "%u", i);
160 else
161 sprintf(mask_str, "%s", inet_ntoa(mask_addr));
162
163 return mask_str;
164 }
165
nft_ipv4_parse_meta(struct nft_xt_ctx * ctx,struct nftnl_expr * e,void * data)166 static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
167 void *data)
168 {
169 struct iptables_command_state *cs = data;
170
171 parse_meta(e, ctx->meta.key, cs->fw.ip.iniface, cs->fw.ip.iniface_mask,
172 cs->fw.ip.outiface, cs->fw.ip.outiface_mask,
173 &cs->fw.ip.invflags);
174 }
175
parse_mask_ipv4(struct nft_xt_ctx * ctx,struct in_addr * mask)176 static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask)
177 {
178 mask->s_addr = ctx->bitwise.mask[0];
179 }
180
nft_ipv4_parse_payload(struct nft_xt_ctx * ctx,struct nftnl_expr * e,void * data)181 static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx,
182 struct nftnl_expr *e, void *data)
183 {
184 struct iptables_command_state *cs = data;
185 struct in_addr addr;
186 uint8_t proto;
187 bool inv;
188
189 switch(ctx->payload.offset) {
190 case offsetof(struct iphdr, saddr):
191 get_cmp_data(e, &addr, sizeof(addr), &inv);
192 cs->fw.ip.src.s_addr = addr.s_addr;
193 if (ctx->flags & NFT_XT_CTX_BITWISE) {
194 parse_mask_ipv4(ctx, &cs->fw.ip.smsk);
195 ctx->flags &= ~NFT_XT_CTX_BITWISE;
196 } else {
197 cs->fw.ip.smsk.s_addr = 0xffffffff;
198 }
199
200 if (inv)
201 cs->fw.ip.invflags |= IPT_INV_SRCIP;
202 break;
203 case offsetof(struct iphdr, daddr):
204 get_cmp_data(e, &addr, sizeof(addr), &inv);
205 cs->fw.ip.dst.s_addr = addr.s_addr;
206 if (ctx->flags & NFT_XT_CTX_BITWISE) {
207 parse_mask_ipv4(ctx, &cs->fw.ip.dmsk);
208 ctx->flags &= ~NFT_XT_CTX_BITWISE;
209 } else {
210 cs->fw.ip.dmsk.s_addr = 0xffffffff;
211 }
212
213 if (inv)
214 cs->fw.ip.invflags |= IPT_INV_DSTIP;
215 break;
216 case offsetof(struct iphdr, protocol):
217 get_cmp_data(e, &proto, sizeof(proto), &inv);
218 cs->fw.ip.proto = proto;
219 if (inv)
220 cs->fw.ip.invflags |= IPT_INV_PROTO;
221 break;
222 case offsetof(struct iphdr, frag_off):
223 cs->fw.ip.flags |= IPT_F_FRAG;
224 get_frag(ctx, e, &inv);
225 if (inv)
226 cs->fw.ip.invflags |= IPT_INV_FRAG;
227 break;
228 default:
229 DEBUGP("unknown payload offset %d\n", ctx->payload.offset);
230 break;
231 }
232 }
233
nft_ipv4_parse_immediate(const char * jumpto,bool nft_goto,void * data)234 static void nft_ipv4_parse_immediate(const char *jumpto, bool nft_goto,
235 void *data)
236 {
237 struct iptables_command_state *cs = data;
238
239 cs->jumpto = jumpto;
240
241 if (nft_goto)
242 cs->fw.ip.flags |= IPT_F_GOTO;
243 }
244
nft_ipv4_print_header(unsigned int format,const char * chain,const char * pol,const struct xt_counters * counters,bool basechain,uint32_t refs)245 static void nft_ipv4_print_header(unsigned int format, const char *chain,
246 const char *pol,
247 const struct xt_counters *counters,
248 bool basechain, uint32_t refs)
249 {
250 print_header(format, chain, pol, counters, basechain, refs);
251 }
252
print_ipv4_addr(const struct iptables_command_state * cs,unsigned int format)253 static void print_ipv4_addr(const struct iptables_command_state *cs,
254 unsigned int format)
255 {
256 char buf[BUFSIZ];
257
258 fputc(cs->fw.ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
259 if (cs->fw.ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))
260 printf(FMT("%-19s ","%s "), "anywhere");
261 else {
262 if (format & FMT_NUMERIC)
263 strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.src));
264 else
265 strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.src));
266 strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.smsk));
267 printf(FMT("%-19s ","%s "), buf);
268 }
269
270 fputc(cs->fw.ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
271 if (cs->fw.ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
272 printf(FMT("%-19s ","-> %s"), "anywhere");
273 else {
274 if (format & FMT_NUMERIC)
275 strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.dst));
276 else
277 strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.dst));
278 strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.dmsk));
279 printf(FMT("%-19s ","-> %s"), buf);
280 }
281 }
282
print_fragment(unsigned int flags,unsigned int invflags,unsigned int format)283 static void print_fragment(unsigned int flags, unsigned int invflags,
284 unsigned int format)
285 {
286 if (!(format & FMT_OPTIONS))
287 return;
288
289 if (format & FMT_NOTABLE)
290 fputs("opt ", stdout);
291 fputc(invflags & IPT_INV_FRAG ? '!' : '-', stdout);
292 fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);
293 fputc(' ', stdout);
294 }
295
nft_ipv4_print_firewall(struct nftnl_rule * r,unsigned int num,unsigned int format)296 static void nft_ipv4_print_firewall(struct nftnl_rule *r, unsigned int num,
297 unsigned int format)
298 {
299 struct iptables_command_state cs = {};
300
301 nft_rule_to_iptables_command_state(r, &cs);
302
303 print_firewall_details(&cs, cs.jumpto, cs.fw.ip.flags,
304 cs.fw.ip.invflags, cs.fw.ip.proto,
305 num, format);
306 print_fragment(cs.fw.ip.flags, cs.fw.ip.invflags, format);
307 print_ifaces(cs.fw.ip.iniface, cs.fw.ip.outiface, cs.fw.ip.invflags,
308 format);
309 print_ipv4_addr(&cs, format);
310
311 if (format & FMT_NOTABLE)
312 fputs(" ", stdout);
313
314 #ifdef IPT_F_GOTO
315 if (cs.fw.ip.flags & IPT_F_GOTO)
316 printf("[goto] ");
317 #endif
318
319 print_matches_and_target(&cs, format);
320
321 if (!(format & FMT_NONEWLINE))
322 fputc('\n', stdout);
323 }
324
save_ipv4_addr(char letter,const struct in_addr * addr,uint32_t mask,int invert)325 static void save_ipv4_addr(char letter, const struct in_addr *addr,
326 uint32_t mask, int invert)
327 {
328 if (!mask && !invert && !addr->s_addr)
329 return;
330
331 printf("%s-%c %s/%s ", invert ? "! " : "", letter, inet_ntoa(*addr),
332 mask_to_str(mask));
333 }
334
nft_ipv4_save_firewall(const void * data,unsigned int format)335 static void nft_ipv4_save_firewall(const void *data, unsigned int format)
336 {
337 const struct iptables_command_state *cs = data;
338
339 save_firewall_details(cs, cs->fw.ip.invflags, cs->fw.ip.proto,
340 cs->fw.ip.iniface, cs->fw.ip.iniface_mask,
341 cs->fw.ip.outiface, cs->fw.ip.outiface_mask);
342
343 if (cs->fw.ip.flags & IPT_F_FRAG) {
344 if (cs->fw.ip.invflags & IPT_INV_FRAG)
345 printf("! ");
346 printf("-f ");
347 }
348
349 save_ipv4_addr('s', &cs->fw.ip.src, cs->fw.ip.smsk.s_addr,
350 cs->fw.ip.invflags & IPT_INV_SRCIP);
351 save_ipv4_addr('d', &cs->fw.ip.dst, cs->fw.ip.dmsk.s_addr,
352 cs->fw.ip.invflags & IPT_INV_DSTIP);
353
354 save_matches_and_target(cs->matches, cs->target,
355 cs->jumpto, cs->fw.ip.flags, &cs->fw);
356
357 if (cs->target == NULL && strlen(cs->jumpto) > 0) {
358 printf("-%c %s", cs->fw.ip.flags & IPT_F_GOTO ? 'g' : 'j',
359 cs->jumpto);
360 }
361 printf("\n");
362 }
363
nft_ipv4_proto_parse(struct iptables_command_state * cs,struct xtables_args * args)364 static void nft_ipv4_proto_parse(struct iptables_command_state *cs,
365 struct xtables_args *args)
366 {
367 cs->fw.ip.proto = args->proto;
368 cs->fw.ip.invflags = args->invflags;
369 }
370
nft_ipv4_post_parse(int command,struct iptables_command_state * cs,struct xtables_args * args)371 static void nft_ipv4_post_parse(int command,
372 struct iptables_command_state *cs,
373 struct xtables_args *args)
374 {
375 cs->fw.ip.flags = args->flags;
376 /* We already set invflags in proto_parse, but we need to refresh it
377 * to include new parsed options.
378 */
379 cs->fw.ip.invflags = args->invflags;
380
381 strncpy(cs->fw.ip.iniface, args->iniface, IFNAMSIZ);
382 memcpy(cs->fw.ip.iniface_mask,
383 args->iniface_mask, IFNAMSIZ*sizeof(unsigned char));
384
385 strncpy(cs->fw.ip.outiface, args->outiface, IFNAMSIZ);
386 memcpy(cs->fw.ip.outiface_mask,
387 args->outiface_mask, IFNAMSIZ*sizeof(unsigned char));
388
389 if (args->goto_set)
390 cs->fw.ip.flags |= IPT_F_GOTO;
391
392 cs->counters.pcnt = args->pcnt_cnt;
393 cs->counters.bcnt = args->bcnt_cnt;
394
395 if (command & (CMD_REPLACE | CMD_INSERT |
396 CMD_DELETE | CMD_APPEND | CMD_CHECK)) {
397 if (!(cs->options & OPT_DESTINATION))
398 args->dhostnetworkmask = "0.0.0.0/0";
399 if (!(cs->options & OPT_SOURCE))
400 args->shostnetworkmask = "0.0.0.0/0";
401 }
402
403 if (args->shostnetworkmask)
404 xtables_ipparse_multiple(args->shostnetworkmask,
405 &args->s.addr.v4, &args->s.mask.v4,
406 &args->s.naddrs);
407 if (args->dhostnetworkmask)
408 xtables_ipparse_multiple(args->dhostnetworkmask,
409 &args->d.addr.v4, &args->d.mask.v4,
410 &args->d.naddrs);
411
412 if ((args->s.naddrs > 1 || args->d.naddrs > 1) &&
413 (cs->fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
414 xtables_error(PARAMETER_PROBLEM,
415 "! not allowed with multiple"
416 " source or destination IP addresses");
417 }
418
nft_ipv4_parse_target(struct xtables_target * t,void * data)419 static void nft_ipv4_parse_target(struct xtables_target *t, void *data)
420 {
421 struct iptables_command_state *cs = data;
422
423 cs->target = t;
424 }
425
nft_ipv4_rule_find(struct nft_family_ops * ops,struct nftnl_rule * r,void * data)426 static bool nft_ipv4_rule_find(struct nft_family_ops *ops,
427 struct nftnl_rule *r, void *data)
428 {
429 struct iptables_command_state *cs = data;
430
431 return nft_ipv46_rule_find(ops, r, cs);
432 }
433
nft_ipv4_save_counters(const void * data)434 static void nft_ipv4_save_counters(const void *data)
435 {
436 const struct iptables_command_state *cs = data;
437
438 save_counters(cs->counters.pcnt, cs->counters.bcnt);
439 }
440
nft_ipv4_xlate(const void * data,struct xt_xlate * xl)441 static int nft_ipv4_xlate(const void *data, struct xt_xlate *xl)
442 {
443 const struct iptables_command_state *cs = data;
444 const char *comment;
445 int ret;
446
447 xlate_ifname(xl, "iifname", cs->fw.ip.iniface,
448 cs->fw.ip.invflags & IPT_INV_VIA_IN);
449 xlate_ifname(xl, "oifname", cs->fw.ip.outiface,
450 cs->fw.ip.invflags & IPT_INV_VIA_OUT);
451
452 if (cs->fw.ip.flags & IPT_F_FRAG) {
453 xt_xlate_add(xl, "ip frag-off %s%x ",
454 cs->fw.ip.invflags & IPT_INV_FRAG? "" : "!= ", 0);
455 }
456
457 if (cs->fw.ip.proto != 0) {
458 const struct protoent *pent =
459 getprotobynumber(cs->fw.ip.proto);
460 char protonum[strlen("255") + 1];
461
462 if (!xlate_find_match(cs, pent->p_name)) {
463 snprintf(protonum, sizeof(protonum), "%u",
464 cs->fw.ip.proto);
465 protonum[sizeof(protonum) - 1] = '\0';
466 xt_xlate_add(xl, "ip protocol %s%s ",
467 cs->fw.ip.invflags & IPT_INV_PROTO ?
468 "!= " : "",
469 pent ? pent->p_name : protonum);
470 }
471 }
472
473 if (cs->fw.ip.src.s_addr != 0) {
474 xt_xlate_add(xl, "ip saddr %s%s%s ",
475 cs->fw.ip.invflags & IPT_INV_SRCIP ? "!= " : "",
476 inet_ntoa(cs->fw.ip.src),
477 xtables_ipmask_to_numeric(&cs->fw.ip.smsk));
478 }
479 if (cs->fw.ip.dst.s_addr != 0) {
480 xt_xlate_add(xl, "ip daddr %s%s%s ",
481 cs->fw.ip.invflags & IPT_INV_DSTIP ? "!= " : "",
482 inet_ntoa(cs->fw.ip.dst),
483 xtables_ipmask_to_numeric(&cs->fw.ip.dmsk));
484 }
485
486 ret = xlate_matches(cs, xl);
487 if (!ret)
488 return ret;
489
490 /* Always add counters per rule, as in iptables */
491 xt_xlate_add(xl, "counter ");
492 ret = xlate_action(cs, !!(cs->fw.ip.flags & IPT_F_GOTO), xl);
493
494 comment = xt_xlate_get_comment(xl);
495 if (comment)
496 xt_xlate_add(xl, " comment %s", comment);
497
498 return ret;
499 }
500
501 struct nft_family_ops nft_family_ops_ipv4 = {
502 .add = nft_ipv4_add,
503 .is_same = nft_ipv4_is_same,
504 .parse_meta = nft_ipv4_parse_meta,
505 .parse_payload = nft_ipv4_parse_payload,
506 .parse_immediate = nft_ipv4_parse_immediate,
507 .print_header = nft_ipv4_print_header,
508 .print_firewall = nft_ipv4_print_firewall,
509 .save_firewall = nft_ipv4_save_firewall,
510 .save_counters = nft_ipv4_save_counters,
511 .proto_parse = nft_ipv4_proto_parse,
512 .post_parse = nft_ipv4_post_parse,
513 .parse_target = nft_ipv4_parse_target,
514 .rule_find = nft_ipv4_rule_find,
515 .xlate = nft_ipv4_xlate,
516 };
517