diff options
author | Patrick McHardy <kaber@trash.net> | 2006-12-03 01:09:41 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-12-03 01:09:41 -0500 |
commit | f09943fefe6b702e40893d35b4f10fd1064037fe (patch) | |
tree | b170d046ecf0642471bb3c55d8e1f316fe9e5ddc /net/ipv4 | |
parent | 92703eee4ccde3c55ee067a89c373e8a51a8adf9 (diff) |
[NETFILTER]: nf_conntrack/nf_nat: add PPTP helper port
Add nf_conntrack port of the PPtP conntrack/NAT helper. Since there seems
to be no IPv6-capable PPtP implementation the helper only support IPv4.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/netfilter/Kconfig | 10 | ||||
-rw-r--r-- | net/ipv4/netfilter/Makefile | 4 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_nat_pptp.c | 315 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_nat_proto_gre.c | 179 |
4 files changed, 508 insertions, 0 deletions
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 4555f721dfc1..c3327ac024de 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig | |||
@@ -484,6 +484,10 @@ config IP_NF_NAT_SNMP_BASIC | |||
484 | # <expr> '&&' <expr> (6) | 484 | # <expr> '&&' <expr> (6) |
485 | # | 485 | # |
486 | # (6) Returns the result of min(/expr/, /expr/). | 486 | # (6) Returns the result of min(/expr/, /expr/). |
487 | config NF_NAT_PROTO_GRE | ||
488 | tristate | ||
489 | depends on NF_NAT && NF_CT_PROTO_GRE | ||
490 | |||
487 | config IP_NF_NAT_FTP | 491 | config IP_NF_NAT_FTP |
488 | tristate | 492 | tristate |
489 | depends on IP_NF_IPTABLES && IP_NF_CONNTRACK && IP_NF_NAT | 493 | depends on IP_NF_IPTABLES && IP_NF_CONNTRACK && IP_NF_NAT |
@@ -528,6 +532,12 @@ config IP_NF_NAT_PPTP | |||
528 | default IP_NF_NAT if IP_NF_PPTP=y | 532 | default IP_NF_NAT if IP_NF_PPTP=y |
529 | default m if IP_NF_PPTP=m | 533 | default m if IP_NF_PPTP=m |
530 | 534 | ||
535 | config NF_NAT_PPTP | ||
536 | tristate | ||
537 | depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT | ||
538 | default NF_NAT && NF_CONNTRACK_PPTP | ||
539 | select NF_NAT_PROTO_GRE | ||
540 | |||
531 | config IP_NF_NAT_H323 | 541 | config IP_NF_NAT_H323 |
532 | tristate | 542 | tristate |
533 | depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n | 543 | depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n |
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 56733c370327..ef33ff2cdda9 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile | |||
@@ -54,6 +54,10 @@ obj-$(CONFIG_NF_NAT_AMANDA) += nf_nat_amanda.o | |||
54 | obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o | 54 | obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o |
55 | obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o | 55 | obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o |
56 | obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o | 56 | obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o |
57 | obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o | ||
58 | |||
59 | # NAT protocols (nf_nat) | ||
60 | obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o | ||
57 | 61 | ||
58 | # generic IP tables | 62 | # generic IP tables |
59 | obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o | 63 | obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o |
diff --git a/net/ipv4/netfilter/nf_nat_pptp.c b/net/ipv4/netfilter/nf_nat_pptp.c new file mode 100644 index 000000000000..0ae45b79a4eb --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_pptp.c | |||
@@ -0,0 +1,315 @@ | |||
1 | /* | ||
2 | * nf_nat_pptp.c | ||
3 | * | ||
4 | * NAT support for PPTP (Point to Point Tunneling Protocol). | ||
5 | * PPTP is a a protocol for creating virtual private networks. | ||
6 | * It is a specification defined by Microsoft and some vendors | ||
7 | * working with Microsoft. PPTP is built on top of a modified | ||
8 | * version of the Internet Generic Routing Encapsulation Protocol. | ||
9 | * GRE is defined in RFC 1701 and RFC 1702. Documentation of | ||
10 | * PPTP can be found in RFC 2637 | ||
11 | * | ||
12 | * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org> | ||
13 | * | ||
14 | * Development of this code funded by Astaro AG (http://www.astaro.com/) | ||
15 | * | ||
16 | * TODO: - NAT to a unique tuple, not to TCP source port | ||
17 | * (needs netfilter tuple reservation) | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/tcp.h> | ||
22 | |||
23 | #include <net/netfilter/nf_nat.h> | ||
24 | #include <net/netfilter/nf_nat_helper.h> | ||
25 | #include <net/netfilter/nf_nat_rule.h> | ||
26 | #include <net/netfilter/nf_conntrack_helper.h> | ||
27 | #include <net/netfilter/nf_conntrack_expect.h> | ||
28 | #include <linux/netfilter/nf_conntrack_proto_gre.h> | ||
29 | #include <linux/netfilter/nf_conntrack_pptp.h> | ||
30 | |||
31 | #define NF_NAT_PPTP_VERSION "3.0" | ||
32 | |||
33 | #define REQ_CID(req, off) (*(__be16 *)((char *)(req) + (off))) | ||
34 | |||
35 | MODULE_LICENSE("GPL"); | ||
36 | MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); | ||
37 | MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP"); | ||
38 | MODULE_ALIAS("ip_nat_pptp"); | ||
39 | |||
40 | #if 0 | ||
41 | extern const char *pptp_msg_name[]; | ||
42 | #define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \ | ||
43 | __FUNCTION__, ## args) | ||
44 | #else | ||
45 | #define DEBUGP(format, args...) | ||
46 | #endif | ||
47 | |||
48 | static void pptp_nat_expected(struct nf_conn *ct, | ||
49 | struct nf_conntrack_expect *exp) | ||
50 | { | ||
51 | struct nf_conn *master = ct->master; | ||
52 | struct nf_conntrack_expect *other_exp; | ||
53 | struct nf_conntrack_tuple t; | ||
54 | struct nf_ct_pptp_master *ct_pptp_info; | ||
55 | struct nf_nat_pptp *nat_pptp_info; | ||
56 | struct ip_nat_range range; | ||
57 | |||
58 | ct_pptp_info = &nfct_help(master)->help.ct_pptp_info; | ||
59 | nat_pptp_info = &nfct_nat(master)->help.nat_pptp_info; | ||
60 | |||
61 | /* And here goes the grand finale of corrosion... */ | ||
62 | if (exp->dir == IP_CT_DIR_ORIGINAL) { | ||
63 | DEBUGP("we are PNS->PAC\n"); | ||
64 | /* therefore, build tuple for PAC->PNS */ | ||
65 | t.src.l3num = AF_INET; | ||
66 | t.src.u3.ip = master->tuplehash[!exp->dir].tuple.src.u3.ip; | ||
67 | t.src.u.gre.key = ct_pptp_info->pac_call_id; | ||
68 | t.dst.u3.ip = master->tuplehash[!exp->dir].tuple.dst.u3.ip; | ||
69 | t.dst.u.gre.key = ct_pptp_info->pns_call_id; | ||
70 | t.dst.protonum = IPPROTO_GRE; | ||
71 | } else { | ||
72 | DEBUGP("we are PAC->PNS\n"); | ||
73 | /* build tuple for PNS->PAC */ | ||
74 | t.src.l3num = AF_INET; | ||
75 | t.src.u3.ip = master->tuplehash[exp->dir].tuple.src.u3.ip; | ||
76 | t.src.u.gre.key = nat_pptp_info->pns_call_id; | ||
77 | t.dst.u3.ip = master->tuplehash[exp->dir].tuple.dst.u3.ip; | ||
78 | t.dst.u.gre.key = nat_pptp_info->pac_call_id; | ||
79 | t.dst.protonum = IPPROTO_GRE; | ||
80 | } | ||
81 | |||
82 | DEBUGP("trying to unexpect other dir: "); | ||
83 | NF_CT_DUMP_TUPLE(&t); | ||
84 | other_exp = nf_conntrack_expect_find_get(&t); | ||
85 | if (other_exp) { | ||
86 | nf_conntrack_unexpect_related(other_exp); | ||
87 | nf_conntrack_expect_put(other_exp); | ||
88 | DEBUGP("success\n"); | ||
89 | } else { | ||
90 | DEBUGP("not found!\n"); | ||
91 | } | ||
92 | |||
93 | /* This must be a fresh one. */ | ||
94 | BUG_ON(ct->status & IPS_NAT_DONE_MASK); | ||
95 | |||
96 | /* Change src to where master sends to */ | ||
97 | range.flags = IP_NAT_RANGE_MAP_IPS; | ||
98 | range.min_ip = range.max_ip | ||
99 | = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip; | ||
100 | if (exp->dir == IP_CT_DIR_ORIGINAL) { | ||
101 | range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; | ||
102 | range.min = range.max = exp->saved_proto; | ||
103 | } | ||
104 | /* hook doesn't matter, but it has to do source manip */ | ||
105 | nf_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); | ||
106 | |||
107 | /* For DST manip, map port here to where it's expected. */ | ||
108 | range.flags = IP_NAT_RANGE_MAP_IPS; | ||
109 | range.min_ip = range.max_ip | ||
110 | = ct->master->tuplehash[!exp->dir].tuple.src.u3.ip; | ||
111 | if (exp->dir == IP_CT_DIR_REPLY) { | ||
112 | range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; | ||
113 | range.min = range.max = exp->saved_proto; | ||
114 | } | ||
115 | /* hook doesn't matter, but it has to do destination manip */ | ||
116 | nf_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); | ||
117 | } | ||
118 | |||
119 | /* outbound packets == from PNS to PAC */ | ||
120 | static int | ||
121 | pptp_outbound_pkt(struct sk_buff **pskb, | ||
122 | struct nf_conn *ct, | ||
123 | enum ip_conntrack_info ctinfo, | ||
124 | struct PptpControlHeader *ctlh, | ||
125 | union pptp_ctrl_union *pptpReq) | ||
126 | |||
127 | { | ||
128 | struct nf_ct_pptp_master *ct_pptp_info; | ||
129 | struct nf_nat_pptp *nat_pptp_info; | ||
130 | u_int16_t msg; | ||
131 | __be16 new_callid; | ||
132 | unsigned int cid_off; | ||
133 | |||
134 | ct_pptp_info = &nfct_help(ct)->help.ct_pptp_info; | ||
135 | nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; | ||
136 | |||
137 | new_callid = ct_pptp_info->pns_call_id; | ||
138 | |||
139 | switch (msg = ntohs(ctlh->messageType)) { | ||
140 | case PPTP_OUT_CALL_REQUEST: | ||
141 | cid_off = offsetof(union pptp_ctrl_union, ocreq.callID); | ||
142 | /* FIXME: ideally we would want to reserve a call ID | ||
143 | * here. current netfilter NAT core is not able to do | ||
144 | * this :( For now we use TCP source port. This breaks | ||
145 | * multiple calls within one control session */ | ||
146 | |||
147 | /* save original call ID in nat_info */ | ||
148 | nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id; | ||
149 | |||
150 | /* don't use tcph->source since we are at a DSTmanip | ||
151 | * hook (e.g. PREROUTING) and pkt is not mangled yet */ | ||
152 | new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port; | ||
153 | |||
154 | /* save new call ID in ct info */ | ||
155 | ct_pptp_info->pns_call_id = new_callid; | ||
156 | break; | ||
157 | case PPTP_IN_CALL_REPLY: | ||
158 | cid_off = offsetof(union pptp_ctrl_union, icack.callID); | ||
159 | break; | ||
160 | case PPTP_CALL_CLEAR_REQUEST: | ||
161 | cid_off = offsetof(union pptp_ctrl_union, clrreq.callID); | ||
162 | break; | ||
163 | default: | ||
164 | DEBUGP("unknown outbound packet 0x%04x:%s\n", msg, | ||
165 | (msg <= PPTP_MSG_MAX)? | ||
166 | pptp_msg_name[msg]:pptp_msg_name[0]); | ||
167 | /* fall through */ | ||
168 | case PPTP_SET_LINK_INFO: | ||
169 | /* only need to NAT in case PAC is behind NAT box */ | ||
170 | case PPTP_START_SESSION_REQUEST: | ||
171 | case PPTP_START_SESSION_REPLY: | ||
172 | case PPTP_STOP_SESSION_REQUEST: | ||
173 | case PPTP_STOP_SESSION_REPLY: | ||
174 | case PPTP_ECHO_REQUEST: | ||
175 | case PPTP_ECHO_REPLY: | ||
176 | /* no need to alter packet */ | ||
177 | return NF_ACCEPT; | ||
178 | } | ||
179 | |||
180 | /* only OUT_CALL_REQUEST, IN_CALL_REPLY, CALL_CLEAR_REQUEST pass | ||
181 | * down to here */ | ||
182 | DEBUGP("altering call id from 0x%04x to 0x%04x\n", | ||
183 | ntohs(REQ_CID(pptpReq, cid_off)), ntohs(new_callid)); | ||
184 | |||
185 | /* mangle packet */ | ||
186 | if (nf_nat_mangle_tcp_packet(pskb, ct, ctinfo, | ||
187 | cid_off + sizeof(struct pptp_pkt_hdr) + | ||
188 | sizeof(struct PptpControlHeader), | ||
189 | sizeof(new_callid), (char *)&new_callid, | ||
190 | sizeof(new_callid)) == 0) | ||
191 | return NF_DROP; | ||
192 | return NF_ACCEPT; | ||
193 | } | ||
194 | |||
195 | static void | ||
196 | pptp_exp_gre(struct nf_conntrack_expect *expect_orig, | ||
197 | struct nf_conntrack_expect *expect_reply) | ||
198 | { | ||
199 | struct nf_conn *ct = expect_orig->master; | ||
200 | struct nf_ct_pptp_master *ct_pptp_info; | ||
201 | struct nf_nat_pptp *nat_pptp_info; | ||
202 | |||
203 | ct_pptp_info = &nfct_help(ct)->help.ct_pptp_info; | ||
204 | nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; | ||
205 | |||
206 | /* save original PAC call ID in nat_info */ | ||
207 | nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id; | ||
208 | |||
209 | /* alter expectation for PNS->PAC direction */ | ||
210 | expect_orig->saved_proto.gre.key = ct_pptp_info->pns_call_id; | ||
211 | expect_orig->tuple.src.u.gre.key = nat_pptp_info->pns_call_id; | ||
212 | expect_orig->tuple.dst.u.gre.key = ct_pptp_info->pac_call_id; | ||
213 | expect_orig->dir = IP_CT_DIR_ORIGINAL; | ||
214 | |||
215 | /* alter expectation for PAC->PNS direction */ | ||
216 | expect_reply->saved_proto.gre.key = nat_pptp_info->pns_call_id; | ||
217 | expect_reply->tuple.src.u.gre.key = nat_pptp_info->pac_call_id; | ||
218 | expect_reply->tuple.dst.u.gre.key = ct_pptp_info->pns_call_id; | ||
219 | expect_reply->dir = IP_CT_DIR_REPLY; | ||
220 | } | ||
221 | |||
222 | /* inbound packets == from PAC to PNS */ | ||
223 | static int | ||
224 | pptp_inbound_pkt(struct sk_buff **pskb, | ||
225 | struct nf_conn *ct, | ||
226 | enum ip_conntrack_info ctinfo, | ||
227 | struct PptpControlHeader *ctlh, | ||
228 | union pptp_ctrl_union *pptpReq) | ||
229 | { | ||
230 | struct nf_nat_pptp *nat_pptp_info; | ||
231 | u_int16_t msg; | ||
232 | __be16 new_pcid; | ||
233 | unsigned int pcid_off; | ||
234 | |||
235 | nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; | ||
236 | new_pcid = nat_pptp_info->pns_call_id; | ||
237 | |||
238 | switch (msg = ntohs(ctlh->messageType)) { | ||
239 | case PPTP_OUT_CALL_REPLY: | ||
240 | pcid_off = offsetof(union pptp_ctrl_union, ocack.peersCallID); | ||
241 | break; | ||
242 | case PPTP_IN_CALL_CONNECT: | ||
243 | pcid_off = offsetof(union pptp_ctrl_union, iccon.peersCallID); | ||
244 | break; | ||
245 | case PPTP_IN_CALL_REQUEST: | ||
246 | /* only need to nat in case PAC is behind NAT box */ | ||
247 | return NF_ACCEPT; | ||
248 | case PPTP_WAN_ERROR_NOTIFY: | ||
249 | pcid_off = offsetof(union pptp_ctrl_union, wanerr.peersCallID); | ||
250 | break; | ||
251 | case PPTP_CALL_DISCONNECT_NOTIFY: | ||
252 | pcid_off = offsetof(union pptp_ctrl_union, disc.callID); | ||
253 | break; | ||
254 | case PPTP_SET_LINK_INFO: | ||
255 | pcid_off = offsetof(union pptp_ctrl_union, setlink.peersCallID); | ||
256 | break; | ||
257 | default: | ||
258 | DEBUGP("unknown inbound packet %s\n", (msg <= PPTP_MSG_MAX)? | ||
259 | pptp_msg_name[msg]:pptp_msg_name[0]); | ||
260 | /* fall through */ | ||
261 | case PPTP_START_SESSION_REQUEST: | ||
262 | case PPTP_START_SESSION_REPLY: | ||
263 | case PPTP_STOP_SESSION_REQUEST: | ||
264 | case PPTP_STOP_SESSION_REPLY: | ||
265 | case PPTP_ECHO_REQUEST: | ||
266 | case PPTP_ECHO_REPLY: | ||
267 | /* no need to alter packet */ | ||
268 | return NF_ACCEPT; | ||
269 | } | ||
270 | |||
271 | /* only OUT_CALL_REPLY, IN_CALL_CONNECT, IN_CALL_REQUEST, | ||
272 | * WAN_ERROR_NOTIFY, CALL_DISCONNECT_NOTIFY pass down here */ | ||
273 | |||
274 | /* mangle packet */ | ||
275 | DEBUGP("altering peer call id from 0x%04x to 0x%04x\n", | ||
276 | ntohs(REQ_CID(pptpReq, pcid_off)), ntohs(new_pcid)); | ||
277 | |||
278 | if (nf_nat_mangle_tcp_packet(pskb, ct, ctinfo, | ||
279 | pcid_off + sizeof(struct pptp_pkt_hdr) + | ||
280 | sizeof(struct PptpControlHeader), | ||
281 | sizeof(new_pcid), (char *)&new_pcid, | ||
282 | sizeof(new_pcid)) == 0) | ||
283 | return NF_DROP; | ||
284 | return NF_ACCEPT; | ||
285 | } | ||
286 | |||
287 | static int __init nf_nat_helper_pptp_init(void) | ||
288 | { | ||
289 | nf_nat_need_gre(); | ||
290 | |||
291 | BUG_ON(rcu_dereference(nf_nat_pptp_hook_outbound)); | ||
292 | rcu_assign_pointer(nf_nat_pptp_hook_outbound, pptp_outbound_pkt); | ||
293 | |||
294 | BUG_ON(rcu_dereference(nf_nat_pptp_hook_inbound)); | ||
295 | rcu_assign_pointer(nf_nat_pptp_hook_inbound, pptp_inbound_pkt); | ||
296 | |||
297 | BUG_ON(rcu_dereference(nf_nat_pptp_hook_exp_gre)); | ||
298 | rcu_assign_pointer(nf_nat_pptp_hook_exp_gre, pptp_exp_gre); | ||
299 | |||
300 | BUG_ON(rcu_dereference(nf_nat_pptp_hook_expectfn)); | ||
301 | rcu_assign_pointer(nf_nat_pptp_hook_expectfn, pptp_nat_expected); | ||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | static void __exit nf_nat_helper_pptp_fini(void) | ||
306 | { | ||
307 | rcu_assign_pointer(nf_nat_pptp_hook_expectfn, NULL); | ||
308 | rcu_assign_pointer(nf_nat_pptp_hook_exp_gre, NULL); | ||
309 | rcu_assign_pointer(nf_nat_pptp_hook_inbound, NULL); | ||
310 | rcu_assign_pointer(nf_nat_pptp_hook_outbound, NULL); | ||
311 | synchronize_rcu(); | ||
312 | } | ||
313 | |||
314 | module_init(nf_nat_helper_pptp_init); | ||
315 | module_exit(nf_nat_helper_pptp_fini); | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_gre.c b/net/ipv4/netfilter/nf_nat_proto_gre.c new file mode 100644 index 000000000000..d3de579e09d2 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_gre.c | |||
@@ -0,0 +1,179 @@ | |||
1 | /* | ||
2 | * nf_nat_proto_gre.c | ||
3 | * | ||
4 | * NAT protocol helper module for GRE. | ||
5 | * | ||
6 | * GRE is a generic encapsulation protocol, which is generally not very | ||
7 | * suited for NAT, as it has no protocol-specific part as port numbers. | ||
8 | * | ||
9 | * It has an optional key field, which may help us distinguishing two | ||
10 | * connections between the same two hosts. | ||
11 | * | ||
12 | * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 | ||
13 | * | ||
14 | * PPTP is built on top of a modified version of GRE, and has a mandatory | ||
15 | * field called "CallID", which serves us for the same purpose as the key | ||
16 | * field in plain GRE. | ||
17 | * | ||
18 | * Documentation about PPTP can be found in RFC 2637 | ||
19 | * | ||
20 | * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org> | ||
21 | * | ||
22 | * Development of this code funded by Astaro AG (http://www.astaro.com/) | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <linux/module.h> | ||
27 | #include <linux/skbuff.h> | ||
28 | #include <linux/ip.h> | ||
29 | |||
30 | #include <net/netfilter/nf_nat.h> | ||
31 | #include <net/netfilter/nf_nat_rule.h> | ||
32 | #include <net/netfilter/nf_nat_protocol.h> | ||
33 | #include <linux/netfilter/nf_conntrack_proto_gre.h> | ||
34 | |||
35 | MODULE_LICENSE("GPL"); | ||
36 | MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); | ||
37 | MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE"); | ||
38 | |||
39 | #if 0 | ||
40 | #define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \ | ||
41 | __FUNCTION__, ## args) | ||
42 | #else | ||
43 | #define DEBUGP(x, args...) | ||
44 | #endif | ||
45 | |||
46 | /* is key in given range between min and max */ | ||
47 | static int | ||
48 | gre_in_range(const struct nf_conntrack_tuple *tuple, | ||
49 | enum nf_nat_manip_type maniptype, | ||
50 | const union nf_conntrack_man_proto *min, | ||
51 | const union nf_conntrack_man_proto *max) | ||
52 | { | ||
53 | __be16 key; | ||
54 | |||
55 | if (maniptype == IP_NAT_MANIP_SRC) | ||
56 | key = tuple->src.u.gre.key; | ||
57 | else | ||
58 | key = tuple->dst.u.gre.key; | ||
59 | |||
60 | return ntohs(key) >= ntohs(min->gre.key) && | ||
61 | ntohs(key) <= ntohs(max->gre.key); | ||
62 | } | ||
63 | |||
64 | /* generate unique tuple ... */ | ||
65 | static int | ||
66 | gre_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
67 | const struct nf_nat_range *range, | ||
68 | enum nf_nat_manip_type maniptype, | ||
69 | const struct nf_conn *conntrack) | ||
70 | { | ||
71 | static u_int16_t key; | ||
72 | __be16 *keyptr; | ||
73 | unsigned int min, i, range_size; | ||
74 | |||
75 | if (maniptype == IP_NAT_MANIP_SRC) | ||
76 | keyptr = &tuple->src.u.gre.key; | ||
77 | else | ||
78 | keyptr = &tuple->dst.u.gre.key; | ||
79 | |||
80 | if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { | ||
81 | DEBUGP("%p: NATing GRE PPTP\n", conntrack); | ||
82 | min = 1; | ||
83 | range_size = 0xffff; | ||
84 | } else { | ||
85 | min = ntohs(range->min.gre.key); | ||
86 | range_size = ntohs(range->max.gre.key) - min + 1; | ||
87 | } | ||
88 | |||
89 | DEBUGP("min = %u, range_size = %u\n", min, range_size); | ||
90 | |||
91 | for (i = 0; i < range_size; i++, key++) { | ||
92 | *keyptr = htons(min + key % range_size); | ||
93 | if (!nf_nat_used_tuple(tuple, conntrack)) | ||
94 | return 1; | ||
95 | } | ||
96 | |||
97 | DEBUGP("%p: no NAT mapping\n", conntrack); | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | /* manipulate a GRE packet according to maniptype */ | ||
102 | static int | ||
103 | gre_manip_pkt(struct sk_buff **pskb, unsigned int iphdroff, | ||
104 | const struct nf_conntrack_tuple *tuple, | ||
105 | enum nf_nat_manip_type maniptype) | ||
106 | { | ||
107 | struct gre_hdr *greh; | ||
108 | struct gre_hdr_pptp *pgreh; | ||
109 | struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff); | ||
110 | unsigned int hdroff = iphdroff + iph->ihl * 4; | ||
111 | |||
112 | /* pgreh includes two optional 32bit fields which are not required | ||
113 | * to be there. That's where the magic '8' comes from */ | ||
114 | if (!skb_make_writable(pskb, hdroff + sizeof(*pgreh) - 8)) | ||
115 | return 0; | ||
116 | |||
117 | greh = (void *)(*pskb)->data + hdroff; | ||
118 | pgreh = (struct gre_hdr_pptp *)greh; | ||
119 | |||
120 | /* we only have destination manip of a packet, since 'source key' | ||
121 | * is not present in the packet itself */ | ||
122 | if (maniptype != IP_NAT_MANIP_DST) | ||
123 | return 1; | ||
124 | switch (greh->version) { | ||
125 | case 0: | ||
126 | if (!greh->key) { | ||
127 | DEBUGP("can't nat GRE w/o key\n"); | ||
128 | break; | ||
129 | } | ||
130 | if (greh->csum) { | ||
131 | /* FIXME: Never tested this code... */ | ||
132 | nf_proto_csum_replace4(gre_csum(greh), *pskb, | ||
133 | *(gre_key(greh)), | ||
134 | tuple->dst.u.gre.key, 0); | ||
135 | } | ||
136 | *(gre_key(greh)) = tuple->dst.u.gre.key; | ||
137 | break; | ||
138 | case GRE_VERSION_PPTP: | ||
139 | DEBUGP("call_id -> 0x%04x\n", ntohs(tuple->dst.u.gre.key)); | ||
140 | pgreh->call_id = tuple->dst.u.gre.key; | ||
141 | break; | ||
142 | default: | ||
143 | DEBUGP("can't nat unknown GRE version\n"); | ||
144 | return 0; | ||
145 | } | ||
146 | return 1; | ||
147 | } | ||
148 | |||
149 | static struct nf_nat_protocol gre __read_mostly = { | ||
150 | .name = "GRE", | ||
151 | .protonum = IPPROTO_GRE, | ||
152 | .manip_pkt = gre_manip_pkt, | ||
153 | .in_range = gre_in_range, | ||
154 | .unique_tuple = gre_unique_tuple, | ||
155 | #if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ | ||
156 | defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) | ||
157 | .range_to_nfattr = nf_nat_port_range_to_nfattr, | ||
158 | .nfattr_to_range = nf_nat_port_nfattr_to_range, | ||
159 | #endif | ||
160 | }; | ||
161 | |||
162 | int __init nf_nat_proto_gre_init(void) | ||
163 | { | ||
164 | return nf_nat_protocol_register(&gre); | ||
165 | } | ||
166 | |||
167 | void __exit nf_nat_proto_gre_fini(void) | ||
168 | { | ||
169 | nf_nat_protocol_unregister(&gre); | ||
170 | } | ||
171 | |||
172 | module_init(nf_nat_proto_gre_init); | ||
173 | module_exit(nf_nat_proto_gre_fini); | ||
174 | |||
175 | void nf_nat_need_gre(void) | ||
176 | { | ||
177 | return; | ||
178 | } | ||
179 | EXPORT_SYMBOL_GPL(nf_nat_need_gre); | ||