]>
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); | |
28 | u32 mask; | |
29 | ||
30 | mask = ~0U >> (sizeof(priv->data) * BITS_PER_BYTE - priv->len); | |
31 | if ((data[priv->sreg].data[0] & mask) == priv->data) | |
32 | return; | |
33 | data[NFT_REG_VERDICT].verdict = NFT_BREAK; | |
34 | } | |
35 | ||
c29b72e0 PM |
36 | static bool nft_payload_fast_eval(const struct nft_expr *expr, |
37 | struct nft_data data[NFT_REG_MAX + 1], | |
38 | const struct nft_pktinfo *pkt) | |
39 | { | |
40 | const struct nft_payload *priv = nft_expr_priv(expr); | |
41 | const struct sk_buff *skb = pkt->skb; | |
42 | struct nft_data *dest = &data[priv->dreg]; | |
43 | unsigned char *ptr; | |
44 | ||
45 | if (priv->base == NFT_PAYLOAD_NETWORK_HEADER) | |
46 | ptr = skb_network_header(skb); | |
47 | else | |
c54032e0 | 48 | ptr = skb_network_header(skb) + pkt->xt.thoff; |
c29b72e0 PM |
49 | |
50 | ptr += priv->offset; | |
51 | ||
52 | if (unlikely(ptr + priv->len >= skb_tail_pointer(skb))) | |
53 | return false; | |
54 | ||
55 | if (priv->len == 2) | |
56 | *(u16 *)dest->data = *(u16 *)ptr; | |
57 | else if (priv->len == 4) | |
58 | *(u32 *)dest->data = *(u32 *)ptr; | |
59 | else | |
60 | *(u8 *)dest->data = *(u8 *)ptr; | |
61 | return true; | |
62 | } | |
63 | ||
0ca743a5 PNA |
64 | struct nft_jumpstack { |
65 | const struct nft_chain *chain; | |
66 | const struct nft_rule *rule; | |
b5bc89bf | 67 | int rulenum; |
0ca743a5 PNA |
68 | }; |
69 | ||
70 | static inline void | |
71 | nft_chain_stats(const struct nft_chain *this, const struct nft_pktinfo *pkt, | |
72 | struct nft_jumpstack *jumpstack, unsigned int stackptr) | |
73 | { | |
74 | struct nft_stats __percpu *stats; | |
75 | const struct nft_chain *chain = stackptr ? jumpstack[0].chain : this; | |
76 | ||
77 | rcu_read_lock_bh(); | |
78 | stats = rcu_dereference(nft_base_chain(chain)->stats); | |
79 | __this_cpu_inc(stats->pkts); | |
80 | __this_cpu_add(stats->bytes, pkt->skb->len); | |
81 | rcu_read_unlock_bh(); | |
82 | } | |
83 | ||
b5bc89bf PNA |
84 | enum nft_trace { |
85 | NFT_TRACE_RULE, | |
86 | NFT_TRACE_RETURN, | |
87 | NFT_TRACE_POLICY, | |
88 | }; | |
89 | ||
90 | static const char *const comments[] = { | |
91 | [NFT_TRACE_RULE] = "rule", | |
92 | [NFT_TRACE_RETURN] = "return", | |
93 | [NFT_TRACE_POLICY] = "policy", | |
94 | }; | |
95 | ||
96 | static struct nf_loginfo trace_loginfo = { | |
97 | .type = NF_LOG_TYPE_LOG, | |
98 | .u = { | |
99 | .log = { | |
100 | .level = 4, | |
101 | .logflags = NF_LOG_MASK, | |
102 | }, | |
103 | }, | |
104 | }; | |
105 | ||
106 | static inline void nft_trace_packet(const struct nft_pktinfo *pkt, | |
107 | const struct nft_chain *chain, | |
108 | int rulenum, enum nft_trace type) | |
109 | { | |
110 | struct net *net = dev_net(pkt->in ? pkt->in : pkt->out); | |
111 | ||
c9484874 | 112 | nf_log_packet(net, pkt->xt.family, pkt->ops->hooknum, pkt->skb, pkt->in, |
b5bc89bf PNA |
113 | pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ", |
114 | chain->table->name, chain->name, comments[type], | |
115 | rulenum); | |
116 | } | |
117 | ||
0ca743a5 PNA |
118 | unsigned int |
119 | nft_do_chain_pktinfo(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) | |
96518518 PM |
120 | { |
121 | const struct nft_chain *chain = ops->priv; | |
122 | const struct nft_rule *rule; | |
123 | const struct nft_expr *expr, *last; | |
124 | struct nft_data data[NFT_REG_MAX + 1]; | |
96518518 | 125 | unsigned int stackptr = 0; |
0ca743a5 | 126 | struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE]; |
b5bc89bf | 127 | int rulenum = 0; |
0628b123 PNA |
128 | /* |
129 | * Cache cursor to avoid problems in case that the cursor is updated | |
130 | * while traversing the ruleset. | |
131 | */ | |
132 | unsigned int gencursor = ACCESS_ONCE(chain->net->nft.gencursor); | |
96518518 PM |
133 | |
134 | do_chain: | |
135 | rule = list_entry(&chain->rules, struct nft_rule, list); | |
136 | next_rule: | |
137 | data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; | |
138 | list_for_each_entry_continue_rcu(rule, &chain->rules, list) { | |
0628b123 PNA |
139 | |
140 | /* This rule is not active, skip. */ | |
141 | if (unlikely(rule->genmask & (1 << gencursor))) | |
142 | continue; | |
143 | ||
b5bc89bf PNA |
144 | rulenum++; |
145 | ||
96518518 | 146 | nft_rule_for_each_expr(expr, last, rule) { |
cb7dbfd0 PM |
147 | if (expr->ops == &nft_cmp_fast_ops) |
148 | nft_cmp_fast_eval(expr, data); | |
c29b72e0 | 149 | else if (expr->ops != &nft_payload_fast_ops || |
0ca743a5 PNA |
150 | !nft_payload_fast_eval(expr, data, pkt)) |
151 | expr->ops->eval(expr, data, pkt); | |
cb7dbfd0 | 152 | |
96518518 PM |
153 | if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE) |
154 | break; | |
155 | } | |
156 | ||
157 | switch (data[NFT_REG_VERDICT].verdict) { | |
158 | case NFT_BREAK: | |
159 | data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; | |
160 | /* fall through */ | |
161 | case NFT_CONTINUE: | |
162 | continue; | |
163 | } | |
164 | break; | |
165 | } | |
166 | ||
e569bdab | 167 | switch (data[NFT_REG_VERDICT].verdict & NF_VERDICT_MASK) { |
96518518 PM |
168 | case NF_ACCEPT: |
169 | case NF_DROP: | |
170 | case NF_QUEUE: | |
b5bc89bf PNA |
171 | if (unlikely(pkt->skb->nf_trace)) |
172 | nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); | |
173 | ||
96518518 | 174 | return data[NFT_REG_VERDICT].verdict; |
e569bdab EL |
175 | } |
176 | ||
177 | switch (data[NFT_REG_VERDICT].verdict) { | |
96518518 | 178 | case NFT_JUMP: |
b5bc89bf PNA |
179 | if (unlikely(pkt->skb->nf_trace)) |
180 | nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); | |
181 | ||
96518518 PM |
182 | BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE); |
183 | jumpstack[stackptr].chain = chain; | |
184 | jumpstack[stackptr].rule = rule; | |
b5bc89bf | 185 | jumpstack[stackptr].rulenum = rulenum; |
96518518 PM |
186 | stackptr++; |
187 | /* fall through */ | |
188 | case NFT_GOTO: | |
189 | chain = data[NFT_REG_VERDICT].chain; | |
190 | goto do_chain; | |
191 | case NFT_RETURN: | |
b5bc89bf PNA |
192 | if (unlikely(pkt->skb->nf_trace)) |
193 | nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN); | |
194 | ||
195 | /* fall through */ | |
96518518 PM |
196 | case NFT_CONTINUE: |
197 | break; | |
198 | default: | |
199 | WARN_ON(1); | |
200 | } | |
201 | ||
202 | if (stackptr > 0) { | |
b5bc89bf PNA |
203 | if (unlikely(pkt->skb->nf_trace)) |
204 | nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_RETURN); | |
205 | ||
96518518 PM |
206 | stackptr--; |
207 | chain = jumpstack[stackptr].chain; | |
208 | rule = jumpstack[stackptr].rule; | |
b5bc89bf | 209 | rulenum = jumpstack[stackptr].rulenum; |
96518518 PM |
210 | goto next_rule; |
211 | } | |
0ca743a5 | 212 | nft_chain_stats(chain, pkt, jumpstack, stackptr); |
96518518 | 213 | |
b5bc89bf PNA |
214 | if (unlikely(pkt->skb->nf_trace)) |
215 | nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_POLICY); | |
216 | ||
0ca743a5 | 217 | return nft_base_chain(chain)->policy; |
96518518 | 218 | } |
0ca743a5 | 219 | EXPORT_SYMBOL_GPL(nft_do_chain_pktinfo); |
96518518 PM |
220 | |
221 | int __init nf_tables_core_module_init(void) | |
222 | { | |
223 | int err; | |
224 | ||
225 | err = nft_immediate_module_init(); | |
226 | if (err < 0) | |
227 | goto err1; | |
228 | ||
229 | err = nft_cmp_module_init(); | |
230 | if (err < 0) | |
231 | goto err2; | |
232 | ||
233 | err = nft_lookup_module_init(); | |
234 | if (err < 0) | |
235 | goto err3; | |
236 | ||
237 | err = nft_bitwise_module_init(); | |
238 | if (err < 0) | |
239 | goto err4; | |
240 | ||
241 | err = nft_byteorder_module_init(); | |
242 | if (err < 0) | |
243 | goto err5; | |
244 | ||
245 | err = nft_payload_module_init(); | |
246 | if (err < 0) | |
247 | goto err6; | |
248 | ||
249 | return 0; | |
250 | ||
251 | err6: | |
252 | nft_byteorder_module_exit(); | |
253 | err5: | |
254 | nft_bitwise_module_exit(); | |
255 | err4: | |
256 | nft_lookup_module_exit(); | |
257 | err3: | |
258 | nft_cmp_module_exit(); | |
259 | err2: | |
260 | nft_immediate_module_exit(); | |
261 | err1: | |
262 | return err; | |
263 | } | |
264 | ||
265 | void nf_tables_core_module_exit(void) | |
266 | { | |
267 | nft_payload_module_exit(); | |
268 | nft_byteorder_module_exit(); | |
269 | nft_bitwise_module_exit(); | |
270 | nft_lookup_module_exit(); | |
271 | nft_cmp_module_exit(); | |
272 | nft_immediate_module_exit(); | |
273 | } |