aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2006-12-03 01:09:41 -0500
committerDavid S. Miller <davem@davemloft.net>2006-12-03 01:09:41 -0500
commitf09943fefe6b702e40893d35b4f10fd1064037fe (patch)
treeb170d046ecf0642471bb3c55d8e1f316fe9e5ddc /net
parent92703eee4ccde3c55ee067a89c373e8a51a8adf9 (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')
-rw-r--r--net/ipv4/netfilter/Kconfig10
-rw-r--r--net/ipv4/netfilter/Makefile4
-rw-r--r--net/ipv4/netfilter/nf_nat_pptp.c315
-rw-r--r--net/ipv4/netfilter/nf_nat_proto_gre.c179
-rw-r--r--net/netfilter/Kconfig23
-rw-r--r--net/netfilter/Makefile2
-rw-r--r--net/netfilter/nf_conntrack_core.c4
-rw-r--r--net/netfilter/nf_conntrack_pptp.c607
-rw-r--r--net/netfilter/nf_conntrack_proto_gre.c305
9 files changed, 1449 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/).
487config NF_NAT_PROTO_GRE
488 tristate
489 depends on NF_NAT && NF_CT_PROTO_GRE
490
487config IP_NF_NAT_FTP 491config 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
535config 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
531config IP_NF_NAT_H323 541config 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
54obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o 54obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o
55obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o 55obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o
56obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o 56obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o
57obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o
58
59# NAT protocols (nf_nat)
60obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o
57 61
58# generic IP tables 62# generic IP tables
59obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o 63obj-$(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
35MODULE_LICENSE("GPL");
36MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
37MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP");
38MODULE_ALIAS("ip_nat_pptp");
39
40#if 0
41extern 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
48static 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 */
120static int
121pptp_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
195static void
196pptp_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 */
223static int
224pptp_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
287static 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
305static 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
314module_init(nf_nat_helper_pptp_init);
315module_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
35MODULE_LICENSE("GPL");
36MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
37MODULE_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 */
47static int
48gre_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 ... */
65static int
66gre_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 */
102static int
103gre_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
149static 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
162int __init nf_nat_proto_gre_init(void)
163{
164 return nf_nat_protocol_register(&gre);
165}
166
167void __exit nf_nat_proto_gre_fini(void)
168{
169 nf_nat_protocol_unregister(&gre);
170}
171
172module_init(nf_nat_proto_gre_init);
173module_exit(nf_nat_proto_gre_fini);
174
175void nf_nat_need_gre(void)
176{
177 return;
178}
179EXPORT_SYMBOL_GPL(nf_nat_need_gre);
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index d1a365d83c53..6b2eb26ae03f 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -120,6 +120,10 @@ config NF_CONNTRACK_EVENTS
120 120
121 If unsure, say `N'. 121 If unsure, say `N'.
122 122
123config NF_CT_PROTO_GRE
124 tristate
125 depends on EXPERIMENTAL && NF_CONNTRACK
126
123config NF_CT_PROTO_SCTP 127config NF_CT_PROTO_SCTP
124 tristate 'SCTP protocol on new connection tracking support (EXPERIMENTAL)' 128 tristate 'SCTP protocol on new connection tracking support (EXPERIMENTAL)'
125 depends on EXPERIMENTAL && NF_CONNTRACK 129 depends on EXPERIMENTAL && NF_CONNTRACK
@@ -213,6 +217,25 @@ config NF_CONNTRACK_NETBIOS_NS
213 217
214 To compile it as a module, choose M here. If unsure, say N. 218 To compile it as a module, choose M here. If unsure, say N.
215 219
220config NF_CONNTRACK_PPTP
221 tristate "PPtP protocol support (EXPERIMENTAL)"
222 depends on EXPERIMENTAL && NF_CONNTRACK
223 select NF_CT_PROTO_GRE
224 help
225 This module adds support for PPTP (Point to Point Tunnelling
226 Protocol, RFC2637) connection tracking and NAT.
227
228 If you are running PPTP sessions over a stateful firewall or NAT
229 box, you may want to enable this feature.
230
231 Please note that not all PPTP modes of operation are supported yet.
232 Specifically these limitations exist:
233 - Blindy assumes that control connections are always established
234 in PNS->PAC direction. This is a violation of RFC2637.
235 - Only supports a single call within each session
236
237 To compile it as a module, choose M here. If unsure, say N.
238
216config NF_CT_NETLINK 239config NF_CT_NETLINK
217 tristate 'Connection tracking netlink interface (EXPERIMENTAL)' 240 tristate 'Connection tracking netlink interface (EXPERIMENTAL)'
218 depends on EXPERIMENTAL && NF_CONNTRACK && NETFILTER_NETLINK 241 depends on EXPERIMENTAL && NF_CONNTRACK && NETFILTER_NETLINK
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 67144b2af647..897bed4cbd79 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o
14obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o 14obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o
15 15
16# SCTP protocol connection tracking 16# SCTP protocol connection tracking
17obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o
17obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o 18obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o
18 19
19# netlink interface for nf_conntrack 20# netlink interface for nf_conntrack
@@ -27,6 +28,7 @@ obj-$(CONFIG_NF_CONNTRACK_FTP) += nf_conntrack_ftp.o
27obj-$(CONFIG_NF_CONNTRACK_H323) += nf_conntrack_h323.o 28obj-$(CONFIG_NF_CONNTRACK_H323) += nf_conntrack_h323.o
28obj-$(CONFIG_NF_CONNTRACK_IRC) += nf_conntrack_irc.o 29obj-$(CONFIG_NF_CONNTRACK_IRC) += nf_conntrack_irc.o
29obj-$(CONFIG_NF_CONNTRACK_NETBIOS_NS) += nf_conntrack_netbios_ns.o 30obj-$(CONFIG_NF_CONNTRACK_NETBIOS_NS) += nf_conntrack_netbios_ns.o
31obj-$(CONFIG_NF_CONNTRACK_PPTP) += nf_conntrack_pptp.o
30 32
31# generic X tables 33# generic X tables
32obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o 34obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index aa8beabfeebb..ed756c928bc4 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -300,6 +300,7 @@ static void
300destroy_conntrack(struct nf_conntrack *nfct) 300destroy_conntrack(struct nf_conntrack *nfct)
301{ 301{
302 struct nf_conn *ct = (struct nf_conn *)nfct; 302 struct nf_conn *ct = (struct nf_conn *)nfct;
303 struct nf_conn_help *help = nfct_help(ct);
303 struct nf_conntrack_l3proto *l3proto; 304 struct nf_conntrack_l3proto *l3proto;
304 struct nf_conntrack_l4proto *l4proto; 305 struct nf_conntrack_l4proto *l4proto;
305 306
@@ -310,6 +311,9 @@ destroy_conntrack(struct nf_conntrack *nfct)
310 nf_conntrack_event(IPCT_DESTROY, ct); 311 nf_conntrack_event(IPCT_DESTROY, ct);
311 set_bit(IPS_DYING_BIT, &ct->status); 312 set_bit(IPS_DYING_BIT, &ct->status);
312 313
314 if (help && help->helper && help->helper->destroy)
315 help->helper->destroy(ct);
316
313 /* To make sure we don't get any weird locking issues here: 317 /* To make sure we don't get any weird locking issues here:
314 * destroy_conntrack() MUST NOT be called with a write lock 318 * destroy_conntrack() MUST NOT be called with a write lock
315 * to nf_conntrack_lock!!! -HW */ 319 * to nf_conntrack_lock!!! -HW */
diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c
new file mode 100644
index 000000000000..f0ff00e0d052
--- /dev/null
+++ b/net/netfilter/nf_conntrack_pptp.c
@@ -0,0 +1,607 @@
1/*
2 * Connection tracking support for PPTP (Point to Point Tunneling Protocol).
3 * PPTP is a a protocol for creating virtual private networks.
4 * It is a specification defined by Microsoft and some vendors
5 * working with Microsoft. PPTP is built on top of a modified
6 * version of the Internet Generic Routing Encapsulation Protocol.
7 * GRE is defined in RFC 1701 and RFC 1702. Documentation of
8 * PPTP can be found in RFC 2637
9 *
10 * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
11 *
12 * Development of this code funded by Astaro AG (http://www.astaro.com/)
13 *
14 * Limitations:
15 * - We blindly assume that control connections are always
16 * established in PNS->PAC direction. This is a violation
17 * of RFFC2673
18 * - We can only support one single call within each session
19 * TODO:
20 * - testing of incoming PPTP calls
21 */
22
23#include <linux/module.h>
24#include <linux/skbuff.h>
25#include <linux/in.h>
26#include <linux/tcp.h>
27
28#include <net/netfilter/nf_conntrack.h>
29#include <net/netfilter/nf_conntrack_core.h>
30#include <net/netfilter/nf_conntrack_helper.h>
31#include <linux/netfilter/nf_conntrack_proto_gre.h>
32#include <linux/netfilter/nf_conntrack_pptp.h>
33
34#define NF_CT_PPTP_VERSION "3.1"
35
36MODULE_LICENSE("GPL");
37MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
38MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP");
39MODULE_ALIAS("ip_conntrack_pptp");
40
41static DEFINE_SPINLOCK(nf_pptp_lock);
42
43int
44(*nf_nat_pptp_hook_outbound)(struct sk_buff **pskb,
45 struct nf_conn *ct, enum ip_conntrack_info ctinfo,
46 struct PptpControlHeader *ctlh,
47 union pptp_ctrl_union *pptpReq) __read_mostly;
48EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_outbound);
49
50int
51(*nf_nat_pptp_hook_inbound)(struct sk_buff **pskb,
52 struct nf_conn *ct, enum ip_conntrack_info ctinfo,
53 struct PptpControlHeader *ctlh,
54 union pptp_ctrl_union *pptpReq) __read_mostly;
55EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_inbound);
56
57void
58(*nf_nat_pptp_hook_exp_gre)(struct nf_conntrack_expect *expect_orig,
59 struct nf_conntrack_expect *expect_reply)
60 __read_mostly;
61EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_exp_gre);
62
63void
64(*nf_nat_pptp_hook_expectfn)(struct nf_conn *ct,
65 struct nf_conntrack_expect *exp) __read_mostly;
66EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn);
67
68#if 0
69/* PptpControlMessageType names */
70const char *pptp_msg_name[] = {
71 "UNKNOWN_MESSAGE",
72 "START_SESSION_REQUEST",
73 "START_SESSION_REPLY",
74 "STOP_SESSION_REQUEST",
75 "STOP_SESSION_REPLY",
76 "ECHO_REQUEST",
77 "ECHO_REPLY",
78 "OUT_CALL_REQUEST",
79 "OUT_CALL_REPLY",
80 "IN_CALL_REQUEST",
81 "IN_CALL_REPLY",
82 "IN_CALL_CONNECT",
83 "CALL_CLEAR_REQUEST",
84 "CALL_DISCONNECT_NOTIFY",
85 "WAN_ERROR_NOTIFY",
86 "SET_LINK_INFO"
87};
88EXPORT_SYMBOL(pptp_msg_name);
89#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args)
90#else
91#define DEBUGP(format, args...)
92#endif
93
94#define SECS *HZ
95#define MINS * 60 SECS
96#define HOURS * 60 MINS
97
98#define PPTP_GRE_TIMEOUT (10 MINS)
99#define PPTP_GRE_STREAM_TIMEOUT (5 HOURS)
100
101static void pptp_expectfn(struct nf_conn *ct,
102 struct nf_conntrack_expect *exp)
103{
104 typeof(nf_nat_pptp_hook_expectfn) nf_nat_pptp_expectfn;
105 DEBUGP("increasing timeouts\n");
106
107 /* increase timeout of GRE data channel conntrack entry */
108 ct->proto.gre.timeout = PPTP_GRE_TIMEOUT;
109 ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT;
110
111 /* Can you see how rusty this code is, compared with the pre-2.6.11
112 * one? That's what happened to my shiny newnat of 2002 ;( -HW */
113
114 rcu_read_lock();
115 nf_nat_pptp_expectfn = rcu_dereference(nf_nat_pptp_hook_expectfn);
116 if (nf_nat_pptp_expectfn && ct->status & IPS_NAT_MASK)
117 nf_nat_pptp_expectfn(ct, exp);
118 else {
119 struct nf_conntrack_tuple inv_t;
120 struct nf_conntrack_expect *exp_other;
121
122 /* obviously this tuple inversion only works until you do NAT */
123 nf_ct_invert_tuplepr(&inv_t, &exp->tuple);
124 DEBUGP("trying to unexpect other dir: ");
125 NF_CT_DUMP_TUPLE(&inv_t);
126
127 exp_other = nf_conntrack_expect_find_get(&inv_t);
128 if (exp_other) {
129 /* delete other expectation. */
130 DEBUGP("found\n");
131 nf_conntrack_unexpect_related(exp_other);
132 nf_conntrack_expect_put(exp_other);
133 } else {
134 DEBUGP("not found\n");
135 }
136 }
137 rcu_read_unlock();
138}
139
140static int destroy_sibling_or_exp(const struct nf_conntrack_tuple *t)
141{
142 struct nf_conntrack_tuple_hash *h;
143 struct nf_conntrack_expect *exp;
144 struct nf_conn *sibling;
145
146 DEBUGP("trying to timeout ct or exp for tuple ");
147 NF_CT_DUMP_TUPLE(t);
148
149 h = nf_conntrack_find_get(t, NULL);
150 if (h) {
151 sibling = nf_ct_tuplehash_to_ctrack(h);
152 DEBUGP("setting timeout of conntrack %p to 0\n", sibling);
153 sibling->proto.gre.timeout = 0;
154 sibling->proto.gre.stream_timeout = 0;
155 if (del_timer(&sibling->timeout))
156 sibling->timeout.function((unsigned long)sibling);
157 nf_ct_put(sibling);
158 return 1;
159 } else {
160 exp = nf_conntrack_expect_find_get(t);
161 if (exp) {
162 DEBUGP("unexpect_related of expect %p\n", exp);
163 nf_conntrack_unexpect_related(exp);
164 nf_conntrack_expect_put(exp);
165 return 1;
166 }
167 }
168 return 0;
169}
170
171/* timeout GRE data connections */
172static void pptp_destroy_siblings(struct nf_conn *ct)
173{
174 struct nf_conn_help *help = nfct_help(ct);
175 struct nf_conntrack_tuple t;
176
177 nf_ct_gre_keymap_destroy(ct);
178
179 /* try original (pns->pac) tuple */
180 memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t));
181 t.dst.protonum = IPPROTO_GRE;
182 t.src.u.gre.key = help->help.ct_pptp_info.pns_call_id;
183 t.dst.u.gre.key = help->help.ct_pptp_info.pac_call_id;
184 if (!destroy_sibling_or_exp(&t))
185 DEBUGP("failed to timeout original pns->pac ct/exp\n");
186
187 /* try reply (pac->pns) tuple */
188 memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t));
189 t.dst.protonum = IPPROTO_GRE;
190 t.src.u.gre.key = help->help.ct_pptp_info.pac_call_id;
191 t.dst.u.gre.key = help->help.ct_pptp_info.pns_call_id;
192 if (!destroy_sibling_or_exp(&t))
193 DEBUGP("failed to timeout reply pac->pns ct/exp\n");
194}
195
196/* expect GRE connections (PNS->PAC and PAC->PNS direction) */
197static int exp_gre(struct nf_conn *ct, __be16 callid, __be16 peer_callid)
198{
199 struct nf_conntrack_expect *exp_orig, *exp_reply;
200 enum ip_conntrack_dir dir;
201 int ret = 1;
202 typeof(nf_nat_pptp_hook_exp_gre) nf_nat_pptp_exp_gre;
203
204 exp_orig = nf_conntrack_expect_alloc(ct);
205 if (exp_orig == NULL)
206 goto out;
207
208 exp_reply = nf_conntrack_expect_alloc(ct);
209 if (exp_reply == NULL)
210 goto out_put_orig;
211
212 /* original direction, PNS->PAC */
213 dir = IP_CT_DIR_ORIGINAL;
214 nf_conntrack_expect_init(exp_orig, ct->tuplehash[dir].tuple.src.l3num,
215 &ct->tuplehash[dir].tuple.src.u3,
216 &ct->tuplehash[dir].tuple.dst.u3,
217 IPPROTO_GRE, &peer_callid, &callid);
218 exp_orig->expectfn = pptp_expectfn;
219
220 /* reply direction, PAC->PNS */
221 dir = IP_CT_DIR_REPLY;
222 nf_conntrack_expect_init(exp_reply, ct->tuplehash[dir].tuple.src.l3num,
223 &ct->tuplehash[dir].tuple.src.u3,
224 &ct->tuplehash[dir].tuple.dst.u3,
225 IPPROTO_GRE, &callid, &peer_callid);
226 exp_reply->expectfn = pptp_expectfn;
227
228 nf_nat_pptp_exp_gre = rcu_dereference(nf_nat_pptp_hook_exp_gre);
229 if (nf_nat_pptp_exp_gre && ct->status & IPS_NAT_MASK)
230 nf_nat_pptp_exp_gre(exp_orig, exp_reply);
231 if (nf_conntrack_expect_related(exp_orig) != 0)
232 goto out_put_both;
233 if (nf_conntrack_expect_related(exp_reply) != 0)
234 goto out_unexpect_orig;
235
236 /* Add GRE keymap entries */
237 if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_ORIGINAL, &exp_orig->tuple) != 0)
238 goto out_unexpect_both;
239 if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_REPLY, &exp_reply->tuple) != 0) {
240 nf_ct_gre_keymap_destroy(ct);
241 goto out_unexpect_both;
242 }
243 ret = 0;
244
245out_put_both:
246 nf_conntrack_expect_put(exp_reply);
247out_put_orig:
248 nf_conntrack_expect_put(exp_orig);
249out:
250 return ret;
251
252out_unexpect_both:
253 nf_conntrack_unexpect_related(exp_reply);
254out_unexpect_orig:
255 nf_conntrack_unexpect_related(exp_orig);
256 goto out_put_both;
257}
258
259static inline int
260pptp_inbound_pkt(struct sk_buff **pskb,
261 struct PptpControlHeader *ctlh,
262 union pptp_ctrl_union *pptpReq,
263 unsigned int reqlen,
264 struct nf_conn *ct,
265 enum ip_conntrack_info ctinfo)
266{
267 struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info;
268 u_int16_t msg;
269 __be16 cid = 0, pcid = 0;
270 typeof(nf_nat_pptp_hook_inbound) nf_nat_pptp_inbound;
271
272 msg = ntohs(ctlh->messageType);
273 DEBUGP("inbound control message %s\n", pptp_msg_name[msg]);
274
275 switch (msg) {
276 case PPTP_START_SESSION_REPLY:
277 /* server confirms new control session */
278 if (info->sstate < PPTP_SESSION_REQUESTED)
279 goto invalid;
280 if (pptpReq->srep.resultCode == PPTP_START_OK)
281 info->sstate = PPTP_SESSION_CONFIRMED;
282 else
283 info->sstate = PPTP_SESSION_ERROR;
284 break;
285
286 case PPTP_STOP_SESSION_REPLY:
287 /* server confirms end of control session */
288 if (info->sstate > PPTP_SESSION_STOPREQ)
289 goto invalid;
290 if (pptpReq->strep.resultCode == PPTP_STOP_OK)
291 info->sstate = PPTP_SESSION_NONE;
292 else
293 info->sstate = PPTP_SESSION_ERROR;
294 break;
295
296 case PPTP_OUT_CALL_REPLY:
297 /* server accepted call, we now expect GRE frames */
298 if (info->sstate != PPTP_SESSION_CONFIRMED)
299 goto invalid;
300 if (info->cstate != PPTP_CALL_OUT_REQ &&
301 info->cstate != PPTP_CALL_OUT_CONF)
302 goto invalid;
303
304 cid = pptpReq->ocack.callID;
305 pcid = pptpReq->ocack.peersCallID;
306 if (info->pns_call_id != pcid)
307 goto invalid;
308 DEBUGP("%s, CID=%X, PCID=%X\n", pptp_msg_name[msg],
309 ntohs(cid), ntohs(pcid));
310
311 if (pptpReq->ocack.resultCode == PPTP_OUTCALL_CONNECT) {
312 info->cstate = PPTP_CALL_OUT_CONF;
313 info->pac_call_id = cid;
314 exp_gre(ct, cid, pcid);
315 } else
316 info->cstate = PPTP_CALL_NONE;
317 break;
318
319 case PPTP_IN_CALL_REQUEST:
320 /* server tells us about incoming call request */
321 if (info->sstate != PPTP_SESSION_CONFIRMED)
322 goto invalid;
323
324 cid = pptpReq->icreq.callID;
325 DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
326 info->cstate = PPTP_CALL_IN_REQ;
327 info->pac_call_id = cid;
328 break;
329
330 case PPTP_IN_CALL_CONNECT:
331 /* server tells us about incoming call established */
332 if (info->sstate != PPTP_SESSION_CONFIRMED)
333 goto invalid;
334 if (info->cstate != PPTP_CALL_IN_REP &&
335 info->cstate != PPTP_CALL_IN_CONF)
336 goto invalid;
337
338 pcid = pptpReq->iccon.peersCallID;
339 cid = info->pac_call_id;
340
341 if (info->pns_call_id != pcid)
342 goto invalid;
343
344 DEBUGP("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(pcid));
345 info->cstate = PPTP_CALL_IN_CONF;
346
347 /* we expect a GRE connection from PAC to PNS */
348 exp_gre(ct, cid, pcid);
349 break;
350
351 case PPTP_CALL_DISCONNECT_NOTIFY:
352 /* server confirms disconnect */
353 cid = pptpReq->disc.callID;
354 DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
355 info->cstate = PPTP_CALL_NONE;
356
357 /* untrack this call id, unexpect GRE packets */
358 pptp_destroy_siblings(ct);
359 break;
360
361 case PPTP_WAN_ERROR_NOTIFY:
362 case PPTP_ECHO_REQUEST:
363 case PPTP_ECHO_REPLY:
364 /* I don't have to explain these ;) */
365 break;
366
367 default:
368 goto invalid;
369 }
370
371 nf_nat_pptp_inbound = rcu_dereference(nf_nat_pptp_hook_inbound);
372 if (nf_nat_pptp_inbound && ct->status & IPS_NAT_MASK)
373 return nf_nat_pptp_inbound(pskb, ct, ctinfo, ctlh, pptpReq);
374 return NF_ACCEPT;
375
376invalid:
377 DEBUGP("invalid %s: type=%d cid=%u pcid=%u "
378 "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n",
379 msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0],
380 msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate,
381 ntohs(info->pns_call_id), ntohs(info->pac_call_id));
382 return NF_ACCEPT;
383}
384
385static inline int
386pptp_outbound_pkt(struct sk_buff **pskb,
387 struct PptpControlHeader *ctlh,
388 union pptp_ctrl_union *pptpReq,
389 unsigned int reqlen,
390 struct nf_conn *ct,
391 enum ip_conntrack_info ctinfo)
392{
393 struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info;
394 u_int16_t msg;
395 __be16 cid = 0, pcid = 0;
396 typeof(nf_nat_pptp_hook_outbound) nf_nat_pptp_outbound;
397
398 msg = ntohs(ctlh->messageType);
399 DEBUGP("outbound control message %s\n", pptp_msg_name[msg]);
400
401 switch (msg) {
402 case PPTP_START_SESSION_REQUEST:
403 /* client requests for new control session */
404 if (info->sstate != PPTP_SESSION_NONE)
405 goto invalid;
406 info->sstate = PPTP_SESSION_REQUESTED;
407 break;
408
409 case PPTP_STOP_SESSION_REQUEST:
410 /* client requests end of control session */
411 info->sstate = PPTP_SESSION_STOPREQ;
412 break;
413
414 case PPTP_OUT_CALL_REQUEST:
415 /* client initiating connection to server */
416 if (info->sstate != PPTP_SESSION_CONFIRMED)
417 goto invalid;
418 info->cstate = PPTP_CALL_OUT_REQ;
419 /* track PNS call id */
420 cid = pptpReq->ocreq.callID;
421 DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
422 info->pns_call_id = cid;
423 break;
424
425 case PPTP_IN_CALL_REPLY:
426 /* client answers incoming call */
427 if (info->cstate != PPTP_CALL_IN_REQ &&
428 info->cstate != PPTP_CALL_IN_REP)
429 goto invalid;
430
431 cid = pptpReq->icack.callID;
432 pcid = pptpReq->icack.peersCallID;
433 if (info->pac_call_id != pcid)
434 goto invalid;
435 DEBUGP("%s, CID=%X PCID=%X\n", pptp_msg_name[msg],
436 ntohs(cid), ntohs(pcid));
437
438 if (pptpReq->icack.resultCode == PPTP_INCALL_ACCEPT) {
439 /* part two of the three-way handshake */
440 info->cstate = PPTP_CALL_IN_REP;
441 info->pns_call_id = cid;
442 } else
443 info->cstate = PPTP_CALL_NONE;
444 break;
445
446 case PPTP_CALL_CLEAR_REQUEST:
447 /* client requests hangup of call */
448 if (info->sstate != PPTP_SESSION_CONFIRMED)
449 goto invalid;
450 /* FUTURE: iterate over all calls and check if
451 * call ID is valid. We don't do this without newnat,
452 * because we only know about last call */
453 info->cstate = PPTP_CALL_CLEAR_REQ;
454 break;
455
456 case PPTP_SET_LINK_INFO:
457 case PPTP_ECHO_REQUEST:
458 case PPTP_ECHO_REPLY:
459 /* I don't have to explain these ;) */
460 break;
461
462 default:
463 goto invalid;
464 }
465
466 nf_nat_pptp_outbound = rcu_dereference(nf_nat_pptp_hook_outbound);
467 if (nf_nat_pptp_outbound && ct->status & IPS_NAT_MASK)
468 return nf_nat_pptp_outbound(pskb, ct, ctinfo, ctlh, pptpReq);
469 return NF_ACCEPT;
470
471invalid:
472 DEBUGP("invalid %s: type=%d cid=%u pcid=%u "
473 "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n",
474 msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0],
475 msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate,
476 ntohs(info->pns_call_id), ntohs(info->pac_call_id));
477 return NF_ACCEPT;
478}
479
480static const unsigned int pptp_msg_size[] = {
481 [PPTP_START_SESSION_REQUEST] = sizeof(struct PptpStartSessionRequest),
482 [PPTP_START_SESSION_REPLY] = sizeof(struct PptpStartSessionReply),
483 [PPTP_STOP_SESSION_REQUEST] = sizeof(struct PptpStopSessionRequest),
484 [PPTP_STOP_SESSION_REPLY] = sizeof(struct PptpStopSessionReply),
485 [PPTP_OUT_CALL_REQUEST] = sizeof(struct PptpOutCallRequest),
486 [PPTP_OUT_CALL_REPLY] = sizeof(struct PptpOutCallReply),
487 [PPTP_IN_CALL_REQUEST] = sizeof(struct PptpInCallRequest),
488 [PPTP_IN_CALL_REPLY] = sizeof(struct PptpInCallReply),
489 [PPTP_IN_CALL_CONNECT] = sizeof(struct PptpInCallConnected),
490 [PPTP_CALL_CLEAR_REQUEST] = sizeof(struct PptpClearCallRequest),
491 [PPTP_CALL_DISCONNECT_NOTIFY] = sizeof(struct PptpCallDisconnectNotify),
492 [PPTP_WAN_ERROR_NOTIFY] = sizeof(struct PptpWanErrorNotify),
493 [PPTP_SET_LINK_INFO] = sizeof(struct PptpSetLinkInfo),
494};
495
496/* track caller id inside control connection, call expect_related */
497static int
498conntrack_pptp_help(struct sk_buff **pskb, unsigned int protoff,
499 struct nf_conn *ct, enum ip_conntrack_info ctinfo)
500
501{
502 int dir = CTINFO2DIR(ctinfo);
503 struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info;
504 struct tcphdr _tcph, *tcph;
505 struct pptp_pkt_hdr _pptph, *pptph;
506 struct PptpControlHeader _ctlh, *ctlh;
507 union pptp_ctrl_union _pptpReq, *pptpReq;
508 unsigned int tcplen = (*pskb)->len - protoff;
509 unsigned int datalen, reqlen, nexthdr_off;
510 int oldsstate, oldcstate;
511 int ret;
512 u_int16_t msg;
513
514 /* don't do any tracking before tcp handshake complete */
515 if (ctinfo != IP_CT_ESTABLISHED &&
516 ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY)
517 return NF_ACCEPT;
518
519 nexthdr_off = protoff;
520 tcph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_tcph), &_tcph);
521 BUG_ON(!tcph);
522 nexthdr_off += tcph->doff * 4;
523 datalen = tcplen - tcph->doff * 4;
524
525 pptph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_pptph), &_pptph);
526 if (!pptph) {
527 DEBUGP("no full PPTP header, can't track\n");
528 return NF_ACCEPT;
529 }
530 nexthdr_off += sizeof(_pptph);
531 datalen -= sizeof(_pptph);
532
533 /* if it's not a control message we can't do anything with it */
534 if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL ||
535 ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) {
536 DEBUGP("not a control packet\n");
537 return NF_ACCEPT;
538 }
539
540 ctlh = skb_header_pointer(*pskb, nexthdr_off, sizeof(_ctlh), &_ctlh);
541 if (!ctlh)
542 return NF_ACCEPT;
543 nexthdr_off += sizeof(_ctlh);
544 datalen -= sizeof(_ctlh);
545
546 reqlen = datalen;
547 msg = ntohs(ctlh->messageType);
548 if (msg > 0 && msg <= PPTP_MSG_MAX && reqlen < pptp_msg_size[msg])
549 return NF_ACCEPT;
550 if (reqlen > sizeof(*pptpReq))
551 reqlen = sizeof(*pptpReq);
552
553 pptpReq = skb_header_pointer(*pskb, nexthdr_off, reqlen, &_pptpReq);
554 if (!pptpReq)
555 return NF_ACCEPT;
556
557 oldsstate = info->sstate;
558 oldcstate = info->cstate;
559
560 spin_lock_bh(&nf_pptp_lock);
561
562 /* FIXME: We just blindly assume that the control connection is always
563 * established from PNS->PAC. However, RFC makes no guarantee */
564 if (dir == IP_CT_DIR_ORIGINAL)
565 /* client -> server (PNS -> PAC) */
566 ret = pptp_outbound_pkt(pskb, ctlh, pptpReq, reqlen, ct,
567 ctinfo);
568 else
569 /* server -> client (PAC -> PNS) */
570 ret = pptp_inbound_pkt(pskb, ctlh, pptpReq, reqlen, ct,
571 ctinfo);
572 DEBUGP("sstate: %d->%d, cstate: %d->%d\n",
573 oldsstate, info->sstate, oldcstate, info->cstate);
574 spin_unlock_bh(&nf_pptp_lock);
575
576 return ret;
577}
578
579/* control protocol helper */
580static struct nf_conntrack_helper pptp __read_mostly = {
581 .name = "pptp",
582 .me = THIS_MODULE,
583 .max_expected = 2,
584 .timeout = 5 * 60,
585 .tuple.src.l3num = AF_INET,
586 .tuple.src.u.tcp.port = __constant_htons(PPTP_CONTROL_PORT),
587 .tuple.dst.protonum = IPPROTO_TCP,
588 .mask.src.l3num = 0xffff,
589 .mask.src.u.tcp.port = __constant_htons(0xffff),
590 .mask.dst.protonum = 0xff,
591 .help = conntrack_pptp_help,
592 .destroy = pptp_destroy_siblings,
593};
594
595static int __init nf_conntrack_pptp_init(void)
596{
597 return nf_conntrack_helper_register(&pptp);
598}
599
600static void __exit nf_conntrack_pptp_fini(void)
601{
602 nf_conntrack_helper_unregister(&pptp);
603 nf_ct_gre_keymap_flush();
604}
605
606module_init(nf_conntrack_pptp_init);
607module_exit(nf_conntrack_pptp_fini);
diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c
new file mode 100644
index 000000000000..ac193ce70249
--- /dev/null
+++ b/net/netfilter/nf_conntrack_proto_gre.c
@@ -0,0 +1,305 @@
1/*
2 * ip_conntrack_proto_gre.c - Version 3.0
3 *
4 * Connection tracking 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/types.h>
28#include <linux/timer.h>
29#include <linux/list.h>
30#include <linux/seq_file.h>
31#include <linux/in.h>
32#include <linux/skbuff.h>
33
34#include <net/netfilter/nf_conntrack_l4proto.h>
35#include <net/netfilter/nf_conntrack_helper.h>
36#include <net/netfilter/nf_conntrack_core.h>
37#include <linux/netfilter/nf_conntrack_proto_gre.h>
38#include <linux/netfilter/nf_conntrack_pptp.h>
39
40#define GRE_TIMEOUT (30 * HZ)
41#define GRE_STREAM_TIMEOUT (180 * HZ)
42
43#if 0
44#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args)
45#else
46#define DEBUGP(x, args...)
47#endif
48
49static DEFINE_RWLOCK(nf_ct_gre_lock);
50static LIST_HEAD(gre_keymap_list);
51
52void nf_ct_gre_keymap_flush(void)
53{
54 struct list_head *pos, *n;
55
56 write_lock_bh(&nf_ct_gre_lock);
57 list_for_each_safe(pos, n, &gre_keymap_list) {
58 list_del(pos);
59 kfree(pos);
60 }
61 write_unlock_bh(&nf_ct_gre_lock);
62}
63EXPORT_SYMBOL(nf_ct_gre_keymap_flush);
64
65static inline int gre_key_cmpfn(const struct nf_ct_gre_keymap *km,
66 const struct nf_conntrack_tuple *t)
67{
68 return km->tuple.src.l3num == t->src.l3num &&
69 !memcmp(&km->tuple.src.u3, &t->src.u3, sizeof(t->src.u3)) &&
70 !memcmp(&km->tuple.dst.u3, &t->dst.u3, sizeof(t->dst.u3)) &&
71 km->tuple.dst.protonum == t->dst.protonum &&
72 km->tuple.dst.u.all == t->dst.u.all;
73}
74
75/* look up the source key for a given tuple */
76static __be16 gre_keymap_lookup(struct nf_conntrack_tuple *t)
77{
78 struct nf_ct_gre_keymap *km;
79 __be16 key = 0;
80
81 read_lock_bh(&nf_ct_gre_lock);
82 list_for_each_entry(km, &gre_keymap_list, list) {
83 if (gre_key_cmpfn(km, t)) {
84 key = km->tuple.src.u.gre.key;
85 break;
86 }
87 }
88 read_unlock_bh(&nf_ct_gre_lock);
89
90 DEBUGP("lookup src key 0x%x for ", key);
91 NF_CT_DUMP_TUPLE(t);
92
93 return key;
94}
95
96/* add a single keymap entry, associate with specified master ct */
97int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
98 struct nf_conntrack_tuple *t)
99{
100 struct nf_conn_help *help = nfct_help(ct);
101 struct nf_ct_gre_keymap **kmp, *km;
102
103 BUG_ON(strcmp(help->helper->name, "pptp"));
104 kmp = &help->help.ct_pptp_info.keymap[dir];
105 if (*kmp) {
106 /* check whether it's a retransmission */
107 list_for_each_entry(km, &gre_keymap_list, list) {
108 if (gre_key_cmpfn(km, t) && km == *kmp)
109 return 0;
110 }
111 DEBUGP("trying to override keymap_%s for ct %p\n",
112 dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct);
113 return -EEXIST;
114 }
115
116 km = kmalloc(sizeof(*km), GFP_ATOMIC);
117 if (!km)
118 return -ENOMEM;
119 memcpy(&km->tuple, t, sizeof(*t));
120 *kmp = km;
121
122 DEBUGP("adding new entry %p: ", km);
123 NF_CT_DUMP_TUPLE(&km->tuple);
124
125 write_lock_bh(&nf_ct_gre_lock);
126 list_add_tail(&km->list, &gre_keymap_list);
127 write_unlock_bh(&nf_ct_gre_lock);
128
129 return 0;
130}
131EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add);
132
133/* destroy the keymap entries associated with specified master ct */
134void nf_ct_gre_keymap_destroy(struct nf_conn *ct)
135{
136 struct nf_conn_help *help = nfct_help(ct);
137 enum ip_conntrack_dir dir;
138
139 DEBUGP("entering for ct %p\n", ct);
140 BUG_ON(strcmp(help->helper->name, "pptp"));
141
142 write_lock_bh(&nf_ct_gre_lock);
143 for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) {
144 if (help->help.ct_pptp_info.keymap[dir]) {
145 DEBUGP("removing %p from list\n",
146 help->help.ct_pptp_info.keymap[dir]);
147 list_del(&help->help.ct_pptp_info.keymap[dir]->list);
148 kfree(help->help.ct_pptp_info.keymap[dir]);
149 help->help.ct_pptp_info.keymap[dir] = NULL;
150 }
151 }
152 write_unlock_bh(&nf_ct_gre_lock);
153}
154EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_destroy);
155
156/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */
157
158/* invert gre part of tuple */
159static int gre_invert_tuple(struct nf_conntrack_tuple *tuple,
160 const struct nf_conntrack_tuple *orig)
161{
162 tuple->dst.u.gre.key = orig->src.u.gre.key;
163 tuple->src.u.gre.key = orig->dst.u.gre.key;
164 return 1;
165}
166
167/* gre hdr info to tuple */
168static int gre_pkt_to_tuple(const struct sk_buff *skb,
169 unsigned int dataoff,
170 struct nf_conntrack_tuple *tuple)
171{
172 struct gre_hdr_pptp _pgrehdr, *pgrehdr;
173 __be16 srckey;
174 struct gre_hdr _grehdr, *grehdr;
175
176 /* first only delinearize old RFC1701 GRE header */
177 grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr);
178 if (!grehdr || grehdr->version != GRE_VERSION_PPTP) {
179 /* try to behave like "nf_conntrack_proto_generic" */
180 tuple->src.u.all = 0;
181 tuple->dst.u.all = 0;
182 return 1;
183 }
184
185 /* PPTP header is variable length, only need up to the call_id field */
186 pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr);
187 if (!pgrehdr)
188 return 1;
189
190 if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) {
191 DEBUGP("GRE_VERSION_PPTP but unknown proto\n");
192 return 0;
193 }
194
195 tuple->dst.u.gre.key = pgrehdr->call_id;
196 srckey = gre_keymap_lookup(tuple);
197 tuple->src.u.gre.key = srckey;
198
199 return 1;
200}
201
202/* print gre part of tuple */
203static int gre_print_tuple(struct seq_file *s,
204 const struct nf_conntrack_tuple *tuple)
205{
206 return seq_printf(s, "srckey=0x%x dstkey=0x%x ",
207 ntohs(tuple->src.u.gre.key),
208 ntohs(tuple->dst.u.gre.key));
209}
210
211/* print private data for conntrack */
212static int gre_print_conntrack(struct seq_file *s,
213 const struct nf_conn *ct)
214{
215 return seq_printf(s, "timeout=%u, stream_timeout=%u ",
216 (ct->proto.gre.timeout / HZ),
217 (ct->proto.gre.stream_timeout / HZ));
218}
219
220/* Returns verdict for packet, and may modify conntrack */
221static int gre_packet(struct nf_conn *ct,
222 const struct sk_buff *skb,
223 unsigned int dataoff,
224 enum ip_conntrack_info ctinfo,
225 int pf,
226 unsigned int hooknum)
227{
228 /* If we've seen traffic both ways, this is a GRE connection.
229 * Extend timeout. */
230 if (ct->status & IPS_SEEN_REPLY) {
231 nf_ct_refresh_acct(ct, ctinfo, skb,
232 ct->proto.gre.stream_timeout);
233 /* Also, more likely to be important, and not a probe. */
234 set_bit(IPS_ASSURED_BIT, &ct->status);
235 nf_conntrack_event_cache(IPCT_STATUS, skb);
236 } else
237 nf_ct_refresh_acct(ct, ctinfo, skb,
238 ct->proto.gre.timeout);
239
240 return NF_ACCEPT;
241}
242
243/* Called when a new connection for this protocol found. */
244static int gre_new(struct nf_conn *ct, const struct sk_buff *skb,
245 unsigned int dataoff)
246{
247 DEBUGP(": ");
248 NF_CT_DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
249
250 /* initialize to sane value. Ideally a conntrack helper
251 * (e.g. in case of pptp) is increasing them */
252 ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT;
253 ct->proto.gre.timeout = GRE_TIMEOUT;
254
255 return 1;
256}
257
258/* Called when a conntrack entry has already been removed from the hashes
259 * and is about to be deleted from memory */
260static void gre_destroy(struct nf_conn *ct)
261{
262 struct nf_conn *master = ct->master;
263 DEBUGP(" entering\n");
264
265 if (!master)
266 DEBUGP("no master !?!\n");
267 else
268 nf_ct_gre_keymap_destroy(master);
269}
270
271/* protocol helper struct */
272static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 = {
273 .l3proto = AF_INET,
274 .l4proto = IPPROTO_GRE,
275 .name = "gre",
276 .pkt_to_tuple = gre_pkt_to_tuple,
277 .invert_tuple = gre_invert_tuple,
278 .print_tuple = gre_print_tuple,
279 .print_conntrack = gre_print_conntrack,
280 .packet = gre_packet,
281 .new = gre_new,
282 .destroy = gre_destroy,
283 .me = THIS_MODULE,
284#if defined(CONFIG_NF_CONNTRACK_NETLINK) || \
285 defined(CONFIG_NF_CONNTRACK_NETLINK_MODULE)
286 .tuple_to_nfattr = nf_ct_port_tuple_to_nfattr,
287 .nfattr_to_tuple = nf_ct_port_nfattr_to_tuple,
288#endif
289};
290
291static int __init nf_ct_proto_gre_init(void)
292{
293 return nf_conntrack_l4proto_register(&nf_conntrack_l4proto_gre4);
294}
295
296static void nf_ct_proto_gre_fini(void)
297{
298 nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4);
299 nf_ct_gre_keymap_flush();
300}
301
302module_init(nf_ct_proto_gre_init);
303module_exit(nf_ct_proto_gre_fini);
304
305MODULE_LICENSE("GPL");