Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetoot...
[linux-block.git] / net / netfilter / nf_tables_core.c
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/kernel.h>
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/list.h>
15 #include <linux/rculist.h>
16 #include <linux/skbuff.h>
17 #include <linux/netlink.h>
18 #include <linux/netfilter.h>
19 #include <linux/netfilter/nfnetlink.h>
20 #include <linux/netfilter/nf_tables.h>
21 #include <net/netfilter/nf_tables_core.h>
22 #include <net/netfilter/nf_tables.h>
23 #include <net/netfilter/nf_log.h>
24
25 enum nft_trace {
26         NFT_TRACE_RULE,
27         NFT_TRACE_RETURN,
28         NFT_TRACE_POLICY,
29 };
30
31 static const char *const comments[] = {
32         [NFT_TRACE_RULE]        = "rule",
33         [NFT_TRACE_RETURN]      = "return",
34         [NFT_TRACE_POLICY]      = "policy",
35 };
36
37 static struct nf_loginfo trace_loginfo = {
38         .type = NF_LOG_TYPE_LOG,
39         .u = {
40                 .log = {
41                         .level = LOGLEVEL_WARNING,
42                         .logflags = NF_LOG_MASK,
43                 },
44         },
45 };
46
47 static void __nft_trace_packet(const struct nft_pktinfo *pkt,
48                                const struct nft_chain *chain,
49                                int rulenum, enum nft_trace type)
50 {
51         struct net *net = dev_net(pkt->in ? pkt->in : pkt->out);
52
53         nf_log_trace(net, pkt->xt.family, pkt->ops->hooknum, pkt->skb, pkt->in,
54                      pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ",
55                      chain->table->name, chain->name, comments[type],
56                      rulenum);
57 }
58
59 static inline void nft_trace_packet(const struct nft_pktinfo *pkt,
60                                     const struct nft_chain *chain,
61                                     int rulenum, enum nft_trace type)
62 {
63         if (unlikely(pkt->skb->nf_trace))
64                 __nft_trace_packet(pkt, chain, rulenum, type);
65 }
66
67 static void nft_cmp_fast_eval(const struct nft_expr *expr,
68                               struct nft_data data[NFT_REG_MAX + 1])
69 {
70         const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
71         u32 mask = nft_cmp_fast_mask(priv->len);
72
73         if ((data[priv->sreg].data[0] & mask) == priv->data)
74                 return;
75         data[NFT_REG_VERDICT].verdict = NFT_BREAK;
76 }
77
78 static bool nft_payload_fast_eval(const struct nft_expr *expr,
79                                   struct nft_data data[NFT_REG_MAX + 1],
80                                   const struct nft_pktinfo *pkt)
81 {
82         const struct nft_payload *priv = nft_expr_priv(expr);
83         const struct sk_buff *skb = pkt->skb;
84         struct nft_data *dest = &data[priv->dreg];
85         unsigned char *ptr;
86
87         if (priv->base == NFT_PAYLOAD_NETWORK_HEADER)
88                 ptr = skb_network_header(skb);
89         else
90                 ptr = skb_network_header(skb) + pkt->xt.thoff;
91
92         ptr += priv->offset;
93
94         if (unlikely(ptr + priv->len >= skb_tail_pointer(skb)))
95                 return false;
96
97         if (priv->len == 2)
98                 *(u16 *)dest->data = *(u16 *)ptr;
99         else if (priv->len == 4)
100                 *(u32 *)dest->data = *(u32 *)ptr;
101         else
102                 *(u8 *)dest->data = *(u8 *)ptr;
103         return true;
104 }
105
106 struct nft_jumpstack {
107         const struct nft_chain  *chain;
108         const struct nft_rule   *rule;
109         int                     rulenum;
110 };
111
112 unsigned int
113 nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops)
114 {
115         const struct nft_chain *chain = ops->priv, *basechain = chain;
116         const struct net *net = read_pnet(&nft_base_chain(basechain)->pnet);
117         const struct nft_rule *rule;
118         const struct nft_expr *expr, *last;
119         struct nft_data data[NFT_REG_MAX + 1];
120         unsigned int stackptr = 0;
121         struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE];
122         struct nft_stats *stats;
123         int rulenum;
124         unsigned int gencursor = nft_genmask_cur(net);
125
126 do_chain:
127         rulenum = 0;
128         rule = list_entry(&chain->rules, struct nft_rule, list);
129 next_rule:
130         data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;
131         list_for_each_entry_continue_rcu(rule, &chain->rules, list) {
132
133                 /* This rule is not active, skip. */
134                 if (unlikely(rule->genmask & (1 << gencursor)))
135                         continue;
136
137                 rulenum++;
138
139                 nft_rule_for_each_expr(expr, last, rule) {
140                         if (expr->ops == &nft_cmp_fast_ops)
141                                 nft_cmp_fast_eval(expr, data);
142                         else if (expr->ops != &nft_payload_fast_ops ||
143                                  !nft_payload_fast_eval(expr, data, pkt))
144                                 expr->ops->eval(expr, data, pkt);
145
146                         if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE)
147                                 break;
148                 }
149
150                 switch (data[NFT_REG_VERDICT].verdict) {
151                 case NFT_BREAK:
152                         data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;
153                         continue;
154                 case NFT_CONTINUE:
155                         nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
156                         continue;
157                 }
158                 break;
159         }
160
161         switch (data[NFT_REG_VERDICT].verdict & NF_VERDICT_MASK) {
162         case NF_ACCEPT:
163         case NF_DROP:
164         case NF_QUEUE:
165                 nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
166                 return data[NFT_REG_VERDICT].verdict;
167         }
168
169         switch (data[NFT_REG_VERDICT].verdict) {
170         case NFT_JUMP:
171                 BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE);
172                 jumpstack[stackptr].chain = chain;
173                 jumpstack[stackptr].rule  = rule;
174                 jumpstack[stackptr].rulenum = rulenum;
175                 stackptr++;
176                 /* fall through */
177         case NFT_GOTO:
178                 nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
179
180                 chain = data[NFT_REG_VERDICT].chain;
181                 goto do_chain;
182         case NFT_CONTINUE:
183                 rulenum++;
184                 /* fall through */
185         case NFT_RETURN:
186                 nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN);
187                 break;
188         default:
189                 WARN_ON(1);
190         }
191
192         if (stackptr > 0) {
193                 stackptr--;
194                 chain = jumpstack[stackptr].chain;
195                 rule  = jumpstack[stackptr].rule;
196                 rulenum = jumpstack[stackptr].rulenum;
197                 goto next_rule;
198         }
199
200         nft_trace_packet(pkt, basechain, -1, NFT_TRACE_POLICY);
201
202         rcu_read_lock_bh();
203         stats = this_cpu_ptr(rcu_dereference(nft_base_chain(basechain)->stats));
204         u64_stats_update_begin(&stats->syncp);
205         stats->pkts++;
206         stats->bytes += pkt->skb->len;
207         u64_stats_update_end(&stats->syncp);
208         rcu_read_unlock_bh();
209
210         return nft_base_chain(basechain)->policy;
211 }
212 EXPORT_SYMBOL_GPL(nft_do_chain);
213
214 int __init nf_tables_core_module_init(void)
215 {
216         int err;
217
218         err = nft_immediate_module_init();
219         if (err < 0)
220                 goto err1;
221
222         err = nft_cmp_module_init();
223         if (err < 0)
224                 goto err2;
225
226         err = nft_lookup_module_init();
227         if (err < 0)
228                 goto err3;
229
230         err = nft_bitwise_module_init();
231         if (err < 0)
232                 goto err4;
233
234         err = nft_byteorder_module_init();
235         if (err < 0)
236                 goto err5;
237
238         err = nft_payload_module_init();
239         if (err < 0)
240                 goto err6;
241
242         err = nft_dynset_module_init();
243         if (err < 0)
244                 goto err7;
245
246         return 0;
247
248 err7:
249         nft_payload_module_exit();
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_dynset_module_exit();
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 }