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