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