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
8 * This is kernel module for port-triggering.
10 * The module follows the Netfilter framework, called extended packet
15 #include <linux/types.h>
17 #include <linux/timer.h>
18 #include <linux/module.h>
19 #include <linux/netfilter.h>
20 #include <linux/netdevice.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>
46 #define DEBUGP(format, args...)
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]);
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]);
84 MUST_BE_WRITE_LOCKED(&ip_conntrack_lock
);
86 /* delete from 'trigger_list' */
87 list_del(&trig
->list
);
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
);
100 WRITE_UNLOCK(&ip_conntrack_lock
);
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
);
114 WRITE_UNLOCK(&ip_conntrack_lock
);
115 DEBUGP("%s: OOM allocating trigger list\n", __FUNCTION__
);
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
);
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
));
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
,
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
));
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. */
172 found
->srcip
= iph
->saddr
;
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
;
198 return ((rproto
== proto
) && (i
->ports
.rport
[0] <= dport
)
199 && (i
->ports
.rport
[1] >= dport
));
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
,
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
));
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. */
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
,
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
,
266 /* Hand modified range to generic setup. */
267 return ip_nat_setup_info(ct
, &newrange
, hooknum
);
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
,
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
))
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
);
300 trigger_check(const char *tablename
,
301 const struct ipt_entry
*e
,
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
);
313 if (targinfosize
!= IPT_ALIGN(sizeof(*info
))) {
314 DEBUGP("trigger_check: size %u.\n", targinfosize
);
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
);
322 if (info
->proto
!= IPPROTO_TCP
&& info
->proto
!= IPPROTO_UDP
) {
323 DEBUGP("trigger_check: bad proto %d.\n", info
->proto
);
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");
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
);
346 static struct ipt_target redirect_reg
347 = { { NULL
, NULL
}, "TRIGGER", trigger_target
, trigger_check
, NULL
,
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
);