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_regs *regs)
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 ((regs->data[priv->sreg] & mask) == priv->data)
74 		return;
75 	regs->verdict.code = NFT_BREAK;
76 }
77 
78 static bool nft_payload_fast_eval(const struct nft_expr *expr,
79 				  struct nft_regs *regs,
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 	u32 *dest = &regs->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 	*dest = 0;
98 	if (priv->len == 2)
99 		*(u16 *)dest = *(u16 *)ptr;
100 	else if (priv->len == 4)
101 		*(u32 *)dest = *(u32 *)ptr;
102 	else
103 		*(u8 *)dest = *(u8 *)ptr;
104 	return true;
105 }
106 
107 struct nft_jumpstack {
108 	const struct nft_chain	*chain;
109 	const struct nft_rule	*rule;
110 	int			rulenum;
111 };
112 
113 unsigned int
114 nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops)
115 {
116 	const struct nft_chain *chain = ops->priv, *basechain = chain;
117 	const struct net *chain_net = read_pnet(&nft_base_chain(basechain)->pnet);
118 	const struct net *net = dev_net(pkt->in ? pkt->in : pkt->out);
119 	const struct nft_rule *rule;
120 	const struct nft_expr *expr, *last;
121 	struct nft_regs regs;
122 	unsigned int stackptr = 0;
123 	struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE];
124 	struct nft_stats *stats;
125 	int rulenum;
126 	unsigned int gencursor = nft_genmask_cur(net);
127 
128 	/* Ignore chains that are not for the current network namespace */
129 	if (!net_eq(net, chain_net))
130 		return NF_ACCEPT;
131 
132 do_chain:
133 	rulenum = 0;
134 	rule = list_entry(&chain->rules, struct nft_rule, list);
135 next_rule:
136 	regs.verdict.code = NFT_CONTINUE;
137 	list_for_each_entry_continue_rcu(rule, &chain->rules, list) {
138 
139 		/* This rule is not active, skip. */
140 		if (unlikely(rule->genmask & (1 << gencursor)))
141 			continue;
142 
143 		rulenum++;
144 
145 		nft_rule_for_each_expr(expr, last, rule) {
146 			if (expr->ops == &nft_cmp_fast_ops)
147 				nft_cmp_fast_eval(expr, &regs);
148 			else if (expr->ops != &nft_payload_fast_ops ||
149 				 !nft_payload_fast_eval(expr, &regs, pkt))
150 				expr->ops->eval(expr, &regs, pkt);
151 
152 			if (regs.verdict.code != NFT_CONTINUE)
153 				break;
154 		}
155 
156 		switch (regs.verdict.code) {
157 		case NFT_BREAK:
158 			regs.verdict.code = NFT_CONTINUE;
159 			continue;
160 		case NFT_CONTINUE:
161 			nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
162 			continue;
163 		}
164 		break;
165 	}
166 
167 	switch (regs.verdict.code & NF_VERDICT_MASK) {
168 	case NF_ACCEPT:
169 	case NF_DROP:
170 	case NF_QUEUE:
171 		nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
172 		return regs.verdict.code;
173 	}
174 
175 	switch (regs.verdict.code) {
176 	case NFT_JUMP:
177 		BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE);
178 		jumpstack[stackptr].chain = chain;
179 		jumpstack[stackptr].rule  = rule;
180 		jumpstack[stackptr].rulenum = rulenum;
181 		stackptr++;
182 		/* fall through */
183 	case NFT_GOTO:
184 		nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
185 
186 		chain = regs.verdict.chain;
187 		goto do_chain;
188 	case NFT_CONTINUE:
189 		rulenum++;
190 		/* fall through */
191 	case NFT_RETURN:
192 		nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN);
193 		break;
194 	default:
195 		WARN_ON(1);
196 	}
197 
198 	if (stackptr > 0) {
199 		stackptr--;
200 		chain = jumpstack[stackptr].chain;
201 		rule  = jumpstack[stackptr].rule;
202 		rulenum = jumpstack[stackptr].rulenum;
203 		goto next_rule;
204 	}
205 
206 	nft_trace_packet(pkt, basechain, -1, NFT_TRACE_POLICY);
207 
208 	rcu_read_lock_bh();
209 	stats = this_cpu_ptr(rcu_dereference(nft_base_chain(basechain)->stats));
210 	u64_stats_update_begin(&stats->syncp);
211 	stats->pkts++;
212 	stats->bytes += pkt->skb->len;
213 	u64_stats_update_end(&stats->syncp);
214 	rcu_read_unlock_bh();
215 
216 	return nft_base_chain(basechain)->policy;
217 }
218 EXPORT_SYMBOL_GPL(nft_do_chain);
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 	err = nft_dynset_module_init();
249 	if (err < 0)
250 		goto err7;
251 
252 	return 0;
253 
254 err7:
255 	nft_payload_module_exit();
256 err6:
257 	nft_byteorder_module_exit();
258 err5:
259 	nft_bitwise_module_exit();
260 err4:
261 	nft_lookup_module_exit();
262 err3:
263 	nft_cmp_module_exit();
264 err2:
265 	nft_immediate_module_exit();
266 err1:
267 	return err;
268 }
269 
270 void nf_tables_core_module_exit(void)
271 {
272 	nft_dynset_module_exit();
273 	nft_payload_module_exit();
274 	nft_byteorder_module_exit();
275 	nft_bitwise_module_exit();
276 	nft_lookup_module_exit();
277 	nft_cmp_module_exit();
278 	nft_immediate_module_exit();
279 }
280