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
24static 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
35static 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
63struct nft_jumpstack {
64	const struct nft_chain	*chain;
65	const struct nft_rule	*rule;
66	int			rulenum;
67};
68
69enum nft_trace {
70	NFT_TRACE_RULE,
71	NFT_TRACE_RETURN,
72	NFT_TRACE_POLICY,
73};
74
75static const char *const comments[] = {
76	[NFT_TRACE_RULE]	= "rule",
77	[NFT_TRACE_RETURN]	= "return",
78	[NFT_TRACE_POLICY]	= "policy",
79};
80
81static 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
91static 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
103unsigned int
104nft_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
120do_chain:
121	rulenum = 0;
122	rule = list_entry(&chain->rules, struct nft_rule, list);
123next_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}
217EXPORT_SYMBOL_GPL(nft_do_chain);
218
219int __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
249err6:
250	nft_byteorder_module_exit();
251err5:
252	nft_bitwise_module_exit();
253err4:
254	nft_lookup_module_exit();
255err3:
256	nft_cmp_module_exit();
257err2:
258	nft_immediate_module_exit();
259err1:
260	return err;
261}
262
263void 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