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 <stddef.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <netdb.h>
18 #include <net/if.h>
19 #include <netinet/if_ether.h>
20 #include <netinet/ip.h>
21
22 #include <libnftnl/rule.h>
23 #include <libnftnl/expr.h>
24
25 #include "nft-shared.h"
26 #include "nft-ruleparse.h"
27 #include "xshared.h"
28
nft_ipv4_parse_meta(struct nft_xt_ctx * ctx,const struct nft_xt_ctx_reg * reg,struct nftnl_expr * e,struct iptables_command_state * cs)29 static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx,
30 const struct nft_xt_ctx_reg *reg,
31 struct nftnl_expr *e,
32 struct iptables_command_state *cs)
33 {
34 switch (reg->meta_dreg.key) {
35 case NFT_META_L4PROTO:
36 cs->fw.ip.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
37 if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
38 cs->fw.ip.invflags |= XT_INV_PROTO;
39 return;
40 default:
41 break;
42 }
43
44 if (parse_meta(ctx, e, reg->meta_dreg.key, cs->fw.ip.iniface,
45 cs->fw.ip.outiface, &cs->fw.ip.invflags) == 0)
46 return;
47
48 ctx->errmsg = "unknown ipv4 meta key";
49 }
50
parse_mask_ipv4(const struct nft_xt_ctx_reg * sreg,struct in_addr * mask)51 static void parse_mask_ipv4(const struct nft_xt_ctx_reg *sreg, struct in_addr *mask)
52 {
53 mask->s_addr = sreg->bitwise.mask[0];
54 }
55
get_frag(const struct nft_xt_ctx_reg * reg,struct nftnl_expr * e)56 static bool get_frag(const struct nft_xt_ctx_reg *reg, struct nftnl_expr *e)
57 {
58 uint8_t op;
59
60 /* we assume correct mask and xor */
61 if (!reg->bitwise.set)
62 return false;
63
64 /* we assume correct data */
65 op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
66 if (op == NFT_CMP_EQ)
67 return true;
68
69 return false;
70 }
71
nft_ipv4_parse_payload(struct nft_xt_ctx * ctx,const struct nft_xt_ctx_reg * sreg,struct nftnl_expr * e,struct iptables_command_state * cs)72 static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx,
73 const struct nft_xt_ctx_reg *sreg,
74 struct nftnl_expr *e,
75 struct iptables_command_state *cs)
76 {
77 struct in_addr addr;
78 uint8_t proto;
79 bool inv;
80
81 switch (sreg->payload.offset) {
82 case offsetof(struct iphdr, saddr):
83 get_cmp_data(e, &addr, sizeof(addr), &inv);
84 cs->fw.ip.src.s_addr = addr.s_addr;
85 if (sreg->bitwise.set) {
86 parse_mask_ipv4(sreg, &cs->fw.ip.smsk);
87 } else {
88 memset(&cs->fw.ip.smsk, 0xff,
89 min(sreg->payload.len, sizeof(struct in_addr)));
90 }
91
92 if (inv)
93 cs->fw.ip.invflags |= IPT_INV_SRCIP;
94 break;
95 case offsetof(struct iphdr, daddr):
96 get_cmp_data(e, &addr, sizeof(addr), &inv);
97 cs->fw.ip.dst.s_addr = addr.s_addr;
98 if (sreg->bitwise.set)
99 parse_mask_ipv4(sreg, &cs->fw.ip.dmsk);
100 else
101 memset(&cs->fw.ip.dmsk, 0xff,
102 min(sreg->payload.len, sizeof(struct in_addr)));
103
104 if (inv)
105 cs->fw.ip.invflags |= IPT_INV_DSTIP;
106 break;
107 case offsetof(struct iphdr, protocol):
108 get_cmp_data(e, &proto, sizeof(proto), &inv);
109 cs->fw.ip.proto = proto;
110 if (inv)
111 cs->fw.ip.invflags |= IPT_INV_PROTO;
112 break;
113 case offsetof(struct iphdr, frag_off):
114 cs->fw.ip.flags |= IPT_F_FRAG;
115 inv = get_frag(sreg, e);
116 if (inv)
117 cs->fw.ip.invflags |= IPT_INV_FRAG;
118 break;
119 case offsetof(struct iphdr, ttl):
120 if (nft_parse_hl(ctx, e, cs) < 0)
121 ctx->errmsg = "invalid ttl field match";
122 break;
123 default:
124 DEBUGP("unknown payload offset %d\n", sreg->payload.offset);
125 ctx->errmsg = "unknown payload offset";
126 break;
127 }
128 }
129
130 struct nft_ruleparse_ops nft_ruleparse_ops_ipv4 = {
131 .meta = nft_ipv4_parse_meta,
132 .payload = nft_ipv4_parse_payload,
133 };
134