Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ip6t_NPT.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011, 2012 Patrick McHardy <[email protected]>
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>
15 #include <linux/netfilter/x_tables.h>
16 
17 static __sum16 csum16_complement(__sum16 a)
18 {
19  return (__force __sum16)(0xffff - (__force u16)a);
20 }
21 
22 static __sum16 csum16_add(__sum16 a, __sum16 b)
23 {
24  u16 sum;
25 
26  sum = (__force u16)a + (__force u16)b;
27  sum += (__force u16)a < (__force u16)b;
28  return (__force __sum16)sum;
29 }
30 
31 static __sum16 csum16_sub(__sum16 a, __sum16 b)
32 {
33  return csum16_add(a, csum16_complement(b));
34 }
35 
36 static int ip6t_npt_checkentry(const struct xt_tgchk_param *par)
37 {
38  struct ip6t_npt_tginfo *npt = par->targinfo;
39  __sum16 src_sum = 0, dst_sum = 0;
40  unsigned int i;
41 
42  if (npt->src_pfx_len > 64 || npt->dst_pfx_len > 64)
43  return -EINVAL;
44 
45  for (i = 0; i < ARRAY_SIZE(npt->src_pfx.in6.s6_addr16); i++) {
46  src_sum = csum16_add(src_sum,
47  (__force __sum16)npt->src_pfx.in6.s6_addr16[i]);
48  dst_sum = csum16_add(dst_sum,
49  (__force __sum16)npt->dst_pfx.in6.s6_addr16[i]);
50  }
51 
52  npt->adjustment = csum16_sub(src_sum, dst_sum);
53  return 0;
54 }
55 
56 static bool ip6t_npt_map_pfx(const struct ip6t_npt_tginfo *npt,
57  struct in6_addr *addr)
58 {
59  unsigned int pfx_len;
60  unsigned int i, idx;
61  __be32 mask;
62  __sum16 sum;
63 
64  pfx_len = max(npt->src_pfx_len, npt->dst_pfx_len);
65  for (i = 0; i < pfx_len; i += 32) {
66  if (pfx_len - i >= 32)
67  mask = 0;
68  else
69  mask = htonl(~((1 << (pfx_len - i)) - 1));
70 
71  idx = i / 32;
72  addr->s6_addr32[idx] &= mask;
73  addr->s6_addr32[idx] |= npt->dst_pfx.in6.s6_addr32[idx];
74  }
75 
76  if (pfx_len <= 48)
77  idx = 3;
78  else {
79  for (idx = 4; idx < ARRAY_SIZE(addr->s6_addr16); idx++) {
80  if ((__force __sum16)addr->s6_addr16[idx] !=
82  break;
83  }
84  if (idx == ARRAY_SIZE(addr->s6_addr16))
85  return false;
86  }
87 
88  sum = csum16_add((__force __sum16)addr->s6_addr16[idx],
89  npt->adjustment);
90  if (sum == CSUM_MANGLED_0)
91  sum = 0;
92  *(__force __sum16 *)&addr->s6_addr16[idx] = sum;
93 
94  return true;
95 }
96 
97 static unsigned int
98 ip6t_snpt_tg(struct sk_buff *skb, const struct xt_action_param *par)
99 {
100  const struct ip6t_npt_tginfo *npt = par->targinfo;
101 
102  if (!ip6t_npt_map_pfx(npt, &ipv6_hdr(skb)->saddr)) {
104  offsetof(struct ipv6hdr, saddr));
105  return NF_DROP;
106  }
107  return XT_CONTINUE;
108 }
109 
110 static unsigned int
111 ip6t_dnpt_tg(struct sk_buff *skb, const struct xt_action_param *par)
112 {
113  const struct ip6t_npt_tginfo *npt = par->targinfo;
114 
115  if (!ip6t_npt_map_pfx(npt, &ipv6_hdr(skb)->daddr)) {
117  offsetof(struct ipv6hdr, daddr));
118  return NF_DROP;
119  }
120  return XT_CONTINUE;
121 }
122 
123 static struct xt_target ip6t_npt_target_reg[] __read_mostly = {
124  {
125  .name = "SNPT",
126  .target = ip6t_snpt_tg,
127  .targetsize = sizeof(struct ip6t_npt_tginfo),
128  .checkentry = ip6t_npt_checkentry,
129  .family = NFPROTO_IPV6,
130  .hooks = (1 << NF_INET_LOCAL_IN) |
131  (1 << NF_INET_POST_ROUTING),
132  .me = THIS_MODULE,
133  },
134  {
135  .name = "DNPT",
136  .target = ip6t_dnpt_tg,
137  .targetsize = sizeof(struct ip6t_npt_tginfo),
138  .checkentry = ip6t_npt_checkentry,
139  .family = NFPROTO_IPV6,
140  .hooks = (1 << NF_INET_PRE_ROUTING) |
141  (1 << NF_INET_LOCAL_OUT),
142  .me = THIS_MODULE,
143  },
144 };
145 
146 static int __init ip6t_npt_init(void)
147 {
148  return xt_register_targets(ip6t_npt_target_reg,
149  ARRAY_SIZE(ip6t_npt_target_reg));
150 }
151 
152 static void __exit ip6t_npt_exit(void)
153 {
154  xt_unregister_targets(ip6t_npt_target_reg,
155  ARRAY_SIZE(ip6t_npt_target_reg));
156 }
157 
158 module_init(ip6t_npt_init);
159 module_exit(ip6t_npt_exit);
160 
161 MODULE_LICENSE("GPL");
162 MODULE_DESCRIPTION("IPv6-to-IPv6 Network Prefix Translation (RFC 6296)");
163 MODULE_AUTHOR("Patrick McHardy <[email protected]>");
164 MODULE_ALIAS("ip6t_SNPT");
165 MODULE_ALIAS("ip6t_DNPT");