/* * xt_RAWNAT - Netfilter module to do untracked NAT * * Copyright © CC Computer Consultants GmbH, 2008 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 or 3 as published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include "compat_xtables.h" #include "xt_RAWNAT.h" #if LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 23) # warning Have not compile-tested this on anything else than 2.6.23 yet. #endif static inline void csum_repl4(__u16 *sum, __be32 from, __be32 to) { __be32 diff[] = {~from, to}; *sum = csum_fold(csum_partial((const char *)diff, sizeof(diff), ~csum_unfold(*sum))); } static inline u_int32_t remask(u_int32_t addr, u_int32_t repl, unsigned int shift) { u_int32_t mask = (shift == 32) ? 0 : (~(u_int32_t)0 >> shift); return htonl((ntohl(addr) & ~mask) | ntohl(repl)); } static void rawnat_ipv6_mask(__be32 *addr, const __be32 *repl, unsigned int mask) { switch (mask) { case 0: break; case 1 ... 31: addr[0] = remask(addr[0], repl[0], mask); break; case 32: addr[0] = repl[0]; break; case 33 ... 63: addr[0] = repl[0]; addr[1] = remask(addr[1], repl[1], mask - 64); break; case 64: addr[0] = repl[0]; addr[1] = repl[1]; break; case 65 ... 95: addr[0] = repl[0]; addr[1] = repl[1]; addr[2] = remask(addr[2], repl[2], mask - 96); case 96: addr[0] = repl[0]; addr[1] = repl[1]; addr[2] = repl[2]; break; case 97 ... 127: addr[0] = repl[0]; addr[1] = repl[1]; addr[2] = repl[2]; addr[3] = remask(addr[3], repl[3], mask - 128); break; case 128: addr[0] = repl[0]; addr[1] = repl[1]; addr[2] = repl[2]; addr[3] = repl[3]; break; } } static void rawnat4_update_l4(const struct sk_buff *skb) { const struct iphdr *iph = ip_hdr(skb); struct tcphdr *tcph; struct udphdr *udph; unsigned int l4len; switch (iph->protocol) { case IPPROTO_TCP: tcph = tcp_hdr(skb); tcph->check = 0; tcph->check = tcp_v4_check(sizeof(struct tcphdr), iph->saddr, iph->daddr, csum_partial(skb_transport_header(skb), sizeof(struct tcphdr), 0)); break; case IPPROTO_UDP: l4len = skb->len - ip_hdrlen(skb); udph = udp_hdr(skb); udph->check = 0; udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, l4len, IPPROTO_UDP, csum_partial(skb_transport_header(skb), l4len, 0)); break; } } static unsigned int rawsnat_tg4(struct sk_buff *skb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const struct xt_target *target, const void *targinfo) { const struct xt_rawnat_tginfo *info = targinfo; struct iphdr *iph; __be32 na; //u_int32_t na; iph = ip_hdr(skb); //na = remask(iph->saddr, info->addr.ip, info->mask); na = info->addr.ip; if (iph->saddr == na) return XT_CONTINUE; if (!skb_make_writable(skb, sizeof(struct iphdr))) return NF_DROP; // iph = ip_hdr(skb); csum_repl4(&iph->check, iph->saddr, na); iph->saddr = na; // rawnat4_update_l4(skb); return XT_CONTINUE; } static unsigned int rawdnat_tg4(struct sk_buff *skb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const struct xt_target *target, const void *targinfo) { const struct xt_rawnat_tginfo *info = targinfo; struct iphdr *iph; __be32 new_addr; //u_int32_t new_addr; iph = ip_hdr(skb); //new_addr = remask(iph->daddr, info->addr.ip, info->mask); new_addr = info->addr.ip; if (iph->daddr == new_addr) return XT_CONTINUE; if (!skb_make_writable(skb, sizeof(struct iphdr))) return NF_DROP; iph = ip_hdr(skb); csum_repl4(&iph->check, iph->daddr, new_addr); iph->daddr = new_addr; rawnat4_update_l4(skb); return XT_CONTINUE; } static unsigned int rawsnat_tg6(struct sk_buff *skb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const struct xt_target *target, const void *targinfo) { const struct xt_rawnat_tginfo *info = targinfo; struct ipv6hdr *iph; if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) return NF_DROP; iph = ipv6_hdr(skb); // rawnat_ipv6_mask(iph->saddr.s6_addr32, info->addr.ip6, info->mask); return XT_CONTINUE; } static unsigned int rawdnat_tg6(struct sk_buff *skb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const struct xt_target *target, const void *targinfo) { const struct xt_rawnat_tginfo *info = targinfo; struct ipv6hdr *iph; if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) return NF_DROP; iph = ipv6_hdr(skb); // rawnat_ipv6_mask(iph->daddr.s6_addr32, info->addr.ip6, info->mask); return XT_CONTINUE; } static bool rawnat_tg_check(const char *table, const void *entry, const struct xt_target *target, void *targinfo, unsigned int hook_mask) { if (strcmp(table, "raw") == 0 || strcmp(table, "rawpost") == 0) return true; printk(KERN_ERR KBUILD_MODNAME " may only be used in the \"raw\" or " "\"rawpost\" table.\n"); return false; } static struct xt_target rawnat_tg_reg[] __read_mostly = { { .name = "RAWSNAT", .revision = 0, .family = PF_INET, .target = rawsnat_tg4, .targetsize = sizeof(struct xt_rawnat_tginfo), .checkentry = rawnat_tg_check, .me = THIS_MODULE, }, { .name = "RAWSNAT", .revision = 0, .family = PF_INET6, .target = rawsnat_tg6, .targetsize = sizeof(struct xt_rawnat_tginfo), .checkentry = rawnat_tg_check, .me = THIS_MODULE, }, { .name = "RAWDNAT", .revision = 0, .family = PF_INET, .target = rawdnat_tg4, .targetsize = sizeof(struct xt_rawnat_tginfo), .checkentry = rawnat_tg_check, .me = THIS_MODULE, }, { .name = "RAWDNAT", .revision = 0, .family = PF_INET6, .target = rawdnat_tg6, .targetsize = sizeof(struct xt_rawnat_tginfo), .checkentry = rawnat_tg_check, .me = THIS_MODULE, }, }; static int __init rawnat_tg_init(void) { return xt_register_targets(rawnat_tg_reg, ARRAY_SIZE(rawnat_tg_reg)); } static void __exit rawnat_tg_exit(void) { xt_unregister_targets(rawnat_tg_reg, ARRAY_SIZE(rawnat_tg_reg)); } module_init(rawnat_tg_init); module_exit(rawnat_tg_exit); MODULE_AUTHOR("Jan Engelhardt "); MODULE_DESCRIPTION("Xtables: conntrack-less raw NAT"); MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_RAWSNAT"); MODULE_ALIAS("ipt_RAWDNAT"); MODULE_ALIAS("ip6t_RAWSNAT"); MODULE_ALIAS("ip6t_RAWDNAT");