Commit | Line | Data |
---|---|---|
b3d54b3e JE |
1 | /* |
2 | * (C) 2000-2001 Svenning Soerensen <svenning@post5.tele.dk> | |
3 | * Copyright (c) 2011 Patrick McHardy <kaber@trash.net> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | */ | |
9 | ||
10 | #include <linux/ip.h> | |
11 | #include <linux/kernel.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/netdevice.h> | |
14 | #include <linux/ipv6.h> | |
15 | #include <linux/netfilter.h> | |
16 | #include <linux/netfilter_ipv4.h> | |
17 | #include <linux/netfilter_ipv6.h> | |
18 | #include <linux/netfilter/x_tables.h> | |
19 | #include <net/netfilter/nf_nat.h> | |
20 | ||
21 | static unsigned int | |
22 | netmap_tg6(struct sk_buff *skb, const struct xt_action_param *par) | |
23 | { | |
24 | const struct nf_nat_range *range = par->targinfo; | |
25 | struct nf_nat_range newrange; | |
26 | struct nf_conn *ct; | |
27 | enum ip_conntrack_info ctinfo; | |
28 | union nf_inet_addr new_addr, netmask; | |
29 | unsigned int i; | |
30 | ||
31 | ct = nf_ct_get(skb, &ctinfo); | |
32 | for (i = 0; i < ARRAY_SIZE(range->min_addr.ip6); i++) | |
33 | netmask.ip6[i] = ~(range->min_addr.ip6[i] ^ | |
34 | range->max_addr.ip6[i]); | |
35 | ||
36 | if (par->hooknum == NF_INET_PRE_ROUTING || | |
37 | par->hooknum == NF_INET_LOCAL_OUT) | |
38 | new_addr.in6 = ipv6_hdr(skb)->daddr; | |
39 | else | |
40 | new_addr.in6 = ipv6_hdr(skb)->saddr; | |
41 | ||
42 | for (i = 0; i < ARRAY_SIZE(new_addr.ip6); i++) { | |
43 | new_addr.ip6[i] &= ~netmask.ip6[i]; | |
44 | new_addr.ip6[i] |= range->min_addr.ip6[i] & | |
45 | netmask.ip6[i]; | |
46 | } | |
47 | ||
48 | newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS; | |
49 | newrange.min_addr = new_addr; | |
50 | newrange.max_addr = new_addr; | |
51 | newrange.min_proto = range->min_proto; | |
52 | newrange.max_proto = range->max_proto; | |
53 | ||
54 | return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(par->hooknum)); | |
55 | } | |
56 | ||
57 | static int netmap_tg6_checkentry(const struct xt_tgchk_param *par) | |
58 | { | |
59 | const struct nf_nat_range *range = par->targinfo; | |
60 | ||
61 | if (!(range->flags & NF_NAT_RANGE_MAP_IPS)) | |
62 | return -EINVAL; | |
63 | return 0; | |
64 | } | |
65 | ||
66 | static unsigned int | |
67 | netmap_tg4(struct sk_buff *skb, const struct xt_action_param *par) | |
68 | { | |
69 | struct nf_conn *ct; | |
70 | enum ip_conntrack_info ctinfo; | |
71 | __be32 new_ip, netmask; | |
72 | const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; | |
73 | struct nf_nat_range newrange; | |
74 | ||
75 | NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || | |
76 | par->hooknum == NF_INET_POST_ROUTING || | |
77 | par->hooknum == NF_INET_LOCAL_OUT || | |
78 | par->hooknum == NF_INET_LOCAL_IN); | |
79 | ct = nf_ct_get(skb, &ctinfo); | |
80 | ||
81 | netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip); | |
82 | ||
83 | if (par->hooknum == NF_INET_PRE_ROUTING || | |
84 | par->hooknum == NF_INET_LOCAL_OUT) | |
85 | new_ip = ip_hdr(skb)->daddr & ~netmask; | |
86 | else | |
87 | new_ip = ip_hdr(skb)->saddr & ~netmask; | |
88 | new_ip |= mr->range[0].min_ip & netmask; | |
89 | ||
90 | memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); | |
91 | memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); | |
92 | newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS; | |
93 | newrange.min_addr.ip = new_ip; | |
94 | newrange.max_addr.ip = new_ip; | |
95 | newrange.min_proto = mr->range[0].min; | |
96 | newrange.max_proto = mr->range[0].max; | |
97 | ||
98 | /* Hand modified range to generic setup. */ | |
99 | return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(par->hooknum)); | |
100 | } | |
101 | ||
102 | static int netmap_tg4_check(const struct xt_tgchk_param *par) | |
103 | { | |
104 | const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; | |
105 | ||
106 | if (!(mr->range[0].flags & NF_NAT_RANGE_MAP_IPS)) { | |
107 | pr_debug("bad MAP_IPS.\n"); | |
108 | return -EINVAL; | |
109 | } | |
110 | if (mr->rangesize != 1) { | |
111 | pr_debug("bad rangesize %u.\n", mr->rangesize); | |
112 | return -EINVAL; | |
113 | } | |
114 | return 0; | |
115 | } | |
116 | ||
117 | static struct xt_target netmap_tg_reg[] __read_mostly = { | |
118 | { | |
119 | .name = "NETMAP", | |
120 | .family = NFPROTO_IPV6, | |
121 | .revision = 0, | |
122 | .target = netmap_tg6, | |
123 | .targetsize = sizeof(struct nf_nat_range), | |
124 | .table = "nat", | |
125 | .hooks = (1 << NF_INET_PRE_ROUTING) | | |
126 | (1 << NF_INET_POST_ROUTING) | | |
127 | (1 << NF_INET_LOCAL_OUT) | | |
128 | (1 << NF_INET_LOCAL_IN), | |
129 | .checkentry = netmap_tg6_checkentry, | |
130 | .me = THIS_MODULE, | |
131 | }, | |
132 | { | |
133 | .name = "NETMAP", | |
134 | .family = NFPROTO_IPV4, | |
135 | .revision = 0, | |
136 | .target = netmap_tg4, | |
137 | .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), | |
138 | .table = "nat", | |
139 | .hooks = (1 << NF_INET_PRE_ROUTING) | | |
140 | (1 << NF_INET_POST_ROUTING) | | |
141 | (1 << NF_INET_LOCAL_OUT) | | |
142 | (1 << NF_INET_LOCAL_IN), | |
143 | .checkentry = netmap_tg4_check, | |
144 | .me = THIS_MODULE, | |
145 | }, | |
146 | }; | |
147 | ||
148 | static int __init netmap_tg_init(void) | |
149 | { | |
150 | return xt_register_targets(netmap_tg_reg, ARRAY_SIZE(netmap_tg_reg)); | |
151 | } | |
152 | ||
153 | static void netmap_tg_exit(void) | |
154 | { | |
155 | xt_unregister_targets(netmap_tg_reg, ARRAY_SIZE(netmap_tg_reg)); | |
156 | } | |
157 | ||
158 | module_init(netmap_tg_init); | |
159 | module_exit(netmap_tg_exit); | |
160 | ||
161 | MODULE_LICENSE("GPL"); | |
162 | MODULE_DESCRIPTION("Xtables: 1:1 NAT mapping of subnets"); | |
163 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | |
164 | MODULE_ALIAS("ip6t_NETMAP"); | |
165 | MODULE_ALIAS("ipt_NETMAP"); |