summaryrefslogtreecommitdiffstats
path: root/ip6t_SNPTV6.c
blob: 378ad8a249850b84446b5b136b7efe97fba12c73 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/*
 * 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/icmpv6.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");
		icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_NOT_NEIGHBOUR, 0);
		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 CHECKENTRY_RET 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 CHECKENTRY_RET_EINVAL;
	}

	return CHECKENTRY_RET_SUCCESS;
}

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) | (1 << NF_INET_LOCAL_IN),
	.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);