Commit | Line | Data |
---|---|---|
96518518 PM |
1 | /* |
2 | * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | * | |
8 | * Development of this code funded by Astaro AG (http://www.astaro.com/) | |
9 | */ | |
10 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/list.h> | |
14 | #include <linux/rculist.h> | |
15 | #include <linux/skbuff.h> | |
16 | #include <linux/netlink.h> | |
17 | #include <linux/netfilter.h> | |
18 | #include <linux/netfilter/nfnetlink.h> | |
19 | #include <linux/netfilter/nf_tables.h> | |
20 | #include <net/netfilter/nf_tables_core.h> | |
21 | #include <net/netfilter/nf_tables.h> | |
b5bc89bf | 22 | #include <net/netfilter/nf_log.h> |
96518518 | 23 | |
cb7dbfd0 PM |
24 | static void nft_cmp_fast_eval(const struct nft_expr *expr, |
25 | struct nft_data data[NFT_REG_MAX + 1]) | |
26 | { | |
27 | const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); | |
b855d416 | 28 | u32 mask = nft_cmp_fast_mask(priv->len); |
cb7dbfd0 | 29 | |
cb7dbfd0 PM |
30 | if ((data[priv->sreg].data[0] & mask) == priv->data) |
31 | return; | |
32 | data[NFT_REG_VERDICT].verdict = NFT_BREAK; | |
33 | } | |
34 | ||
c29b72e0 PM |
35 | static bool nft_payload_fast_eval(const struct nft_expr *expr, |
36 | struct nft_data data[NFT_REG_MAX + 1], | |
37 | const struct nft_pktinfo *pkt) | |
38 | { | |
39 | const struct nft_payload *priv = nft_expr_priv(expr); | |
40 | const struct sk_buff *skb = pkt->skb; | |
41 | struct nft_data *dest = &data[priv->dreg]; | |
42 | unsigned char *ptr; | |
43 | ||
44 | if (priv->base == NFT_PAYLOAD_NETWORK_HEADER) | |
45 | ptr = skb_network_header(skb); | |
46 | else | |
c54032e0 | 47 | ptr = skb_network_header(skb) + pkt->xt.thoff; |
c29b72e0 PM |
48 | |
49 | ptr += priv->offset; | |
50 | ||
51 | if (unlikely(ptr + priv->len >= skb_tail_pointer(skb))) | |
52 | return false; | |
53 | ||
54 | if (priv->len == 2) | |
55 | *(u16 *)dest->data = *(u16 *)ptr; | |
56 | else if (priv->len == 4) | |
57 | *(u32 *)dest->data = *(u32 *)ptr; | |
58 | else | |
59 | *(u8 *)dest->data = *(u8 *)ptr; | |
60 | return true; | |
61 | } | |
62 | ||
0ca743a5 PNA |
63 | struct nft_jumpstack { |
64 | const struct nft_chain *chain; | |
65 | const struct nft_rule *rule; | |
b5bc89bf | 66 | int rulenum; |
0ca743a5 PNA |
67 | }; |
68 | ||
b5bc89bf PNA |
69 | enum nft_trace { |
70 | NFT_TRACE_RULE, | |
71 | NFT_TRACE_RETURN, | |
72 | NFT_TRACE_POLICY, | |
73 | }; | |
74 | ||
75 | static const char *const comments[] = { | |
76 | [NFT_TRACE_RULE] = "rule", | |
77 | [NFT_TRACE_RETURN] = "return", | |
78 | [NFT_TRACE_POLICY] = "policy", | |
79 | }; | |
80 | ||
81 | static struct nf_loginfo trace_loginfo = { | |
82 | .type = NF_LOG_TYPE_LOG, | |
83 | .u = { | |
84 | .log = { | |
85 | .level = 4, | |
86 | .logflags = NF_LOG_MASK, | |
87 | }, | |
88 | }, | |
89 | }; | |
90 | ||
6d8c00d5 PM |
91 | static void nft_trace_packet(const struct nft_pktinfo *pkt, |
92 | const struct nft_chain *chain, | |
93 | int rulenum, enum nft_trace type) | |
b5bc89bf PNA |
94 | { |
95 | struct net *net = dev_net(pkt->in ? pkt->in : pkt->out); | |
96 | ||
c9484874 | 97 | nf_log_packet(net, pkt->xt.family, pkt->ops->hooknum, pkt->skb, pkt->in, |
b5bc89bf PNA |
98 | pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ", |
99 | chain->table->name, chain->name, comments[type], | |
100 | rulenum); | |
101 | } | |
102 | ||
0ca743a5 | 103 | unsigned int |
3876d22d | 104 | nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) |
96518518 | 105 | { |
5467a512 | 106 | const struct nft_chain *chain = ops->priv, *basechain = chain; |
96518518 PM |
107 | const struct nft_rule *rule; |
108 | const struct nft_expr *expr, *last; | |
109 | struct nft_data data[NFT_REG_MAX + 1]; | |
96518518 | 110 | unsigned int stackptr = 0; |
0ca743a5 | 111 | struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE]; |
ce355e20 | 112 | struct nft_stats *stats; |
d088be80 | 113 | int rulenum; |
0628b123 PNA |
114 | /* |
115 | * Cache cursor to avoid problems in case that the cursor is updated | |
116 | * while traversing the ruleset. | |
117 | */ | |
118 | unsigned int gencursor = ACCESS_ONCE(chain->net->nft.gencursor); | |
96518518 PM |
119 | |
120 | do_chain: | |
d088be80 | 121 | rulenum = 0; |
96518518 PM |
122 | rule = list_entry(&chain->rules, struct nft_rule, list); |
123 | next_rule: | |
124 | data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; | |
125 | list_for_each_entry_continue_rcu(rule, &chain->rules, list) { | |
0628b123 PNA |
126 | |
127 | /* This rule is not active, skip. */ | |
128 | if (unlikely(rule->genmask & (1 << gencursor))) | |
129 | continue; | |
130 | ||
b5bc89bf PNA |
131 | rulenum++; |
132 | ||
96518518 | 133 | nft_rule_for_each_expr(expr, last, rule) { |
cb7dbfd0 PM |
134 | if (expr->ops == &nft_cmp_fast_ops) |
135 | nft_cmp_fast_eval(expr, data); | |
c29b72e0 | 136 | else if (expr->ops != &nft_payload_fast_ops || |
0ca743a5 PNA |
137 | !nft_payload_fast_eval(expr, data, pkt)) |
138 | expr->ops->eval(expr, data, pkt); | |
cb7dbfd0 | 139 | |
96518518 PM |
140 | if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE) |
141 | break; | |
142 | } | |
143 | ||
144 | switch (data[NFT_REG_VERDICT].verdict) { | |
145 | case NFT_BREAK: | |
146 | data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; | |
3b084e99 | 147 | continue; |
96518518 | 148 | case NFT_CONTINUE: |
3b084e99 PNA |
149 | if (unlikely(pkt->skb->nf_trace)) |
150 | nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); | |
96518518 PM |
151 | continue; |
152 | } | |
153 | break; | |
154 | } | |
155 | ||
e569bdab | 156 | switch (data[NFT_REG_VERDICT].verdict & NF_VERDICT_MASK) { |
96518518 PM |
157 | case NF_ACCEPT: |
158 | case NF_DROP: | |
159 | case NF_QUEUE: | |
b5bc89bf PNA |
160 | if (unlikely(pkt->skb->nf_trace)) |
161 | nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); | |
162 | ||
96518518 | 163 | return data[NFT_REG_VERDICT].verdict; |
e569bdab EL |
164 | } |
165 | ||
166 | switch (data[NFT_REG_VERDICT].verdict) { | |
96518518 | 167 | case NFT_JUMP: |
b5bc89bf PNA |
168 | if (unlikely(pkt->skb->nf_trace)) |
169 | nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); | |
170 | ||
96518518 PM |
171 | BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE); |
172 | jumpstack[stackptr].chain = chain; | |
173 | jumpstack[stackptr].rule = rule; | |
b5bc89bf | 174 | jumpstack[stackptr].rulenum = rulenum; |
96518518 | 175 | stackptr++; |
7b9d5ef9 PNA |
176 | chain = data[NFT_REG_VERDICT].chain; |
177 | goto do_chain; | |
96518518 | 178 | case NFT_GOTO: |
7b9d5ef9 PNA |
179 | if (unlikely(pkt->skb->nf_trace)) |
180 | nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); | |
181 | ||
96518518 PM |
182 | chain = data[NFT_REG_VERDICT].chain; |
183 | goto do_chain; | |
184 | case NFT_RETURN: | |
b5bc89bf PNA |
185 | if (unlikely(pkt->skb->nf_trace)) |
186 | nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN); | |
7e9bc10d | 187 | break; |
96518518 | 188 | case NFT_CONTINUE: |
7e9bc10d PNA |
189 | if (unlikely(pkt->skb->nf_trace && !(chain->flags & NFT_BASE_CHAIN))) |
190 | nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_RETURN); | |
96518518 PM |
191 | break; |
192 | default: | |
193 | WARN_ON(1); | |
194 | } | |
195 | ||
196 | if (stackptr > 0) { | |
197 | stackptr--; | |
198 | chain = jumpstack[stackptr].chain; | |
199 | rule = jumpstack[stackptr].rule; | |
b5bc89bf | 200 | rulenum = jumpstack[stackptr].rulenum; |
96518518 PM |
201 | goto next_rule; |
202 | } | |
203 | ||
b5bc89bf | 204 | if (unlikely(pkt->skb->nf_trace)) |
f7e7e39b | 205 | nft_trace_packet(pkt, basechain, -1, NFT_TRACE_POLICY); |
5467a512 PNA |
206 | |
207 | rcu_read_lock_bh(); | |
ce355e20 ED |
208 | stats = this_cpu_ptr(rcu_dereference(nft_base_chain(basechain)->stats)); |
209 | u64_stats_update_begin(&stats->syncp); | |
210 | stats->pkts++; | |
211 | stats->bytes += pkt->skb->len; | |
212 | u64_stats_update_end(&stats->syncp); | |
5467a512 | 213 | rcu_read_unlock_bh(); |
b5bc89bf | 214 | |
5467a512 | 215 | return nft_base_chain(basechain)->policy; |
96518518 | 216 | } |
3876d22d | 217 | EXPORT_SYMBOL_GPL(nft_do_chain); |
96518518 PM |
218 | |
219 | int __init nf_tables_core_module_init(void) | |
220 | { | |
221 | int err; | |
222 | ||
223 | err = nft_immediate_module_init(); | |
224 | if (err < 0) | |
225 | goto err1; | |
226 | ||
227 | err = nft_cmp_module_init(); | |
228 | if (err < 0) | |
229 | goto err2; | |
230 | ||
231 | err = nft_lookup_module_init(); | |
232 | if (err < 0) | |
233 | goto err3; | |
234 | ||
235 | err = nft_bitwise_module_init(); | |
236 | if (err < 0) | |
237 | goto err4; | |
238 | ||
239 | err = nft_byteorder_module_init(); | |
240 | if (err < 0) | |
241 | goto err5; | |
242 | ||
243 | err = nft_payload_module_init(); | |
244 | if (err < 0) | |
245 | goto err6; | |
246 | ||
247 | return 0; | |
248 | ||
249 | err6: | |
250 | nft_byteorder_module_exit(); | |
251 | err5: | |
252 | nft_bitwise_module_exit(); | |
253 | err4: | |
254 | nft_lookup_module_exit(); | |
255 | err3: | |
256 | nft_cmp_module_exit(); | |
257 | err2: | |
258 | nft_immediate_module_exit(); | |
259 | err1: | |
260 | return err; | |
261 | } | |
262 | ||
263 | void nf_tables_core_module_exit(void) | |
264 | { | |
265 | nft_payload_module_exit(); | |
266 | nft_byteorder_module_exit(); | |
267 | nft_bitwise_module_exit(); | |
268 | nft_lookup_module_exit(); | |
269 | nft_cmp_module_exit(); | |
270 | nft_immediate_module_exit(); | |
271 | } |