Tomato 1.25
[tomato.git] / release / src / linux / linux / net / ipv4 / netfilter / ipt_TRIGGER.c
blob07103fa59886c3f53ef0056b5d4d1065963d6e82
1 /* Kernel module to match the port-ranges, trigger related port-ranges,
2 * and alters the destination to a local IP address.
4 * Copyright (C) 2003, CyberTAN Corporation
5 * All Rights Reserved.
7 * Description:
8 * This is kernel module for port-triggering.
10 * The module follows the Netfilter framework, called extended packet
11 * matching modules.
15 #include <linux/types.h>
16 #include <linux/ip.h>
17 #include <linux/timer.h>
18 #include <linux/module.h>
19 #include <linux/netfilter.h>
20 #include <linux/netdevice.h>
21 #include <linux/if.h>
22 #include <linux/inetdevice.h>
23 #include <net/protocol.h>
24 #include <net/checksum.h>
26 #include <linux/netfilter_ipv4.h>
27 #include <linux/netfilter_ipv4/ip_tables.h>
28 #include <linux/netfilter_ipv4/ip_conntrack.h>
29 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
30 #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
31 #include <linux/netfilter_ipv4/ip_autofw.h>
32 #include <linux/netfilter_ipv4/lockhelp.h>
33 #include <linux/netfilter_ipv4/ip_nat_rule.h>
34 #include <linux/netfilter_ipv4/ipt_TRIGGER.h>
36 /* This rwlock protects the main hash table, protocol/helper/expected
37 * registrations, conntrack timers*/
38 #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock)
39 #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock)
41 #include <linux/netfilter_ipv4/listhelp.h>
43 #if 0
44 #define DEBUGP printk
45 #else
46 #define DEBUGP(format, args...)
47 #endif
49 struct ipt_trigger {
50 struct list_head list; /* Trigger list */
51 struct timer_list timeout; /* Timer for list destroying */
52 u_int32_t srcip; /* Outgoing source address */
53 u_int32_t dstip; /* Outgoing destination address */
54 u_int16_t mproto; /* Trigger protocol */
55 u_int16_t rproto; /* Related protocol */
56 struct ipt_trigger_ports ports; /* Trigger and related ports */
57 u_int8_t reply; /* Confirm a reply connection */
60 LIST_HEAD(trigger_list);
61 //DECLARE_LOCK(ip_trigger_lock);
63 static void trigger_refresh(struct ipt_trigger *trig, unsigned long extra_jiffies)
65 DEBUGP("%s: mport=%u-%u\n", __FUNCTION__, trig->ports.mport[0], trig->ports.mport[1]);
67 IP_NF_ASSERT(trig);
68 WRITE_LOCK(&ip_conntrack_lock);
70 /* Need del_timer for race avoidance (may already be dying). */
71 if (del_timer(&trig->timeout)) {
72 trig->timeout.expires = jiffies + extra_jiffies;
73 add_timer(&trig->timeout);
76 WRITE_UNLOCK(&ip_conntrack_lock);
79 static void __del_trigger(struct ipt_trigger *trig)
81 DEBUGP("%s: mport=%u-%u\n", __FUNCTION__, trig->ports.mport[0], trig->ports.mport[1]);
83 IP_NF_ASSERT(trig);
84 MUST_BE_WRITE_LOCKED(&ip_conntrack_lock);
86 /* delete from 'trigger_list' */
87 list_del(&trig->list);
88 kfree(trig);
91 static void trigger_timeout(unsigned long ul_trig)
93 struct ipt_trigger *trig= (void *) ul_trig;
95 // DEBUGP("trigger list %p timed out\n", trig);
96 DEBUGP("%s: mport=%u-%u\n", __FUNCTION__, trig->ports.mport[0], trig->ports.mport[1]);
98 WRITE_LOCK(&ip_conntrack_lock);
99 __del_trigger(trig);
100 WRITE_UNLOCK(&ip_conntrack_lock);
103 static unsigned int
104 add_new_trigger(struct ipt_trigger *trig)
106 struct ipt_trigger *new;
108 DEBUGP("!!!!!!!!!!!! %s !!!!!!!!!!!\n", __FUNCTION__);
109 WRITE_LOCK(&ip_conntrack_lock);
110 new = (struct ipt_trigger *)
111 kmalloc(sizeof(struct ipt_trigger), GFP_ATOMIC);
113 if (!new) {
114 WRITE_UNLOCK(&ip_conntrack_lock);
115 DEBUGP("%s: OOM allocating trigger list\n", __FUNCTION__);
116 return -ENOMEM;
119 memset(new, 0, sizeof(*trig));
120 INIT_LIST_HEAD(&new->list);
121 memcpy(new, trig, sizeof(*trig));
123 /* add to global table of trigger */
124 list_prepend(&trigger_list, &new->list);
125 /* add and start timer if required */
126 init_timer(&new->timeout);
127 new->timeout.data = (unsigned long)new;
128 new->timeout.function = trigger_timeout;
129 new->timeout.expires = jiffies + (TRIGGER_TIMEOUT * HZ);
130 add_timer(&new->timeout);
132 WRITE_UNLOCK(&ip_conntrack_lock);
134 return 0;
137 static inline int trigger_out_matched(const struct ipt_trigger *i,
138 const u_int16_t proto, const u_int16_t dport)
140 /* DEBUGP("%s: i=%p, proto= %d, dport=%d.\n", __FUNCTION__, i, proto, dport);
141 DEBUGP("%s: Got one, mproto= %d, mport[0..1]=%d, %d.\n", __FUNCTION__,
142 i->mproto, i->ports.mport[0], i->ports.mport[1]); */
144 return ((i->mproto == proto) && (i->ports.mport[0] <= dport)
145 && (i->ports.mport[1] >= dport));
148 static unsigned int
149 trigger_out(struct sk_buff **pskb,
150 unsigned int hooknum,
151 const struct net_device *in,
152 const struct net_device *out,
153 const void *targinfo,
154 void *userinfo)
156 const struct ipt_trigger_info *info = targinfo;
157 struct ipt_trigger trig, *found;
158 const struct iphdr *iph = (*pskb)->nh.iph;
159 struct tcphdr *tcph = (void *)iph + iph->ihl*4; /* Might be TCP, UDP */
161 DEBUGP("############# %s ############\n", __FUNCTION__);
162 /* Check if the trigger range has already existed in 'trigger_list'. */
163 found = LIST_FIND(&trigger_list, trigger_out_matched,
164 struct ipt_trigger *, iph->protocol, ntohs(tcph->dest));
166 if (found) {
167 /* Yeah, it exists. We need to update(delay) the destroying timer. */
168 trigger_refresh(found, TRIGGER_TIMEOUT * HZ);
169 /* In order to allow multiple hosts use the same port range, we update
170 the 'saddr' after previous trigger has a reply connection. */
171 if (found->reply)
172 found->srcip = iph->saddr;
174 else {
175 /* Create new trigger */
176 memset(&trig, 0, sizeof(trig));
177 trig.srcip = iph->saddr;
178 trig.mproto = iph->protocol;
179 trig.rproto = info->proto;
180 memcpy(&trig.ports, &info->ports, sizeof(struct ipt_trigger_ports));
181 add_new_trigger(&trig); /* Add the new 'trig' to list 'trigger_list'. */
184 return IPT_CONTINUE; /* We don't block any packet. */
187 static inline int trigger_in_matched(const struct ipt_trigger *i,
188 const u_int16_t proto, const u_int16_t dport)
190 /* DEBUGP("%s: i=%p, proto= %d, dport=%d.\n", __FUNCTION__, i, proto, dport);
191 DEBUGP("%s: Got one, rproto= %d, rport[0..1]=%d, %d.\n", __FUNCTION__,
192 i->rproto, i->ports.rport[0], i->ports.rport[1]); */
193 u_int16_t rproto = i->rproto;
195 if (!rproto)
196 rproto = proto;
198 return ((rproto == proto) && (i->ports.rport[0] <= dport)
199 && (i->ports.rport[1] >= dport));
202 static unsigned int
203 trigger_in(struct sk_buff **pskb,
204 unsigned int hooknum,
205 const struct net_device *in,
206 const struct net_device *out,
207 const void *targinfo,
208 void *userinfo)
210 struct ipt_trigger *found;
211 const struct iphdr *iph = (*pskb)->nh.iph;
212 struct tcphdr *tcph = (void *)iph + iph->ihl*4; /* Might be TCP, UDP */
214 /* Check if the trigger-ed range has already existed in 'trigger_list'. */
215 found = LIST_FIND(&trigger_list, trigger_in_matched,
216 struct ipt_trigger *, iph->protocol, ntohs(tcph->dest));
217 if (found) {
218 DEBUGP("############# %s ############\n", __FUNCTION__);
219 /* Yeah, it exists. We need to update(delay) the destroying timer. */
220 trigger_refresh(found, TRIGGER_TIMEOUT * HZ);
221 return NF_ACCEPT; /* Accept it, or the imcoming packet could be
222 dropped in the FORWARD chain */
225 return IPT_CONTINUE; /* Our job is the interception. */
228 static unsigned int
229 trigger_dnat(struct sk_buff **pskb,
230 unsigned int hooknum,
231 const struct net_device *in,
232 const struct net_device *out,
233 const void *targinfo,
234 void *userinfo)
236 struct ipt_trigger *found;
237 const struct iphdr *iph = (*pskb)->nh.iph;
238 struct tcphdr *tcph = (void *)iph + iph->ihl*4; /* Might be TCP, UDP */
239 struct ip_conntrack *ct;
240 enum ip_conntrack_info ctinfo;
241 struct ip_nat_multi_range newrange;
243 IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING);
244 /* Check if the trigger-ed range has already existed in 'trigger_list'. */
245 found = LIST_FIND(&trigger_list, trigger_in_matched,
246 struct ipt_trigger *, iph->protocol, ntohs(tcph->dest));
248 if (!found || !found->srcip)
249 return IPT_CONTINUE; /* We don't block any packet. */
251 DEBUGP("############# %s ############\n", __FUNCTION__);
252 found->reply = 1; /* Confirm there has been a reply connection. */
253 ct = ip_conntrack_get(*pskb, &ctinfo);
254 IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW));
256 DEBUGP("%s: got ", __FUNCTION__);
257 DUMP_TUPLE_RAW(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
259 /* Alter the destination of imcoming packet. */
260 newrange = ((struct ip_nat_multi_range)
261 { 1, { { IP_NAT_RANGE_MAP_IPS,
262 found->srcip, found->srcip,
263 { 0 }, { 0 }
264 } } });
266 /* Hand modified range to generic setup. */
267 return ip_nat_setup_info(ct, &newrange, hooknum);
270 static unsigned int
271 trigger_target(struct sk_buff **pskb,
272 unsigned int hooknum,
273 const struct net_device *in,
274 const struct net_device *out,
275 const void *targinfo,
276 void *userinfo)
278 const struct ipt_trigger_info *info = targinfo;
279 const struct iphdr *iph = (*pskb)->nh.iph;
281 /* DEBUGP("%s: type = %s\n", __FUNCTION__,
282 (info->type == IPT_TRIGGER_DNAT) ? "dnat" :
283 (info->type == IPT_TRIGGER_IN) ? "in" : "out"); */
285 /* The Port-trigger only supports TCP and UDP. */
286 if ((iph->protocol != IPPROTO_TCP) && (iph->protocol != IPPROTO_UDP))
287 return IPT_CONTINUE;
289 if (info->type == IPT_TRIGGER_OUT)
290 return trigger_out(pskb, hooknum, in, out, targinfo, userinfo);
291 else if (info->type == IPT_TRIGGER_IN)
292 return trigger_in(pskb, hooknum, in, out, targinfo, userinfo);
293 else if (info->type == IPT_TRIGGER_DNAT)
294 return trigger_dnat(pskb, hooknum, in, out, targinfo, userinfo);
296 return IPT_CONTINUE;
299 static int
300 trigger_check(const char *tablename,
301 const struct ipt_entry *e,
302 void *targinfo,
303 unsigned int targinfosize,
304 unsigned int hook_mask)
306 const struct ipt_trigger_info *info = targinfo;
307 struct list_head *cur_item, *tmp_item;
309 if ((strcmp(tablename, "mangle") == 0)) {
310 DEBUGP("trigger_check: bad table `%s'.\n", tablename);
311 return 0;
313 if (targinfosize != IPT_ALIGN(sizeof(*info))) {
314 DEBUGP("trigger_check: size %u.\n", targinfosize);
315 return 0;
317 if (hook_mask & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_POST_ROUTING))) {
318 DEBUGP("trigger_check: bad hooks %x.\n", hook_mask);
319 return 0;
321 if (info->proto) {
322 if (info->proto != IPPROTO_TCP && info->proto != IPPROTO_UDP) {
323 DEBUGP("trigger_check: bad proto %d.\n", info->proto);
324 return 0;
327 if (info->type == IPT_TRIGGER_OUT) {
328 if (!info->ports.mport[0] || !info->ports.rport[0]) {
329 DEBUGP("trigger_check: Try 'iptbles -j TRIGGER -h' for help.\n");
330 return 0;
334 /* Empty the 'trigger_list' */
335 list_for_each_safe(cur_item, tmp_item, &trigger_list) {
336 struct ipt_trigger *trig = (void *)cur_item;
338 DEBUGP("%s: list_for_each_safe(): %p.\n", __FUNCTION__, trig);
339 del_timer(&trig->timeout);
340 __del_trigger(trig);
343 return 1;
346 static struct ipt_target redirect_reg
347 = { { NULL, NULL }, "TRIGGER", trigger_target, trigger_check, NULL,
348 THIS_MODULE };
350 static int __init init(void)
352 return ipt_register_target(&redirect_reg);
355 static void __exit fini(void)
357 ipt_unregister_target(&redirect_reg);
360 module_init(init);
361 module_exit(fini);