• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/net/ipv4/netfilter/
1/* Kernel module to match the port-ranges, trigger related port-ranges,
2 * and alters the destination to a local IP address.
3 *
4 * Copyright (C) 2003, CyberTAN Corporation
5 * All Rights Reserved.
6 *
7 * Description:
8 *   This is kernel module for port-triggering.
9 *
10 *   The module follows the Netfilter framework, called extended packet
11 *   matching modules.
12 *
13 * History:
14 *
15 * 2008.07: code cleaning by Delta Networks Inc.
16 */
17
18#include <linux/types.h>
19#include <linux/skbuff.h>
20#include <linux/version.h>
21#include <linux/ip.h>
22#include <linux/tcp.h>
23#include <net/sock.h>
24#include <linux/timer.h>
25#include <linux/module.h>
26#include <linux/netfilter.h>
27#include <linux/netdevice.h>
28#include <linux/if.h>
29#include <linux/inetdevice.h>
30#include <net/protocol.h>
31#include <net/checksum.h>
32
33#include <linux/netfilter_ipv4.h>
34#include <linux/netfilter_ipv4/ip_tables.h>
35#include <linux/netfilter_ipv4/ip_autofw.h>
36
37#include <net/netfilter/nf_nat.h>
38#include <net/netfilter/nf_nat_helper.h>
39#include <net/netfilter/nf_conntrack_core.h>
40#include <net/netfilter/nf_conntrack_helper.h>
41#include <net/netfilter/nf_conntrack_expect.h>
42
43#ifdef CONFIG_NF_NAT_NEEDED
44#include <net/netfilter/nf_nat_rule.h>
45#else
46#include <linux/netfilter_ipv4/ip_nat_rule.h>
47#endif
48#include <linux/netfilter_ipv4/ipt_TRIGGER.h>
49
50/* This rwlock protects the main hash table, protocol/helper/expected
51 * registrations, conntrack timers
52 */
53static DEFINE_RWLOCK(trigger_lock);
54
55#include <linux/list.h>
56
57#if 0
58#define DEBUGP printk
59#else
60#define DEBUGP(format, args...)
61#endif
62
63#define LIST_FIND(head, cmpfn, type, args...)		\
64({							\
65	const struct list_head *__i, *__j = NULL;	\
66							\
67	read_lock_bh(&trigger_lock);			\
68	list_for_each(__i, (head))			\
69		if (cmpfn((const type)__i , ## args)) {	\
70			__j = __i;			\
71			break;				\
72		}					\
73	read_unlock_bh(&trigger_lock);			\
74	(type)__j;					\
75})
76
77struct ipt_trigger {
78	struct list_head list;	/* Trigger list */
79	struct timer_list timeout;	/* Timer for list destroying */
80	u_int32_t srcip;	/* Outgoing source address */
81	u_int16_t mproto;	/* Trigger protocol */
82	u_int16_t rproto;	/* Related protocol */
83	struct ipt_trigger_ports ports;	/* Trigger and related ports */
84	u_int8_t reply;		/* Confirm a reply connection */
85	struct net *net;
86};
87
88static LIST_HEAD(trigger_list);
89
90static void trigger_timer_refresh(struct ipt_trigger *trig)
91{
92	DEBUGP("%s: mport=%u-%u\n", __FUNCTION__, trig->ports.mport[0], trig->ports.mport[1]);
93	NF_CT_ASSERT(trig);
94	write_lock_bh(&trigger_lock);
95
96	/* Need del_timer for race avoidance (may already be dying). */
97	if (del_timer(&trig->timeout)) {
98		trig->timeout.expires = jiffies + (TRIGGER_TIMEOUT * HZ);
99		add_timer(&trig->timeout);
100	}
101
102	write_unlock_bh(&trigger_lock);
103}
104
105static void __del_trigger(struct ipt_trigger *trig)
106{
107	DEBUGP("%s: mport=%u-%u\n", __FUNCTION__, trig->ports.mport[0], trig->ports.mport[1]);
108	NF_CT_ASSERT(trig);
109
110	/* delete from 'trigger_list' */
111	list_del(&trig->list);
112	kfree(trig);
113}
114
115static int ip_ct_kill_triggered(struct nf_conn *i, void *ifindex)
116{
117	u_int16_t proto, dport;
118	struct ipt_trigger *trig;
119
120	if (!(i->status & IPS_TRIGGER))
121		return 0;
122
123	trig = ifindex;
124	proto = i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
125	dport = ntohs(i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.all);
126
127	if (trig->rproto == proto || trig->rproto == 0)
128		return (trig->ports.rport[0] <= dport && trig->ports.rport[1] >= dport);
129	else
130		return 0;
131}
132
133static void trigger_timeout(unsigned long ul_trig)
134{
135	struct ipt_trigger *trig = (void *)ul_trig;
136
137	DEBUGP("%s: mport=%u-%u\n", __FUNCTION__, trig->ports.mport[0], trig->ports.mport[1]);
138
139	nf_ct_iterate_cleanup(&init_net, ip_ct_kill_triggered, (void *)trig);
140
141	write_lock_bh(&trigger_lock);
142	__del_trigger(trig);
143	write_unlock_bh(&trigger_lock);
144}
145
146static void trigger_flush(void)
147{
148	struct list_head *cur_item, *tmp_item;
149
150	DEBUGP("%s\n", __FUNCTION__);
151	write_lock_bh(&trigger_lock);
152	list_for_each_safe(cur_item, tmp_item, &trigger_list) {
153		struct ipt_trigger *trig = (void *)cur_item;
154
155		DEBUGP("%s: list_for_each_safe(): %p.\n", __FUNCTION__, trig);
156		del_timer(&trig->timeout);
157		nf_ct_iterate_cleanup(trig->net, ip_ct_kill_triggered, (void *)trig);
158		__del_trigger(trig);
159	}
160	write_unlock_bh(&trigger_lock);
161}
162
163/*
164 *	Service-Name	OutBound	InBound
165 * 1.	TMD		UDP:1000	TCP/UDP:2000..2010
166 * 2.	WOKAO		UDP:1000	TCP/UDP:3000..3010
167 * 3.	net2phone-1	UDP:6801	TCP:30000..30000
168 * 4.	net2phone-2	UDP:6801	UDP:30000..30000
169 *
170 * For supporting to use the same outgoing port to trigger different port rules,
171 * it should check the inbound protocol and port range value. If all conditions
172 * are matched, it is a same trigger item, else it needs to create a new one.
173 */
174static inline int trigger_out_matched(const struct ipt_trigger *i,
175				      const u_int16_t proto, const u_int16_t dport, const struct ipt_trigger_info *info)
176{
177	return
178	    i->mproto == proto &&
179	    i->ports.mport[0] <= dport &&
180	    i->ports.mport[1] >= dport &&
181	    i->rproto == info->proto && i->ports.rport[0] == info->ports.rport[0] && i->ports.rport[1] == info->ports.rport[1];
182}
183
184static unsigned int trigger_out(struct sk_buff **pskb, const void *targinfo)
185{
186	const struct ipt_trigger_info *info = targinfo;
187	struct ipt_trigger *trig;
188	struct iphdr *iph = ip_hdr(*pskb);
189	struct tcphdr *tcph = (void *)iph + (iph->ihl << 2);	/* Might be TCP, UDP */
190
191	/* Check if the trigger range has already existed in 'trigger_list'. */
192	trig = LIST_FIND(&trigger_list, trigger_out_matched, struct ipt_trigger *, iph->protocol, ntohs(tcph->dest), info);
193
194	if (trig != NULL) {
195		DEBUGP("Tirgger Out Refresh: %pI4 %u\n", &iph->saddr, ntohs(tcph->dest));
196		/* Yeah, it exists. We need to update(delay) the destroying timer. */
197		trigger_timer_refresh(trig);
198		/* In order to allow multiple hosts use the same port range, we update
199		   the 'saddr' after previous trigger has a reply connection. */
200#if 0
201		if (trig->reply) {
202			trig->srcip = iph->saddr;
203			trig->reply = 0;
204		}
205#else
206		/*
207		 * Well, CD-Router verifies Port-Triggering to support multiple LAN hosts can
208		 * use trigger ports after mappings are aged out. It tests as bellowing ...
209		 *
210		 * net2phone-1  UDP:6801        TCP:30000..30000
211		 * net2phone-2  UDP:6801        UDP:3000..3000
212		 *
213		 * 1. 192.168.1.2 --> UDP:6801 --> verify TCP:30000 opened ?
214		 * 2. waiting for all trigger port mappings to be deleted.
215		 * 3. 192.168.1.3 --> UDP:6801 --> verify TCP:30000 opened ?
216		 *
217		 * 4. 192.168.1.2 --> UDP:6801 --> verify UDP:3000 opened ?
218		 * 5. waiting for all trigger port mappings to be deleted.
219		 * 6. 192.168.1.3 --> UDP:6801 --> verify UDP:3000 opened ?
220		 *
221		 * Between steps 3 and 4, it doesn't wait time out, and on step 3, it has created
222		 * two trigger items: [A].  TCP:30000 ('reply' = 1); B). UDP:3000 ('reply' = 0). so
223		 * on step 4, it can't update the 'srcip' value from '192.168.1.3' to '192.168.1.2'.
224		 * For passing test, and let the customer be happy, we ... ^_^, it is not so bad.
225		 */
226		trig->srcip = iph->saddr;
227#endif
228	} else {
229		/* Create new trigger */
230		trig = (struct ipt_trigger *)kzalloc(sizeof(struct ipt_trigger), GFP_ATOMIC);
231		if (trig == NULL) {
232			DEBUGP("No memory for adding Tigger!\n");
233			return XT_CONTINUE;
234		}
235
236		INIT_LIST_HEAD(&trig->list);
237		init_timer(&trig->timeout);
238		trig->timeout.data = (unsigned long)trig;
239		trig->timeout.function = trigger_timeout;
240		trig->timeout.expires = jiffies + (TRIGGER_TIMEOUT * HZ);
241
242		trig->srcip = iph->saddr;
243		trig->mproto = iph->protocol;
244		trig->rproto = info->proto;
245		trig->reply = 0;
246		trig->net = dev_net((*pskb)->dev);
247		memcpy(&trig->ports, &info->ports, sizeof(struct ipt_trigger_ports));
248
249		/* add to global table of trigger and start timer. */
250		write_lock_bh(&trigger_lock);
251		list_add(&trig->list, &trigger_list);
252		add_timer(&trig->timeout);
253		write_unlock_bh(&trigger_lock);
254	}
255
256	return XT_CONTINUE;	/* We don't block any packet. */
257}
258
259static inline int trigger_in_matched(const struct ipt_trigger *i, const u_int16_t proto, const u_int16_t dport)
260{
261	u_int16_t rproto = i->rproto ? : proto;
262
263	return ((rproto == proto) && (i->ports.rport[0] <= dport)
264		&& (i->ports.rport[1] >= dport));
265}
266
267static unsigned int trigger_in(struct sk_buff **pskb)
268{
269	struct ipt_trigger *trig;
270	struct nf_conn *ct;
271	enum ip_conntrack_info ctinfo;
272	struct iphdr *iph;
273	struct tcphdr *tcph;
274
275	ct = nf_ct_get(*pskb, &ctinfo);
276	if ((ct == NULL) || !(ct->status & IPS_TRIGGER))
277		return XT_CONTINUE;
278
279	iph = ip_hdr(*pskb);
280	tcph = (void *)iph + (iph->ihl << 2);	/* Might be TCP, UDP */
281
282	/* Check if the trigger-ed range has already existed in 'trigger_list'. */
283	trig = LIST_FIND(&trigger_list, trigger_in_matched, struct ipt_trigger *, iph->protocol, ntohs(tcph->dest));
284
285	if (trig != NULL) {
286		DEBUGP("Trigger In: from %pI4, destination port %u\n", &iph->saddr, ntohs(tcph->dest));
287		/* Yeah, it exists. We need to update(delay) the destroying timer. */
288		trigger_timer_refresh(trig);
289
290		return NF_ACCEPT;	/* Accept it, or the imcoming packet could be
291					   dropped in the FORWARD chain */
292	}
293
294	return XT_CONTINUE;	/* Our job is the interception. */
295}
296
297static unsigned int trigger_dnat(struct sk_buff **pskb, unsigned int hooknum)
298{
299	struct ipt_trigger *trig;
300	struct iphdr *iph;
301	struct tcphdr *tcph;
302	struct nf_conn *ct;
303	enum ip_conntrack_info ctinfo;
304	struct nf_nat_multi_range_compat newrange;
305
306	iph = ip_hdr(*pskb);
307	tcph = (void *)iph + (iph->ihl << 2);	/* Might be TCP, UDP */
308
309	NF_CT_ASSERT(hooknum == NF_INET_PRE_ROUTING);
310	/* Check if the trigger-ed range has already existed in 'trigger_list'. */
311	trig = LIST_FIND(&trigger_list, trigger_in_matched, struct ipt_trigger *, iph->protocol, ntohs(tcph->dest));
312
313	if (trig == NULL || trig->srcip == 0)
314		return XT_CONTINUE;	/* We don't block any packet. */
315
316	trig->reply = 1;	/* Confirm there has been a reply connection. */
317	ct = nf_ct_get(*pskb, &ctinfo);
318	NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW));
319
320	DEBUGP("Trigger DNAT: %pI4 ", &trig->srcip);
321	nf_ct_dump_tuple_ip(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
322
323	/* Alter the destination of imcoming packet. */
324	newrange = ((struct nf_nat_multi_range_compat) {
325		    1, { {
326		    IP_NAT_RANGE_MAP_IPS, trig->srcip, trig->srcip, {
327		    0}, {
328		    0}
329		    }}});
330
331	ct->status |= IPS_TRIGGER;
332
333	/* Hand modified range to generic setup. */
334	return nf_nat_setup_info(ct, &newrange.range[0], IP_NAT_MANIP_DST);
335}
336
337static inline int trigger_refresh_matched(const struct ipt_trigger *i, u_int16_t proto, u_int16_t sport)
338{
339	u_int16_t rproto = i->rproto ? : proto;
340
341	return rproto == proto && i->ports.rport[0] <= sport && i->ports.rport[1] >= sport;
342}
343
344static unsigned int trigger_refresh(struct sk_buff **pskb)
345{
346	struct iphdr *iph;
347	struct tcphdr *tcph;
348	struct ipt_trigger *trig;
349	struct nf_conn *ct;
350	enum ip_conntrack_info ctinfo;
351
352	ct = nf_ct_get(*pskb, &ctinfo);
353	if ((ct == NULL) || !(ct->status & IPS_TRIGGER))
354		return XT_CONTINUE;
355
356	iph = ip_hdr(*pskb);
357	tcph = (void *)iph + (iph->ihl << 2);	/* Might be TCP, UDP */
358
359	trig = LIST_FIND(&trigger_list, trigger_refresh_matched, struct ipt_trigger *, iph->protocol, tcph->source);
360	if (trig != NULL) {
361		DEBUGP("Trigger Refresh: from %pI4, %u\n", &iph->saddr, ntohs(tcph->source));
362		trigger_timer_refresh(trig);
363	}
364
365	return XT_CONTINUE;
366}
367
368static unsigned int target(struct sk_buff *skb, const struct xt_action_param *par)
369{
370	const struct ipt_trigger_info *info = par->targinfo;
371	unsigned int hooknum = par->hooknum;
372	const struct iphdr *iph = ip_hdr(skb);
373
374	/* DEBUGP("%s: type = %s\n", __FUNCTION__,
375	   (info->type == IPT_TRIGGER_DNAT) ? "dnat" :
376	   (info->type == IPT_TRIGGER_IN) ? "in" : "out"); */
377
378	/* The Port-trigger only supports TCP and UDP. */
379	if ((iph->protocol != IPPROTO_TCP) && (iph->protocol != IPPROTO_UDP))
380		return XT_CONTINUE;
381
382	if (info->type == IPT_TRIGGER_OUT)
383		return trigger_out(&skb, par->targinfo);
384	else if (info->type == IPT_TRIGGER_IN)
385		return trigger_in(&skb);
386	else if (info->type == IPT_TRIGGER_DNAT)
387		return trigger_dnat(&skb, hooknum);
388	else if (info->type == IPT_TRIGGER_REFRESH)
389		return trigger_refresh(&skb);
390
391	return XT_CONTINUE;
392}
393
394static int checkentry(const struct xt_tgchk_param *par)
395{
396	unsigned int hook_mask = par->hook_mask;
397	const struct ipt_trigger_info *info = par->targinfo;
398	const char *tablename = par->table;
399
400	if ((strcmp(tablename, "mangle") == 0)) {
401		DEBUGP("trigger_check: bad table `%s'.\n", tablename);
402		return -EINVAL;
403	}
404	if (hook_mask & ~((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_FORWARD))) {
405		DEBUGP("trigger_check: bad hooks %x.\n", hook_mask);
406		return -EINVAL;
407	}
408	if (info->proto) {
409		if (info->proto != IPPROTO_TCP && info->proto != IPPROTO_UDP) {
410			DEBUGP("trigger_check: bad proto %d.\n", info->proto);
411			return -EINVAL;
412		}
413	}
414	if (info->type == IPT_TRIGGER_OUT) {
415		if (!info->ports.mport[0] || !info->ports.rport[0]) {
416			DEBUGP("trigger_check: Try 'iptables -j TRIGGER -h' for help.\n");
417			return -EINVAL;
418		}
419	}
420
421	/* Empty the 'trigger_list' */
422	trigger_flush();
423
424	return 0;
425}
426
427static struct xt_target redirect_reg = {
428	.name = "TRIGGER",
429	.family = NFPROTO_IPV4,
430	.target = target,
431	.targetsize = sizeof(struct ipt_trigger_info),
432	.checkentry = checkentry,
433	.me = THIS_MODULE,
434};
435
436static int __init init(void)
437{
438	return xt_register_target(&redirect_reg);
439}
440
441static void __exit fini(void)
442{
443	xt_unregister_target(&redirect_reg);
444	trigger_flush();
445}
446
447module_init(init);
448module_exit(fini);
449