summaryrefslogtreecommitdiffstats
path: root/ip6t_MAP66.c
diff options
context:
space:
mode:
Diffstat (limited to 'ip6t_MAP66.c')
-rw-r--r--ip6t_MAP66.c307
1 files changed, 307 insertions, 0 deletions
diff --git a/ip6t_MAP66.c b/ip6t_MAP66.c
new file mode 100644
index 0000000..fb0a243
--- /dev/null
+++ b/ip6t_MAP66.c
@@ -0,0 +1,307 @@
+/*
+ * MAP66: Network Address Translation IPv6-to-IPv6 as
+ * proposed in the IETF's second NAT66 draft document.
+ * (c) 2010 sven-ola()gmx.de
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <net/ipv6.h>
+
+#include "ip6t_MAP66.h"
+
+MODULE_AUTHOR("Sven-Ola <sven-ola()gmx.de>");
+MODULE_DESCRIPTION("Xtables: MAP66 - IPv6 to IPv6 SNAT");
+MODULE_LICENSE("GPL");
+
+#undef MAP66_DEBUG
+#ifdef MAP66_DEBUG
+/* Use lock to serialize, so printks don't overlap */
+static DEFINE_SPINLOCK(MAP66_lock);
+#endif
+
+/* One's complement add */
+static inline u_int16_t add16(
+ u_int16_t a,
+ u_int16_t b)
+{
+ a += b;
+ return a + (a < b);
+}
+
+/* Calc one's complement csum */
+static inline u_int16_t csum16(const u_int16_t *buf, int len)
+{
+ u_int16_t csum = 0;
+ while(len--) csum = add16(csum, *buf++);
+ return csum;
+}
+
+/* Perform checksum neutral mapping */
+static void map16(
+ struct in6_addr* addr,
+ unsigned char mask,
+ const struct in6_addr* to,
+ u_int16_t csum)
+{
+ csum = add16(
+ *((u_int16_t*)addr + mask),
+ add16(csum16((const u_int16_t *)addr, mask), csum)
+ );
+ if (csum == 0xffff) csum = 0x0000;
+ *((u_int16_t *)addr + mask) = csum;
+ memcpy(addr, to, sizeof(u_int16_t) * mask);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+# define rcu_dereference(p) p
+# define ipv6_addr_equal(a, b) 0 == ipv6_addr_cmp(a, b)
+# define ipv6_hdr(sbk) skb->nh.ipv6h
+# ifndef bool
+# define bool int
+# define false 0
+# define true 0
+# endif
+# define xt_target ip6t_target
+# define __read_mostly
+# define xt_register_target ip6t_register_target
+# define xt_unregister_target ip6t_unregister_target
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24)
+# define NF_INET_PRE_ROUTING NF_IP6_PRE_ROUTING
+# define NF_INET_POST_ROUTING NF_IP6_POST_ROUTING
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,26)
+
+struct xt_target_param {
+ const struct net_device *in, *out;
+ const void *targinfo;
+ unsigned int hooknum;
+};
+
+struct xt_tgchk_param {
+ void *targinfo;
+};
+
+#endif
+
+static bool is_my_ipv6_addr(
+ const struct net_device *dev,
+ const struct in6_addr *addr)
+{
+#ifdef MAP66_DEBUG
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28)
+ printk("MAP66: is_my_ipv6_addr(%s, " NIP6_FMT ")\n", NULL != dev ? dev->name : "", NIP6(*addr));
+#else
+ printk("MAP66: is_my_ipv6_addr(%s, %pI6)\n", NULL != dev ? dev->name : "", addr);
+#endif
+#endif
+ if (NULL != dev) {
+ const struct inet6_ifaddr *ifa;
+ const struct inet6_dev *idev = rcu_dereference(dev->ip6_ptr);
+ for (ifa = idev->addr_list; NULL != ifa; ifa = ifa->if_next) {
+#ifdef MAP66_DEBUG
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28)
+ printk("MAP66: cmp " NIP6_FMT "\n", NIP6(ifa->addr));
+#else
+ printk("MAP66: cmp %pI6\n", &ifa->addr);
+#endif
+#endif
+ if (ipv6_addr_equal(&ifa->addr, addr)) return true;
+ }
+ }
+ return false;
+}
+
+static unsigned int MAP66_tg6(
+ struct sk_buff *skb,
+ const struct xt_target_param *par)
+{
+ struct ipv6hdr *hdr = ipv6_hdr(skb);
+ const struct ip6t_MAP66_info *info = (const struct ip6t_MAP66_info *)par->targinfo;
+
+#ifdef MAP66_DEBUG
+ spin_lock_bh(&MAP66_lock);
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28)
+ printk("MAP66i: in=%s, out=%s, saddr=" NIP6_FMT ", daddr=" NIP6_FMT "\n",
+ NULL != par->in ? par->in->name : "",
+ NULL != par->out ? par->out->name : "",
+ NIP6(hdr->saddr), NIP6(hdr->daddr));
+#else
+ printk("MAP66i: in=%s, out=%s, saddr=%pI6, daddr=%pI6\n",
+ NULL != par->in ? par->in->name : "",
+ NULL != par->out ? par->out->name : "",
+ &hdr->saddr, &hdr->daddr);
+#endif
+#endif
+
+ switch(par->hooknum) {
+ case NF_INET_PRE_ROUTING:
+#ifdef MAP66_DEBUG
+ printk("MAP66 PRE, spoof=%d\n", 0 != (IP6T_MAP66_OPT_NOSPOOF & info->mapflags));
+#endif
+ if (0 != (IP6T_MAP66_OPT_NOSPOOF & info->mapflags) ||
+ !is_my_ipv6_addr(par->in, &hdr->daddr))
+ {
+ map16(&hdr->daddr, info->prefixlength, &info->prefix, info->prefixcsum);
+ }
+ break;
+ case NF_INET_POST_ROUTING:
+#ifdef MAP66_DEBUG
+ printk("MAP66 POST, spoof=%d\n", 0 != (IP6T_MAP66_OPT_NOSPOOF & info->mapflags));
+#endif
+ map16(&hdr->saddr, info->prefixlength, &info->prefix, info->prefixcsum);
+ if (0 == (IP6T_MAP66_OPT_NOSPOOF & info->mapflags) &&
+ is_my_ipv6_addr(par->out, &hdr->saddr))
+ {
+#ifdef MAP66_DEBUG
+ spin_unlock_bh(&MAP66_lock);
+#endif
+ return NF_DROP;
+ }
+ break;
+ default:
+#ifdef MAP66_DEBUG
+ printk("MAP66: unsupported hook: %d\n", par->hooknum);
+#endif
+ break;
+ }
+
+#ifdef MAP66_DEBUG
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28)
+ printk("MAP66i: in=%s, out=%s, saddr=" NIP6_FMT ", daddr=" NIP6_FMT "\n",
+ NULL != par->in ? par->in->name : "",
+ NULL != par->out ? par->out->name : "",
+ NIP6(hdr->saddr), NIP6(hdr->daddr));
+#else
+ printk("MAP66i: in=%s, out=%s, saddr=%pI6, daddr=%pI6\n",
+ NULL != par->in ? par->in->name : "",
+ NULL != par->out ? par->out->name : "",
+ &hdr->saddr, &hdr->daddr);
+#endif
+ spin_unlock_bh(&MAP66_lock);
+#endif
+
+ return IP6T_CONTINUE;
+}
+
+static bool MAP66_tg6_check(
+ const struct xt_tgchk_param *par)
+{
+ const struct ip6t_MAP66_info *info = (const struct ip6t_MAP66_info *)par->targinfo;
+
+ if (0 >= info->prefixlength || 8 <= info->prefixlength) {
+ printk("MAP66: Unsupported prefix length /%d\n", 16 * info->prefixlength);
+ return false;
+ }
+ return true;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+
+static unsigned int MAP66_tg6_24(
+ struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *targinfo,
+ void *userinfo)
+{
+ const struct xt_target_param par = {
+ .in = in,
+ .out = out,
+ .hooknum = hooknum,
+ .targinfo = targinfo,
+ };
+ return MAP66_tg6(*pskb, &par);
+}
+
+static int MAP66_tg6_check_24(
+ const char *table,
+ const struct ip6t_entry *entry,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ const struct xt_tgchk_param par = {
+ .targinfo = targinfo,
+ };
+ if (0 != (hook_mask & ~((1 << NF_IP6_PRE_ROUTING) | (1 << NF_IP6_POST_ROUTING)))) {
+ printk("MAP66: Only valid for PRE_ROUTING or POST_ROUTING.\n");
+ return 0;
+ }
+ return MAP66_tg6_check(&par);
+}
+
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+
+static unsigned int MAP66_tg6_26(
+ 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_target_param par = {
+ .in = in,
+ .out = out,
+ .hooknum = hooknum,
+ .targinfo = targinfo,
+ };
+ return MAP66_tg6(skb, &par);
+}
+
+static bool MAP66_tg6_check_26(
+ const char *table,
+ const void *entryinfo,
+ const struct xt_target *target,
+ void *targinfo,
+ unsigned int hook_mask)
+{
+ const struct xt_tgchk_param par = {
+ .targinfo = targinfo,
+ };
+ return MAP66_tg6_check(&par);
+}
+
+#endif
+
+static struct xt_target MAP66_tg6_reg __read_mostly = {
+ .name = "MAP66",
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ .target = MAP66_tg6_24,
+ .checkentry = MAP66_tg6_check_24,
+#else
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+ .family = AF_INET6,
+ .target = MAP66_tg6_26,
+ .checkentry = MAP66_tg6_check_26,
+ .destroy = NULL,
+#else
+ .family = NFPROTO_IPV6,
+ .target = MAP66_tg6,
+ .checkentry = MAP66_tg6_check,
+#endif
+ .targetsize = sizeof(struct ip6t_MAP66_info),
+ .table = "mangle",
+ .hooks = (1 << NF_INET_POST_ROUTING) | (1 << NF_INET_PRE_ROUTING),
+#endif
+ .me = THIS_MODULE,
+};
+
+static int __init MAP66_tg6_init(void)
+{
+ return xt_register_target(&MAP66_tg6_reg);
+}
+
+static void __exit MAP66_tg6_exit(void)
+{
+ xt_unregister_target(&MAP66_tg6_reg);
+}
+
+module_init(MAP66_tg6_init);
+module_exit(MAP66_tg6_exit);