/* This is a module which is used for setting the IP destination address * and port in an skb. */ #include #include #include #include #include #include #include #include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Don Mahurin "); MODULE_DESCRIPTION("iptables DEST target module"); static u_int16_t cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck) { u_int32_t diffs[] = { oldvalinv, newval }; return csum_fold(csum_partial((char *)diffs, sizeof(diffs), oldcheck^0xFFFF)); } static unsigned int target(struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) const struct xt_target *target, #endif const void *targinfo, void *userinfo) { struct sockaddr_in * sin = (struct sockaddr_in *)targinfo; struct iphdr *iph = (*pskb)->nh.iph; void *ipdata = (void *)iph + iph->ihl * 4; if(iph->protocol == IPPROTO_TCP) { struct tcphdr *tcph = (struct tcphdr *)ipdata; tcph->check = cheat_check(~iph->daddr, sin->sin_addr.s_addr, cheat_check(tcph->dest ^ 0xFFFF, sin->sin_port, tcph->check)); tcph->dest = sin->sin_port; } else if(iph->protocol == IPPROTO_UDP) { struct udphdr *udph = (struct udphdr *)ipdata; if (udph->check) /* 0 is a special case meaning no checksum */ udph->check = cheat_check(~iph->daddr, sin->sin_addr.s_addr, cheat_check(udph->dest ^ 0xFFFF, sin->sin_port, udph->check)); udph->dest = sin->sin_port; } iph->check = cheat_check(~iph->daddr, sin->sin_addr.s_addr, iph->check); iph->daddr = sin->sin_addr.s_addr; // (*pskb)->nfcache |= NFC_ALTERED; return IPT_CONTINUE; } static int checkentry(const char *tablename, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) const void *e_void, #else const struct ipt_entry *e, #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) const struct xt_target *target, #endif void *targinfo, unsigned int targinfosize, unsigned int hook_mask) { if (targinfosize != IPT_ALIGN(sizeof(struct sockaddr_in))) { printk(KERN_WARNING "DEST: targinfosize %u != %Zu\n", targinfosize, IPT_ALIGN(sizeof(struct sockaddr_in))); return 0; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) if (strcmp(tablename, "mangle") != 0) { printk(KERN_WARNING "DEST: can only be called from \"mangle\" table, not \"%s\"\n", tablename); return 0; } #endif return 1; } static struct ipt_target ipt_dest_reg = { .name = "DEST", .target = target, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) .targetsize = sizeof(struct sockaddr_in), .table = "mangle", #endif .checkentry = checkentry, .me = THIS_MODULE }; static int __init init(void) { if (ipt_register_target(&ipt_dest_reg)) return -EINVAL; return 0; } static void __exit fini(void) { ipt_unregister_target(&ipt_dest_reg); } module_init(init); module_exit(fini);