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