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