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