]>
Commit | Line | Data |
---|---|---|
8a91bb0c PM |
1 | /* |
2 | * Copyright (c) 2011, 2012 Patrick McHardy <kaber@trash.net> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/module.h> | |
10 | #include <linux/skbuff.h> | |
11 | #include <linux/ipv6.h> | |
12 | #include <linux/netfilter.h> | |
13 | #include <linux/netfilter_ipv6.h> | |
14 | #include <linux/netfilter_ipv6/ip6t_NPT.h> | |
15 | #include <linux/netfilter/x_tables.h> | |
16 | ||
8a91bb0c PM |
17 | static int ip6t_npt_checkentry(const struct xt_tgchk_param *par) |
18 | { | |
19 | struct ip6t_npt_tginfo *npt = par->targinfo; | |
429da4c0 | 20 | __wsum src_sum = 0, dst_sum = 0; |
8a91bb0c PM |
21 | unsigned int i; |
22 | ||
23 | if (npt->src_pfx_len > 64 || npt->dst_pfx_len > 64) | |
24 | return -EINVAL; | |
25 | ||
26 | for (i = 0; i < ARRAY_SIZE(npt->src_pfx.in6.s6_addr16); i++) { | |
429da4c0 UW |
27 | src_sum = csum_add(src_sum, |
28 | (__force __wsum)npt->src_pfx.in6.s6_addr16[i]); | |
29 | dst_sum = csum_add(dst_sum, | |
30 | (__force __wsum)npt->dst_pfx.in6.s6_addr16[i]); | |
8a91bb0c PM |
31 | } |
32 | ||
429da4c0 | 33 | npt->adjustment = (__force __sum16) csum_sub(src_sum, dst_sum); |
8a91bb0c PM |
34 | return 0; |
35 | } | |
36 | ||
37 | static bool ip6t_npt_map_pfx(const struct ip6t_npt_tginfo *npt, | |
38 | struct in6_addr *addr) | |
39 | { | |
40 | unsigned int pfx_len; | |
41 | unsigned int i, idx; | |
42 | __be32 mask; | |
43 | __sum16 sum; | |
44 | ||
45 | pfx_len = max(npt->src_pfx_len, npt->dst_pfx_len); | |
46 | for (i = 0; i < pfx_len; i += 32) { | |
47 | if (pfx_len - i >= 32) | |
48 | mask = 0; | |
49 | else | |
50 | mask = htonl(~((1 << (pfx_len - i)) - 1)); | |
51 | ||
52 | idx = i / 32; | |
53 | addr->s6_addr32[idx] &= mask; | |
54 | addr->s6_addr32[idx] |= npt->dst_pfx.in6.s6_addr32[idx]; | |
55 | } | |
56 | ||
57 | if (pfx_len <= 48) | |
58 | idx = 3; | |
59 | else { | |
60 | for (idx = 4; idx < ARRAY_SIZE(addr->s6_addr16); idx++) { | |
61 | if ((__force __sum16)addr->s6_addr16[idx] != | |
62 | CSUM_MANGLED_0) | |
63 | break; | |
64 | } | |
65 | if (idx == ARRAY_SIZE(addr->s6_addr16)) | |
66 | return false; | |
67 | } | |
68 | ||
429da4c0 | 69 | sum = (__force __sum16) csum_add((__force __wsum)addr->s6_addr16[idx], |
8a91bb0c PM |
70 | npt->adjustment); |
71 | if (sum == CSUM_MANGLED_0) | |
72 | sum = 0; | |
73 | *(__force __sum16 *)&addr->s6_addr16[idx] = sum; | |
74 | ||
75 | return true; | |
76 | } | |
77 | ||
78 | static unsigned int | |
79 | ip6t_snpt_tg(struct sk_buff *skb, const struct xt_action_param *par) | |
80 | { | |
81 | const struct ip6t_npt_tginfo *npt = par->targinfo; | |
82 | ||
83 | if (!ip6t_npt_map_pfx(npt, &ipv6_hdr(skb)->saddr)) { | |
84 | icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_HDR_FIELD, | |
85 | offsetof(struct ipv6hdr, saddr)); | |
86 | return NF_DROP; | |
87 | } | |
88 | return XT_CONTINUE; | |
89 | } | |
90 | ||
91 | static unsigned int | |
92 | ip6t_dnpt_tg(struct sk_buff *skb, const struct xt_action_param *par) | |
93 | { | |
94 | const struct ip6t_npt_tginfo *npt = par->targinfo; | |
95 | ||
96 | if (!ip6t_npt_map_pfx(npt, &ipv6_hdr(skb)->daddr)) { | |
97 | icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_HDR_FIELD, | |
98 | offsetof(struct ipv6hdr, daddr)); | |
99 | return NF_DROP; | |
100 | } | |
101 | return XT_CONTINUE; | |
102 | } | |
103 | ||
104 | static struct xt_target ip6t_npt_target_reg[] __read_mostly = { | |
105 | { | |
106 | .name = "SNPT", | |
107 | .target = ip6t_snpt_tg, | |
108 | .targetsize = sizeof(struct ip6t_npt_tginfo), | |
109 | .checkentry = ip6t_npt_checkentry, | |
110 | .family = NFPROTO_IPV6, | |
111 | .hooks = (1 << NF_INET_LOCAL_IN) | | |
112 | (1 << NF_INET_POST_ROUTING), | |
113 | .me = THIS_MODULE, | |
114 | }, | |
115 | { | |
116 | .name = "DNPT", | |
117 | .target = ip6t_dnpt_tg, | |
118 | .targetsize = sizeof(struct ip6t_npt_tginfo), | |
119 | .checkentry = ip6t_npt_checkentry, | |
120 | .family = NFPROTO_IPV6, | |
121 | .hooks = (1 << NF_INET_PRE_ROUTING) | | |
122 | (1 << NF_INET_LOCAL_OUT), | |
123 | .me = THIS_MODULE, | |
124 | }, | |
125 | }; | |
126 | ||
127 | static int __init ip6t_npt_init(void) | |
128 | { | |
129 | return xt_register_targets(ip6t_npt_target_reg, | |
130 | ARRAY_SIZE(ip6t_npt_target_reg)); | |
131 | } | |
132 | ||
133 | static void __exit ip6t_npt_exit(void) | |
134 | { | |
135 | xt_unregister_targets(ip6t_npt_target_reg, | |
136 | ARRAY_SIZE(ip6t_npt_target_reg)); | |
137 | } | |
138 | ||
139 | module_init(ip6t_npt_init); | |
140 | module_exit(ip6t_npt_exit); | |
141 | ||
142 | MODULE_LICENSE("GPL"); | |
143 | MODULE_DESCRIPTION("IPv6-to-IPv6 Network Prefix Translation (RFC 6296)"); | |
144 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | |
145 | MODULE_ALIAS("ip6t_SNPT"); | |
146 | MODULE_ALIAS("ip6t_DNPT"); |