summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2011-11-10 07:53:52 +0100
committerMatthias Schiffer <mschiffer@universe-factory.net>2011-11-10 07:53:52 +0100
commita95082117ad4813141f5733e5c3dda8efc5dec16 (patch)
tree61edab08d7390e0179fc6c2900234c040168500a
parentc25768e4311eaf9e9d9166e42fe74e9c597c466d (diff)
downloadNPTv6-a95082117ad4813141f5733e5c3dda8efc5dec16.tar
NPTv6-a95082117ad4813141f5733e5c3dda8efc5dec16.zip
Make this work again.
-rw-r--r--.gitignore8
-rw-r--r--Makefile33
-rw-r--r--ip6t_DNPTV6.c83
-rw-r--r--ip6t_MAP66.c381
-rw-r--r--ip6t_MAP66.h30
-rw-r--r--ip6t_NPTV6.h16
-rw-r--r--ip6t_NPTV6_common.h88
-rw-r--r--ip6t_SNPTV6.c83
-rw-r--r--libip6t_DNPTV6.c46
-rw-r--r--libip6t_MAP66.c227
-rw-r--r--libip6t_NPTV6.c62
-rw-r--r--libip6t_NPTV6.h29
-rw-r--r--libip6t_SNPTV6.c46
13 files changed, 479 insertions, 653 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2748eec
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+*.o
+*~
+*.so
+*.ko
+*.mod.c
+.*
+Module.symvers
+modules.order
diff --git a/Makefile b/Makefile
index e5cc2f0..3122aad 100644
--- a/Makefile
+++ b/Makefile
@@ -1,30 +1,33 @@
-obj-m := ip6t_MAP66.o
+obj-m := ip6t_SNPTV6.o ip6t_DNPTV6.o
KVERSION := $(shell uname -r)
KPATH := /lib/modules/$(KVERSION)/build
-IPTABLES_VERSION := $(shell (/sbin/ip6tables --version 2>&- || ip6tables --version)|sed 's/^.*\([[:digit:]]\+\.[[:digit:]]\+\.[[:digit:]]\+\).*/\1/')
-IPTABLES_VERSION_CODE := $(shell echo $$(( $$(echo $(IPTABLES_VERSION)|sed 's/^.*\([[:digit:]]\+\)\.\([[:digit:]]\+\)\.\([[:digit:]]\+\).*/\1 * 65536 + \2 * 256 + \3/') )))
ifeq ($(DEBUG),1)
EXTRA_CFLAGS := -DDEBUG
endif
-all: libip6t_MAP66.so
+all: module libip6t_SNPTV6.so libip6t_DNPTV6.so
+
+module:
$(MAKE) -C $(KPATH) M=$(PWD) modules
-libip6t_MAP66.so: libip6t_MAP66.o
- gcc -shared -o $@ $<
+libip6t_SNPTV6.so: libip6t_SNPTV6.o libip6t_NPTV6.o
+ gcc -shared -o $@ $^
-libip6t_MAP66.o: libip6t_MAP66.c ip6t_MAP66.h Makefile
- gcc $(CFLAGS) -DIPTABLES_VERSION=\"$(IPTABLES_VERSION)\" -DIPTABLES_VERSION_CODE=$(IPTABLES_VERSION_CODE) -D_LARGEFILE_SOURCE=1 -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 -D_REENTRANT -Werror -Wall -Waggregate-return -Wmissing-declarations -Wmissing-prototypes -Wredundant-decls -Wshadow -Winline -pipe -DXTABLES_LIBDIR=\"/usr/local/libexec/xtables\" -D_INIT=libip6t_MAP66_init -DPIC -fPIC -g -O2 -o $@ -c $<
+libip6t_DNPTV6.so: libip6t_DNPTV6.o libip6t_NPTV6.o
+ gcc -shared -o $@ $^
-clean:
- $(MAKE) -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
- rm -f *.so
- rm -f *~
+libip6t_NPTV6.o: libip6t_NPTV6.c libip6t_NPTV6.h ip6t_NPTV6.h Makefile
+ gcc $(CFLAGS) -D_LARGEFILE_SOURCE=1 -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 -D_REENTRANT -Werror -Wall -Waggregate-return -Wmissing-declarations -Wmissing-prototypes -Wredundant-decls -Wshadow -Winline -pipe -DXTABLES_LIBDIR=\"/usr/lib/iptables\" -DPIC -fPIC -g -O2 -o $@ -c $<
-install: /lib/xtables/libip6t_MAP66.so
+libip6t_SNPTV6.o: libip6t_SNPTV6.c libip6t_NPTV6.h ip6t_NPTV6.h Makefile
+ gcc $(CFLAGS) -D_LARGEFILE_SOURCE=1 -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 -D_REENTRANT -Werror -Wall -Waggregate-return -Wmissing-declarations -Wmissing-prototypes -Wredundant-decls -Wshadow -Winline -pipe -DXTABLES_LIBDIR=\"/usr/lib/iptables\" -DPIC -fPIC -g -O2 -o $@ -c $<
-/lib/xtables/libip6t_MAP66.so: libip6t_MAP66.so
- cp $< $@
+libip6t_DNPTV6.o: libip6t_DNPTV6.c libip6t_NPTV6.h ip6t_NPTV6.h Makefile
+ gcc $(CFLAGS) -D_LARGEFILE_SOURCE=1 -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 -D_REENTRANT -Werror -Wall -Waggregate-return -Wmissing-declarations -Wmissing-prototypes -Wredundant-decls -Wshadow -Winline -pipe -DXTABLES_LIBDIR=\"/usr/lib/iptables\" -DPIC -fPIC -g -O2 -o $@ -c $<
+
+clean:
+ $(MAKE) -C $(KPATH) M=$(PWD) clean
+ rm -f libip6t_SNPTV6.so libip6t_DNPTV6.so *.o
README.txt: README.dbk
xmlto text README.dbk
diff --git a/ip6t_DNPTV6.c b/ip6t_DNPTV6.c
new file mode 100644
index 0000000..96d16e8
--- /dev/null
+++ b/ip6t_DNPTV6.c
@@ -0,0 +1,83 @@
+/*
+ * NATv6: IPv6-to-IPv6 Network Prefix Translation as
+ * proposed in RFC 6296.
+ * Based on MAP66 (c) 2010 sven-ola()gmx.de
+ * (c) 2011 mschiffer()universe-factory.net "I'm the one to blame for any problems with this version ;P"
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <net/ipv6.h>
+
+#include "ip6t_NPTV6_common.h"
+
+MODULE_AUTHOR("Matthias Schiffer <mschiffer()universe-factory.net>");
+MODULE_DESCRIPTION("Xtables: Destination NPTv6 - IPv6-to-IPv6 Network Prefix Translation");
+MODULE_LICENSE("GPL");
+
+
+static unsigned int dnptv6_tg6(struct sk_buff *skb, const struct xt_action_param *par)
+{
+ struct ipv6hdr* hdr = ipv6_hdr(skb);
+ const struct ip6t_nptv6_info *info = par->targinfo;
+
+ pr_devel("DNPTV6: enter 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));
+
+ if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) {
+ pr_devel("DNPTV6: unwriteable, dropped\n");
+ return NF_DROP;
+ }
+ hdr = ipv6_hdr(skb);
+
+ if (!translate_address(&hdr->daddr, &info->nptv6_prefix, info->nptv6_prefix_len)) {
+ pr_devel("DNPTV6: untranslatable address\n");
+ return NF_DROP;
+ }
+
+ pr_devel("DNPTV6: exit 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));
+
+ return NF_ACCEPT;
+}
+
+static int dnptv6_tg6_check(const struct xt_tgchk_param *par)
+{
+ const struct ip6t_nptv6_info *info = par->targinfo;
+
+ if (info->nptv6_prefix_len > 64) {
+ printk("DNPTV6: Prefix length longer than 64 given\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct xt_target dnptv6_tg6_reg __read_mostly = {
+ .name = "DNPTV6",
+ .family = NFPROTO_IPV6,
+ .target = dnptv6_tg6,
+ .checkentry = dnptv6_tg6_check,
+ .targetsize = sizeof(struct ip6t_nptv6_info),
+ .table = "mangle",
+ .hooks = (1 << NF_INET_PRE_ROUTING),
+ .me = THIS_MODULE,
+};
+
+static int __init dnptv6_tg6_init(void)
+{
+ return xt_register_target(&dnptv6_tg6_reg);
+}
+
+static void __exit dnptv6_tg6_exit(void)
+{
+ xt_unregister_target(&dnptv6_tg6_reg);
+}
+
+module_init(dnptv6_tg6_init);
+module_exit(dnptv6_tg6_exit);
diff --git a/ip6t_MAP66.c b/ip6t_MAP66.c
deleted file mode 100644
index 1711757..0000000
--- a/ip6t_MAP66.c
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * 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 <linux/tcp.h>
-#include <linux/udp.h>
-#include <linux/icmpv6.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
-# include <linux/dccp.h>
-#endif
-
-#include "ip6t_MAP66.h"
-
-MODULE_AUTHOR("Sven-Ola <sven-ola()gmx.de>");
-MODULE_DESCRIPTION("Xtables: MAP66 - IPv6 to IPv6 SNAT");
-MODULE_LICENSE("GPL");
-
-/* 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 mapping with csum update, see RFC 1624 */
-static void map16(
- u_int16_t *dest,
- const u_int16_t *source,
- int len,
- u_int16_t csum,
- u_int16_t* pcsum)
-{
- if (NULL == pcsum) {
- pcsum = dest + len;
- }
- *pcsum = ~add16(
- add16(
- ~(*pcsum),
- ~csum16(dest, len)
- ),
- csum
- );
- memcpy(dest, source, len * sizeof(u_int16_t));
-}
-
-#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(skb) skb->nh.ipv6h
-# ifndef bool
-# define bool int
-# define false 0
-# define true 1
-# endif
-# define xt_target ip6t_target
-# define __read_mostly
-# define xt_register_target ip6t_register_target
-# define xt_unregister_target ip6t_unregister_target
-#endif
-
-#ifndef pr_devel
-# ifdef DEBUG
-# define pr_devel(fmt, ...) printk(fmt, ##__VA_ARGS__)
-# else
-# define pr_devel(fmt, ...) ({ if (0); })
-# endif
-#endif
-
-#ifndef NIP6_FMT
-# define NIP6_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
-# define NIP6(addr) \
- ntohs((addr).s6_addr16[0]), \
- ntohs((addr).s6_addr16[1]), \
- ntohs((addr).s6_addr16[2]), \
- ntohs((addr).s6_addr16[3]), \
- ntohs((addr).s6_addr16[4]), \
- ntohs((addr).s6_addr16[5]), \
- ntohs((addr).s6_addr16[6]), \
- ntohs((addr).s6_addr16[7])
-#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)
-{
- 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) {
- 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);
- unsigned char* transport = (unsigned char* )(hdr + 1);
- u8 nexthdr = 0;
- u_int16_t* pcsum = NULL;
- const struct ip6t_MAP66_info *info = par->targinfo;
-
- pr_devel("MAP66: enter 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));
-
-#if 0
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
- if (skb_cloned(skb) && !skb->sk) {
- struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
- if (!nskb) {
- pr_devel("MAP66: cannot copy, dropped\n");
- return NF_DROP;
- }
- kfree_skb(skb);
- skb = nskb;
- }
-#else
- if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) {
- pr_devel("MAP66: unwriteable, dropped\n");
- return NF_DROP;
- }
-#endif
- hdr = ipv6_hdr(skb);
-#endif
-
- if (0 != (IP6T_MAP66_OPT_CSUM & info->mapflags)) {
- nexthdr = hdr->nexthdr;
- if (ipv6_ext_hdr(nexthdr)) {
- int hoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
- , skb->len - sizeof(struct ipv6hdr)
-#endif
- );
- if (hoff < 0) {
- pr_devel("MAP66: Unsupported packet dropped\n");
- return NF_DROP;
- }
- transport += hoff;
- }
- switch(nexthdr) {
- case IPPROTO_TCP:
- pcsum = &((struct tcphdr*)transport)->check;
- break;
- case IPPROTO_UDP:
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
- case IPPROTO_UDPLITE:
-#endif
- pcsum = &((struct udphdr*)transport)->check;
- break;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
- case IPPROTO_DCCP:
- pcsum = &((struct dccp_hdr*)transport)->dccph_checksum;
- break;
-#endif
- case IPPROTO_ICMPV6:
- pcsum = &((struct icmp6hdr*)transport)->icmp6_cksum;
- break;
- default:
- pr_devel("MAP66: Unsupported protocol %d dropped\n", nexthdr);
- return NF_DROP;
- }
- }
-
- if (0 != (IP6T_MAP66_OPT_DST_TO & info->mapflags)) {
- if (0 != (IP6T_MAP66_OPT_NOCHECK & info->mapflags) || !is_my_ipv6_addr(
- NF_INET_PRE_ROUTING == par->hooknum ? par->in : par->out, &hdr->daddr))
- {
- map16((u_int16_t *)&hdr->daddr, (u_int16_t *)&info->pfix_dst_to,
- info->pfix_dst_len, info->pfix_dst_csum, pcsum);
- if (NULL != pcsum && IPPROTO_ICMPV6 == nexthdr && 128 > ((struct icmp6hdr*)transport)->icmp6_type) {
- printk("Need to change returning ICMPv6 dest addr for traceroute\n");
- }
- }
- }
-
- if (0 != (IP6T_MAP66_OPT_SRC_TO & info->mapflags)) {
- map16((u_int16_t *)&hdr->saddr, (u_int16_t *)&info->pfix_src_to,
- info->pfix_src_len, info->pfix_src_csum, pcsum);
- if (0 == (IP6T_MAP66_OPT_NOCHECK & info->mapflags) && is_my_ipv6_addr(
- NF_INET_PRE_ROUTING == par->hooknum ? par->in : par->out, &hdr->saddr))
- {
- return NF_DROP;
- }
- }
-
- pr_devel("MAP66: exit 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));
-
- return IP6T_CONTINUE;
-}
-
-static bool MAP66_tg6_check(
- const struct xt_tgchk_param *par)
-{
- const struct ip6t_MAP66_info *info = par->targinfo;
-
- if (0 == ((IP6T_MAP66_OPT_DST_TO | IP6T_MAP66_OPT_SRC_TO) & info->mapflags)) {
- printk("MAP66: No --" IP6T_MAP66_DST_TO " nor --" IP6T_MAP66_SRC_TO "\n");
- return false;
- }
-
- if (0 != (IP6T_MAP66_OPT_DST_TO & info->mapflags) && (0 >= info->pfix_dst_len ||
- (0 != (IP6T_MAP66_OPT_CSUM & info->mapflags) ? 8 : 7) < info->pfix_dst_len))
- {
- if (8 == info->pfix_dst_len) {
- printk("MAP66: --" IP6T_MAP66_DST_TO " prefix length /%d only "
- "possible with --csum\n", 16 * info->pfix_dst_len);
- }
- else {
- printk("MAP66: Unsupported --" IP6T_MAP66_DST_TO " prefix "
- "length /%d\n", 16 * info->pfix_dst_len);
- }
- return false;
- }
-
- if (0 != (IP6T_MAP66_OPT_SRC_TO & info->mapflags) && (0 >= info->pfix_src_len ||
- (0 != (IP6T_MAP66_OPT_CSUM & info->mapflags) ? 8 : 7) < info->pfix_src_len))
- {
- if (8 == info->pfix_src_len) {
- printk("MAP66: --" IP6T_MAP66_SRC_TO " prefix length /%d only "
- "possible with --csum\n", 16 * info->pfix_src_len);
- }
- else {
- printk("MAP66: Unsupported --" IP6T_MAP66_SRC_TO " prefix "
- "length /%d\n", 16 * info->pfix_src_len);
- }
- 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);
diff --git a/ip6t_MAP66.h b/ip6t_MAP66.h
deleted file mode 100644
index b29592c..0000000
--- a/ip6t_MAP66.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * MAP66: Network Address Translation IPv6-to-IPv6 as
- * proposed in the IETF's second NAT66 draft document.
- * (c) 2010 sven-ola()gmx.de
- */
-
-#ifndef _IP6T_MAP66_H
-#define _IP6T_MAP66_H
-
-#define IP6T_MAP66_DST_TO "dst-to"
-#define IP6T_MAP66_SRC_TO "src-to"
-
-#define IP6T_MAP66_OPT_DST_TO 0x01
-#define IP6T_MAP66_OPT_SRC_TO 0x02
-#define IP6T_MAP66_OPT_NOCHECK 0x04
-#define IP6T_MAP66_OPT_CSUM 0x08
-
-struct ip6t_MAP66_info {
- struct in6_addr pfix_dst_to; /* The prefix to map destination addrs to */
- u_int16_t pfix_dst_len; /* Destination addrs prefix length DIV 16 */
- u_int16_t pfix_dst_csum; /* Pre-calculated csum for destination addrs */
- struct in6_addr pfix_src_to; /* The prefix to map source addrs from */
- u_int16_t pfix_src_len; /* Source addrs prefix length DIV 16 */
- u_int16_t pfix_src_csum; /* Pre-calculated csum for source addrs */
- u_int16_t mapflags; /* Some flags */
-};
-
-extern void _init(void);
-
-#endif /*_IPT_MAP66_H*/
diff --git a/ip6t_NPTV6.h b/ip6t_NPTV6.h
new file mode 100644
index 0000000..a1e5e77
--- /dev/null
+++ b/ip6t_NPTV6.h
@@ -0,0 +1,16 @@
+/*
+ * NATv6: IPv6-to-IPv6 Network Prefix Translation as
+ * proposed in RFC 6296.
+ * Based on MAP66 (c) 2010 sven-ola()gmx.de
+ * (c) 2011 mschiffer()universe-factory.net "I'm the one to blame for any problems with this version ;P"
+ */
+
+#ifndef _IP6T_NPTV6_H
+#define _IP6T_NPTV6_H
+
+struct ip6t_nptv6_info {
+ struct in6_addr nptv6_prefix; /* Adress prefix to map to/from */
+ u_int16_t nptv6_prefix_len; /* Address prefix length */
+};
+
+#endif /*_IP6T_NPTV6_H*/
diff --git a/ip6t_NPTV6_common.h b/ip6t_NPTV6_common.h
new file mode 100644
index 0000000..86b297c
--- /dev/null
+++ b/ip6t_NPTV6_common.h
@@ -0,0 +1,88 @@
+/*
+ * NATv6: IPv6-to-IPv6 Network Prefix Translation as
+ * proposed in RFC 6296.
+ * Based on MAP66 (c) 2010 sven-ola()gmx.de
+ * (c) 2011 mschiffer()universe-factory.net "I'm the one to blame for any problems with this version ;P"
+ */
+
+#ifndef _IP6T_NPTV6_COMMON_H
+#define _IP6T_NPTV6_COMMON_H
+
+#include "ip6t_NPTV6.h"
+#include <net/ipv6.h>
+
+
+#ifndef pr_devel
+# ifdef DEBUG
+# define pr_devel(fmt, ...) printk(fmt, ##__VA_ARGS__)
+# else
+# define pr_devel(fmt, ...) ({ if (0); })
+# endif
+#endif
+
+#ifndef NIP6_FMT
+# define NIP6_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
+# define NIP6(addr) \
+ ntohs((addr).s6_addr16[0]), \
+ ntohs((addr).s6_addr16[1]), \
+ ntohs((addr).s6_addr16[2]), \
+ ntohs((addr).s6_addr16[3]), \
+ ntohs((addr).s6_addr16[4]), \
+ ntohs((addr).s6_addr16[5]), \
+ ntohs((addr).s6_addr16[6]), \
+ ntohs((addr).s6_addr16[7])
+#endif
+
+
+/* Ones' complement add */
+static inline u_int16_t add16(u_int16_t a, u_int16_t b)
+{
+ a += b;
+ return a + (a < b);
+}
+
+static bool translate_address(struct in6_addr *addr, const struct in6_addr *prefix, u_int16_t plen) {
+ u_int16_t csum = 0;
+ int o = plen / 16, b = plen % 16;
+ int i;
+
+ for (i = 0; i < o; i++) {
+ csum = add16(csum, addr->s6_addr16[i]);
+ csum = add16(csum, ~prefix->s6_addr16[i]);
+
+ addr->s6_addr16[i] = prefix->s6_addr16[i];
+ }
+
+ if (b != 0) {
+ u_int16_t bmask = 0xffff << (16-b);
+ csum = add16(csum, addr->s6_addr16[o] & bmask);
+ csum = add16(csum, ~(prefix->s6_addr16[o] & bmask));
+
+ addr->s6_addr16[o] &= bmask;
+ addr->s6_addr16[o] |= (prefix->s6_addr16[o] & bmask);
+ }
+
+ /* Fix checksum like specified in RFC 6296 */
+ if (plen <= 48) {
+ i = 3;
+ }
+ else {
+ for (i = (plen+15)/16; i < 7; i++) {
+ if (addr->s6_addr16[i] != 0xffff)
+ break;
+ }
+ }
+
+ if (addr->s6_addr16[i] == 0xffff) {
+ return false;
+ }
+
+ addr->s6_addr16[i] = add16(addr->s6_addr16[i], csum);
+ if (addr->s6_addr16[i] == 0xffff) {
+ addr->s6_addr16[i] = 0;
+ }
+
+ return true;
+}
+
+#endif /*_IP6T_NPTV6_COMMON_H*/
diff --git a/ip6t_SNPTV6.c b/ip6t_SNPTV6.c
new file mode 100644
index 0000000..5651382
--- /dev/null
+++ b/ip6t_SNPTV6.c
@@ -0,0 +1,83 @@
+/*
+ * NATv6: IPv6-to-IPv6 Network Prefix Translation as
+ * proposed in RFC 6296.
+ * Based on MAP66 (c) 2010 sven-ola()gmx.de
+ * (c) 2011 mschiffer()universe-factory.net "I'm the one to blame for any problems with this version ;P"
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <net/ipv6.h>
+
+#include "ip6t_NPTV6_common.h"
+
+MODULE_AUTHOR("Matthias Schiffer <mschiffer()universe-factory.net>");
+MODULE_DESCRIPTION("Xtables: Source NPTv6 - IPv6-to-IPv6 Network Prefix Translation");
+MODULE_LICENSE("GPL");
+
+
+static unsigned int snptv6_tg6(struct sk_buff *skb, const struct xt_action_param *par)
+{
+ struct ipv6hdr* hdr = ipv6_hdr(skb);
+ const struct ip6t_nptv6_info *info = par->targinfo;
+
+ pr_devel("SNPTV6: enter 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));
+
+ if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) {
+ pr_devel("SNPTV6: unwriteable, dropped\n");
+ return NF_DROP;
+ }
+ hdr = ipv6_hdr(skb);
+
+ if (!translate_address(&hdr->saddr, &info->nptv6_prefix, info->nptv6_prefix_len)) {
+ pr_devel("SNPTV6: untranslatable address\n");
+ return NF_DROP;
+ }
+
+ pr_devel("SNPTV6: exit 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));
+
+ return NF_ACCEPT;
+}
+
+static int snptv6_tg6_check(const struct xt_tgchk_param *par)
+{
+ const struct ip6t_nptv6_info *info = par->targinfo;
+
+ if (info->nptv6_prefix_len > 64) {
+ printk("SNPTV6: Prefix length longer than 64 given\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct xt_target snptv6_tg6_reg __read_mostly = {
+ .name = "SNPTV6",
+ .family = NFPROTO_IPV6,
+ .target = snptv6_tg6,
+ .checkentry = snptv6_tg6_check,
+ .targetsize = sizeof(struct ip6t_nptv6_info),
+ .table = "mangle",
+ .hooks = (1 << NF_INET_POST_ROUTING),
+ .me = THIS_MODULE,
+};
+
+static int __init snptv6_tg6_init(void)
+{
+ return xt_register_target(&snptv6_tg6_reg);
+}
+
+static void __exit snptv6_tg6_exit(void)
+{
+ xt_unregister_target(&snptv6_tg6_reg);
+}
+
+module_init(snptv6_tg6_init);
+module_exit(snptv6_tg6_exit);
diff --git a/libip6t_DNPTV6.c b/libip6t_DNPTV6.c
new file mode 100644
index 0000000..ad937ae
--- /dev/null
+++ b/libip6t_DNPTV6.c
@@ -0,0 +1,46 @@
+/*
+ * NATv6: IPv6-to-IPv6 Network Prefix Translation as
+ * proposed in RFC 6296.
+ * Based on MAP66 (c) 2010 sven-ola()gmx.de
+ * (c) 2011 mschiffer()universe-factory.net "I'm the one to blame for any problems with this version ;P"
+ */
+
+#include "libip6t_NPTV6.h"
+
+
+static void DNPTV6_help(void)
+{
+ printf(
+"DNPTV6 target options\n"
+" --to-destination ipv6addr/prefixlength (Prefix to map IPv6 destination address to)\n");
+ NPTV6_help_note();
+}
+
+static void DNPTV6_save(const void *ip, const struct xt_entry_target *target)
+{
+ const struct ip6t_nptv6_info* info = (struct ip6t_nptv6_info*)target->data;
+ printf(" --to-destination %s/%d ", xtables_ip6addr_to_numeric(&info->nptv6_prefix), info->nptv6_prefix_len);
+}
+
+static const struct xt_option_entry DNPTV6_opts[] = {
+ {.name = "to-destination", .id = 0, .type = XTTYPE_STRING, .flags = XTOPT_MAND},
+ XTOPT_TABLEEND,
+};
+
+static struct xtables_target DNPTV6_tg6_reg = {
+ .name = "DNPTV6",
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_IPV6,
+ .size = XT_ALIGN(sizeof(struct ip6t_nptv6_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct ip6t_nptv6_info)),
+ .help = DNPTV6_help,
+ .x6_parse = NPTV6_parse,
+ .print = NPTV6_print,
+ .save = DNPTV6_save,
+ .x6_options = DNPTV6_opts,
+};
+
+void _init(void)
+{
+ xtables_register_target(&DNPTV6_tg6_reg);
+}
diff --git a/libip6t_MAP66.c b/libip6t_MAP66.c
deleted file mode 100644
index 466e5d0..0000000
--- a/libip6t_MAP66.c
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * MAP66: Network Address Translation IPv6-to-IPv6 as
- * proposed in the IETF's second NAT66 draft document.
- * (c) 2010 sven-ola()gmx.de
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <getopt.h>
-#include <arpa/inet.h>
-
-#define IPTABLES_VERSION_CMP(a,b,c) (((a) << 16) + ((b) << 8) + (c))
-
-#if IPTABLES_VERSION_CODE < IPTABLES_VERSION_CMP(1,4,0)
-# include <ip6tables.h>
-# define xt_entry_target ip6t_entry_target
-# define void_entry struct ip6t_entry
-# define void_ip6 struct ip6t_ip6
-#else
-# include <xtables.h>
-# define void_entry void
-# define void_ip6 void
-#endif
-
-#if IPTABLES_VERSION_CODE < IPTABLES_VERSION_CMP(1,4,1)
-# define xtables_target ip6tables_target
-# define XTABLES_VERSION IPTABLES_VERSION
-# define xtables_register_target register_target6
-#endif
-
-#if IPTABLES_VERSION_CODE < IPTABLES_VERSION_CMP(1,4,3)
-# define xtables_error exit_error
-# define xtables_check_inverse check_inverse
-# define NFPROTO_IPV6 PF_INET6
-#endif
-
-#ifndef XT_ALIGN
-# define XT_ALIGN IP6T_ALIGN
-#endif
-
-#include "ip6t_MAP66.h"
-
-/* 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;
-}
-
-static void MAP66_help(void)
-{
- printf(
-"MAP66 target options\n"
-" --" IP6T_MAP66_DST_TO " ipv6addr/prefixlength (Prefix to map IPv6 destination address to)\n"
-" --" IP6T_MAP66_SRC_TO " ipv6addr/prefixlength (Prefix to map IPv6 source address to)\n"
-" --nocheck (Disables the do-not-map-to-my-addr check)\n"
-" --csum (No csum neutral address change, calc csum)\n"
-"\n"
-"Note: you need two ip6tables rules to map an internal network\n"
-"using ULAs to/from external network with official IPv6 address.\n"
-"\n"
-"Example:\n"
-"\n"
-"ip6tables -t mangle -I PREROUTING -i eth0 -d 2001:0DB8:0001::/48 -j MAP66 --" IP6T_MAP66_DST_TO " FD01:0203:0405::/48\n"
-"ip6tables -t mangle -I POSTROUTING -o eth0 -s FD01:0203:0405::/48 -j MAP66 --" IP6T_MAP66_SRC_TO " 2001:0DB8:0001::/48\n");
-}
-
-static int MAP66_parse(
- int c,
- char **argv,
- int invert,
- unsigned int *flags,
- const void_entry *entry,
- struct xt_entry_target **target)
-{
- int i;
- char *p;
- struct ip6t_MAP66_info* info = (struct ip6t_MAP66_info*)(*target)->data;
-
- switch(c) {
- case '1':
- if (!optarg) {
- xtables_error(PARAMETER_PROBLEM, "--" IP6T_MAP66_DST_TO ": You must specify a value");
- }
- if (xtables_check_inverse(optarg, &invert, NULL, 0
-#if IPTABLES_VERSION_CODE >= IPTABLES_VERSION_CMP(1,4,6)
- ,argv
-#endif
- )) {
- xtables_error(PARAMETER_PROBLEM, "Unexpected `!' after --" IP6T_MAP66_DST_TO);
- }
- if (0 != (IP6T_MAP66_OPT_DST_TO & *flags)) {
- xtables_error(PARAMETER_PROBLEM, "Multiple --" IP6T_MAP66_DST_TO " not supported");
- }
- *flags |= IP6T_MAP66_OPT_DST_TO;
- info->mapflags |= IP6T_MAP66_OPT_DST_TO;
- if (NULL == (p = strchr(optarg, '/'))) {
- xtables_error(PARAMETER_PROBLEM, "Missing '/' character in --" IP6T_MAP66_DST_TO ": \"%s\"", optarg);
- }
- *p = '\0';
- if (1 != inet_pton(AF_INET6, optarg, &info->pfix_dst_to)) {
- xtables_error(PARAMETER_PROBLEM, "Invalid IPv6 address in --" IP6T_MAP66_DST_TO ": \"%s\"", optarg);
- }
- i = atoi(p + 1);
- if (0 >= i || 128 < i || 0 != i % 16) {
- xtables_error(PARAMETER_PROBLEM, "Invalid prefix length in --" IP6T_MAP66_DST_TO ": \"%s\" (use /128, /112, /96 .. /16)", p + 1);
- }
- info->pfix_dst_len = i / 16;
- info->pfix_dst_csum = csum16((const u_int16_t *)&info->pfix_dst_to, info->pfix_dst_len);
- return 1;
- break;
- case '2':
- if (!optarg) {
- xtables_error(PARAMETER_PROBLEM, "--" IP6T_MAP66_SRC_TO ": You must specify a value");
- }
- if (xtables_check_inverse(optarg, &invert, NULL, 0
-#if IPTABLES_VERSION_CODE >= IPTABLES_VERSION_CMP(1,4,6)
- ,argv
-#endif
- )) {
- xtables_error(PARAMETER_PROBLEM, "Unexpected `!' after --" IP6T_MAP66_SRC_TO);
- }
- if (0 != (IP6T_MAP66_OPT_SRC_TO & *flags)) {
- xtables_error(PARAMETER_PROBLEM, "Multiple --" IP6T_MAP66_SRC_TO " not supported");
- }
- *flags |= IP6T_MAP66_OPT_SRC_TO;
- info->mapflags |= IP6T_MAP66_OPT_SRC_TO;
- if (NULL == (p = strchr(optarg, '/'))) {
- xtables_error(PARAMETER_PROBLEM, "Missing '/' character in --" IP6T_MAP66_SRC_TO ": \"%s\"", optarg);
- }
- *p = '\0';
- if (1 != inet_pton(AF_INET6, optarg, &info->pfix_src_to)) {
- xtables_error(PARAMETER_PROBLEM, "Invalid IPv6 address in --" IP6T_MAP66_SRC_TO ": \"%s\"", optarg);
- }
- i = atoi(p + 1);
- if (0 >= i || 128 < i || 0 != i % 16) {
- xtables_error(PARAMETER_PROBLEM, "Invalid prefix length in --" IP6T_MAP66_SRC_TO ": \"%s\" (use /128, /112, /96 .. /16)", p + 1);
- }
- info->pfix_src_len = i / 16;
- info->pfix_src_csum = csum16((const u_int16_t *)&info->pfix_src_to, info->pfix_src_len);
- return 1;
- break;
- case '3':
- if (0 != (IP6T_MAP66_OPT_NOCHECK & *flags)) {
- xtables_error(PARAMETER_PROBLEM, "Multiple --nocheck not supported");
- }
- info->mapflags |= IP6T_MAP66_OPT_NOCHECK;
- *flags |= IP6T_MAP66_OPT_NOCHECK;
- return 1;
- break;
- case '4':
- if (0 != (IP6T_MAP66_OPT_CSUM & *flags)) {
- xtables_error(PARAMETER_PROBLEM, "Multiple --csum not supported");
- }
- info->mapflags |= IP6T_MAP66_OPT_CSUM;
- *flags |= IP6T_MAP66_OPT_CSUM;
- return 1;
- break;
- }
- return 0;
-}
-
-static void MAP66_check(unsigned int flags)
-{
- if (0 == ((IP6T_MAP66_OPT_DST_TO | IP6T_MAP66_OPT_SRC_TO) & flags)) {
- xtables_error(PARAMETER_PROBLEM, "You must specify --" IP6T_MAP66_DST_TO " or --" IP6T_MAP66_SRC_TO);
- }
-}
-
-static void MAP66_save(
- const void_ip6 *ip,
- const struct xt_entry_target *target)
-{
- char s[50+1];
- const struct ip6t_MAP66_info* info = (struct ip6t_MAP66_info*)target->data;
- if (0 != (IP6T_MAP66_OPT_DST_TO & info->mapflags)) {
- printf("--" IP6T_MAP66_DST_TO " %s/%d ", inet_ntop(AF_INET6, &info->pfix_dst_to, s, sizeof(s)), 16 * info->pfix_dst_len);
- }
- if (0 != (IP6T_MAP66_OPT_SRC_TO & info->mapflags)) {
- printf("--" IP6T_MAP66_SRC_TO " %s/%d ", inet_ntop(AF_INET6, &info->pfix_src_to, s, sizeof(s)), 16 * info->pfix_src_len);
- }
- if (0 != (IP6T_MAP66_OPT_NOCHECK & info->mapflags)) {
- printf("--nocheck ");
- }
- if (0 != (IP6T_MAP66_OPT_CSUM & info->mapflags)) {
- printf("--csum ");
- }
-}
-
-static struct option MAP66_opts[] = {
- { .name = IP6T_MAP66_DST_TO, .has_arg = 1, .flag = NULL, .val = '1' },
- { .name = IP6T_MAP66_SRC_TO, .has_arg = 1, .flag = NULL, .val = '2' },
- { .name = "nocheck", .has_arg = 0, .flag = NULL, .val = '3' },
- { .name = "csum", .has_arg = 0, .flag = NULL, .val = '4' },
- { .name = NULL }
-};
-
-static struct xtables_target MAP66_tg6_reg = {
- .name = "MAP66",
- .version = XTABLES_VERSION,
-#if IPTABLES_VERSION_CODE >= IPTABLES_VERSION_CMP(1,4,1)
- .family = NFPROTO_IPV6,
-#endif
- .size = XT_ALIGN(sizeof(struct ip6t_MAP66_info)),
- .userspacesize = XT_ALIGN(sizeof(struct ip6t_MAP66_info)),
- .help = MAP66_help,
- .parse = MAP66_parse,
- .final_check = MAP66_check,
- .save = MAP66_save,
- .extra_opts = MAP66_opts,
-};
-
-void _init(void)
-{
- xtables_register_target(&MAP66_tg6_reg);
-}
diff --git a/libip6t_NPTV6.c b/libip6t_NPTV6.c
new file mode 100644
index 0000000..4c7aae7
--- /dev/null
+++ b/libip6t_NPTV6.c
@@ -0,0 +1,62 @@
+/*
+ * NATv6: IPv6-to-IPv6 Network Prefix Translation as
+ * proposed in RFC 6296.
+ * Based on MAP66 (c) 2010 sven-ola()gmx.de
+ * (c) 2011 mschiffer()universe-factory.net "I'm the one to blame for any problems with this version ;P"
+ */
+
+#include "libip6t_NPTV6.h"
+
+
+void NPTV6_help_note(void)
+{
+ printf(
+"\n"
+"Note: you need two ip6tables rules to map an internal network\n"
+"using ULAs to/from external network with official IPv6 address.\n"
+"\n"
+"Example:\n"
+"\n"
+"ip6tables -t mangle -I PREROUTING -i eth0 -d 2001:0DB8:0001::/48 -j DNPTV6 --to-destination FD01:0203:0405::/48\n"
+"ip6tables -t mangle -I POSTROUTING -o eth0 -s FD01:0203:0405::/48 -j SNPTV6 --to-source 2001:0DB8:0001::/48\n");
+}
+
+static void parse_to(const char *orig_arg, struct ip6t_nptv6_info *info)
+{
+ char *arg, *slash;
+
+ arg = strdup(orig_arg);
+ if (arg == NULL)
+ xtables_error(RESOURCE_PROBLEM, "strdup");
+
+ slash = strchr(arg, '/');
+ if (!slash)
+ xtables_error(PARAMETER_PROBLEM, "No prefix length given\n");
+
+ info->nptv6_prefix_len = atoi(slash+1);
+ if (info->nptv6_prefix_len <= 0 || info->nptv6_prefix_len > 64)
+ xtables_error(PARAMETER_PROBLEM, "Prefix length `%s' not valid\n", slash+1);
+
+ *slash = 0;
+ info->nptv6_prefix = *xtables_numeric_to_ip6addr(arg);
+
+ free(arg);
+}
+
+void NPTV6_parse(struct xt_option_call *cb)
+{
+ struct ip6t_nptv6_info* info = (struct ip6t_nptv6_info*)cb->data;
+
+ xtables_option_parse(cb);
+ switch(cb->entry->id) {
+ case 0:
+ parse_to(cb->arg, info);
+ break;
+ }
+}
+
+void NPTV6_print(const void *ip, const struct xt_entry_target *target, int numeric)
+{
+ const struct ip6t_nptv6_info* info = (struct ip6t_nptv6_info*)target->data;
+ printf(" to: %s/%d", xtables_ip6addr_to_numeric(&info->nptv6_prefix), info->nptv6_prefix_len);
+}
diff --git a/libip6t_NPTV6.h b/libip6t_NPTV6.h
new file mode 100644
index 0000000..d091de6
--- /dev/null
+++ b/libip6t_NPTV6.h
@@ -0,0 +1,29 @@
+/*
+ * NATv6: IPv6-to-IPv6 Network Prefix Translation as
+ * proposed in RFC 6296.
+ * Based on MAP66 (c) 2010 sven-ola()gmx.de
+ * (c) 2011 mschiffer()universe-factory.net "I'm the one to blame for any problems with this version ;P"
+ */
+
+#ifndef _LIBIP6T_NPTV6_H
+#define _LIBIP6T_NPTV6_H
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+#include "ip6t_NPTV6.h"
+
+
+void NPTV6_help_note(void);
+void NPTV6_parse(struct xt_option_call *cb);
+void NPTV6_print(const void *ip, const struct xt_entry_target *target, int numeric);
+
+void _init(void);
+
+#endif /*_LIBIP6T_NPTV6_H*/
diff --git a/libip6t_SNPTV6.c b/libip6t_SNPTV6.c
new file mode 100644
index 0000000..3bcc66e
--- /dev/null
+++ b/libip6t_SNPTV6.c
@@ -0,0 +1,46 @@
+/*
+ * NATv6: IPv6-to-IPv6 Network Prefix Translation as
+ * proposed in RFC 6296.
+ * Based on MAP66 (c) 2010 sven-ola()gmx.de
+ * (c) 2011 mschiffer()universe-factory.net "I'm the one to blame for any problems with this version ;P"
+ */
+
+#include "libip6t_NPTV6.h"
+
+
+static void SNPTV6_help(void)
+{
+ printf(
+"SNPTV6 target options\n"
+" --to-source ipv6addr/prefixlength (Prefix to map IPv6 source address to)\n");
+ NPTV6_help_note();
+}
+
+static void SNPTV6_save(const void *ip, const struct xt_entry_target *target)
+{
+ const struct ip6t_nptv6_info* info = (struct ip6t_nptv6_info*)target->data;
+ printf(" --to-source %s/%d ", xtables_ip6addr_to_numeric(&info->nptv6_prefix), info->nptv6_prefix_len);
+}
+
+static const struct xt_option_entry SNPTV6_opts[] = {
+ {.name = "to-source", .id = 0, .type = XTTYPE_STRING, .flags = XTOPT_MAND},
+ XTOPT_TABLEEND,
+};
+
+static struct xtables_target SNPTV6_tg6_reg = {
+ .name = "SNPTV6",
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_IPV6,
+ .size = XT_ALIGN(sizeof(struct ip6t_nptv6_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct ip6t_nptv6_info)),
+ .help = SNPTV6_help,
+ .x6_parse = NPTV6_parse,
+ .print = NPTV6_print,
+ .save = SNPTV6_save,
+ .x6_options = SNPTV6_opts,
+};
+
+void _init(void)
+{
+ xtables_register_target(&SNPTV6_tg6_reg);
+}