1 /*
2 * (C) 2012-2014 by Pablo Neira Ayuso <[email protected]>
3 * (C) 2013 by Tomasz Bursztyka <[email protected]>
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, cs->fw.ip.iniface_mask,
45 cs->fw.ip.outiface, cs->fw.ip.outiface_mask,
46 &cs->fw.ip.invflags) == 0)
47 return;
48
49 ctx->errmsg = "unknown ipv4 meta key";
50 }
51
parse_mask_ipv4(const struct nft_xt_ctx_reg * sreg,struct in_addr * mask)52 static void parse_mask_ipv4(const struct nft_xt_ctx_reg *sreg, struct in_addr *mask)
53 {
54 mask->s_addr = sreg->bitwise.mask[0];
55 }
56
get_frag(const struct nft_xt_ctx_reg * reg,struct nftnl_expr * e)57 static bool get_frag(const struct nft_xt_ctx_reg *reg, struct nftnl_expr *e)
58 {
59 uint8_t op;
60
61 /* we assume correct mask and xor */
62 if (!reg->bitwise.set)
63 return false;
64
65 /* we assume correct data */
66 op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
67 if (op == NFT_CMP_EQ)
68 return true;
69
70 return false;
71 }
72
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)73 static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx,
74 const struct nft_xt_ctx_reg *sreg,
75 struct nftnl_expr *e,
76 struct iptables_command_state *cs)
77 {
78 struct in_addr addr;
79 uint8_t proto;
80 bool inv;
81
82 switch (sreg->payload.offset) {
83 case offsetof(struct iphdr, saddr):
84 get_cmp_data(e, &addr, sizeof(addr), &inv);
85 cs->fw.ip.src.s_addr = addr.s_addr;
86 if (sreg->bitwise.set) {
87 parse_mask_ipv4(sreg, &cs->fw.ip.smsk);
88 } else {
89 memset(&cs->fw.ip.smsk, 0xff,
90 min(sreg->payload.len, sizeof(struct in_addr)));
91 }
92
93 if (inv)
94 cs->fw.ip.invflags |= IPT_INV_SRCIP;
95 break;
96 case offsetof(struct iphdr, daddr):
97 get_cmp_data(e, &addr, sizeof(addr), &inv);
98 cs->fw.ip.dst.s_addr = addr.s_addr;
99 if (sreg->bitwise.set)
100 parse_mask_ipv4(sreg, &cs->fw.ip.dmsk);
101 else
102 memset(&cs->fw.ip.dmsk, 0xff,
103 min(sreg->payload.len, sizeof(struct in_addr)));
104
105 if (inv)
106 cs->fw.ip.invflags |= IPT_INV_DSTIP;
107 break;
108 case offsetof(struct iphdr, protocol):
109 get_cmp_data(e, &proto, sizeof(proto), &inv);
110 cs->fw.ip.proto = proto;
111 if (inv)
112 cs->fw.ip.invflags |= IPT_INV_PROTO;
113 break;
114 case offsetof(struct iphdr, frag_off):
115 cs->fw.ip.flags |= IPT_F_FRAG;
116 inv = get_frag(sreg, e);
117 if (inv)
118 cs->fw.ip.invflags |= IPT_INV_FRAG;
119 break;
120 case offsetof(struct iphdr, ttl):
121 if (nft_parse_hl(ctx, e, cs) < 0)
122 ctx->errmsg = "invalid ttl field match";
123 break;
124 default:
125 DEBUGP("unknown payload offset %d\n", sreg->payload.offset);
126 ctx->errmsg = "unknown payload offset";
127 break;
128 }
129 }
130
131 struct nft_ruleparse_ops nft_ruleparse_ops_ipv4 = {
132 .meta = nft_ipv4_parse_meta,
133 .payload = nft_ipv4_parse_payload,
134 };
135