• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/net/ipv4/netfilter/
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 <net/netfilter/nf_conntrack_zones.h>
29#include <linux/netfilter/nf_conntrack_proto_gre.h>
30#include <linux/netfilter/nf_conntrack_pptp.h>
31
32#define NF_NAT_PPTP_VERSION "3.0"
33
34#define REQ_CID(req, off)		(*(__be16 *)((char *)(req) + (off)))
35
36MODULE_LICENSE("GPL");
37MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
38MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP");
39MODULE_ALIAS("ip_nat_pptp");
40
41static void pptp_nat_expected(struct nf_conn *ct,
42			      struct nf_conntrack_expect *exp)
43{
44	struct net *net = nf_ct_net(ct);
45	const struct nf_conn *master = ct->master;
46	struct nf_conntrack_expect *other_exp;
47	struct nf_conntrack_tuple t;
48	const struct nf_ct_pptp_master *ct_pptp_info;
49	const struct nf_nat_pptp *nat_pptp_info;
50	struct nf_nat_range range;
51
52	ct_pptp_info = &nfct_help(master)->help.ct_pptp_info;
53	nat_pptp_info = &nfct_nat(master)->help.nat_pptp_info;
54
55	/* And here goes the grand finale of corrosion... */
56	if (exp->dir == IP_CT_DIR_ORIGINAL) {
57		pr_debug("we are PNS->PAC\n");
58		/* therefore, build tuple for PAC->PNS */
59		t.src.l3num = AF_INET;
60		t.src.u3.ip = master->tuplehash[!exp->dir].tuple.src.u3.ip;
61		t.src.u.gre.key = ct_pptp_info->pac_call_id;
62		t.dst.u3.ip = master->tuplehash[!exp->dir].tuple.dst.u3.ip;
63		t.dst.u.gre.key = ct_pptp_info->pns_call_id;
64		t.dst.protonum = IPPROTO_GRE;
65	} else {
66		pr_debug("we are PAC->PNS\n");
67		/* build tuple for PNS->PAC */
68		t.src.l3num = AF_INET;
69		t.src.u3.ip = master->tuplehash[!exp->dir].tuple.src.u3.ip;
70		t.src.u.gre.key = nat_pptp_info->pns_call_id;
71		t.dst.u3.ip = master->tuplehash[!exp->dir].tuple.dst.u3.ip;
72		t.dst.u.gre.key = nat_pptp_info->pac_call_id;
73		t.dst.protonum = IPPROTO_GRE;
74	}
75
76	pr_debug("trying to unexpect other dir: ");
77	nf_ct_dump_tuple_ip(&t);
78	other_exp = nf_ct_expect_find_get(net, nf_ct_zone(ct), &t);
79	if (other_exp) {
80		nf_ct_unexpect_related(other_exp);
81		nf_ct_expect_put(other_exp);
82		pr_debug("success\n");
83	} else {
84		pr_debug("not found!\n");
85	}
86
87	/* This must be a fresh one. */
88	BUG_ON(ct->status & IPS_NAT_DONE_MASK);
89
90	/* Change src to where master sends to */
91	range.flags = IP_NAT_RANGE_MAP_IPS;
92	range.min_ip = range.max_ip
93		= ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip;
94	if (exp->dir == IP_CT_DIR_ORIGINAL) {
95		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
96		range.min = range.max = exp->saved_proto;
97	}
98	nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC);
99
100	/* For DST manip, map port here to where it's expected. */
101	range.flags = IP_NAT_RANGE_MAP_IPS;
102	range.min_ip = range.max_ip
103		= ct->master->tuplehash[!exp->dir].tuple.src.u3.ip;
104	if (exp->dir == IP_CT_DIR_REPLY) {
105		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
106		range.min = range.max = exp->saved_proto;
107	}
108	nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST);
109}
110
111/* outbound packets == from PNS to PAC */
112static int
113pptp_outbound_pkt(struct sk_buff *skb,
114		  struct nf_conn *ct,
115		  enum ip_conntrack_info ctinfo,
116		  struct PptpControlHeader *ctlh,
117		  union pptp_ctrl_union *pptpReq)
118
119{
120	struct nf_ct_pptp_master *ct_pptp_info;
121	struct nf_nat_pptp *nat_pptp_info;
122	u_int16_t msg;
123	__be16 new_callid;
124	unsigned int cid_off;
125
126	ct_pptp_info  = &nfct_help(ct)->help.ct_pptp_info;
127	nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info;
128
129	new_callid = ct_pptp_info->pns_call_id;
130
131	switch (msg = ntohs(ctlh->messageType)) {
132	case PPTP_OUT_CALL_REQUEST:
133		cid_off = offsetof(union pptp_ctrl_union, ocreq.callID);
134
135		/* save original call ID in nat_info */
136		nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id;
137
138		/* don't use tcph->source since we are at a DSTmanip
139		 * hook (e.g. PREROUTING) and pkt is not mangled yet */
140		new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port;
141
142		/* save new call ID in ct info */
143		ct_pptp_info->pns_call_id = new_callid;
144		break;
145	case PPTP_IN_CALL_REPLY:
146		cid_off = offsetof(union pptp_ctrl_union, icack.callID);
147		break;
148	case PPTP_CALL_CLEAR_REQUEST:
149		cid_off = offsetof(union pptp_ctrl_union, clrreq.callID);
150		break;
151	default:
152		pr_debug("unknown outbound packet 0x%04x:%s\n", msg,
153			 msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] :
154					       pptp_msg_name[0]);
155		/* fall through */
156	case PPTP_SET_LINK_INFO:
157		/* only need to NAT in case PAC is behind NAT box */
158	case PPTP_START_SESSION_REQUEST:
159	case PPTP_START_SESSION_REPLY:
160	case PPTP_STOP_SESSION_REQUEST:
161	case PPTP_STOP_SESSION_REPLY:
162	case PPTP_ECHO_REQUEST:
163	case PPTP_ECHO_REPLY:
164		/* no need to alter packet */
165		return NF_ACCEPT;
166	}
167
168	/* only OUT_CALL_REQUEST, IN_CALL_REPLY, CALL_CLEAR_REQUEST pass
169	 * down to here */
170	pr_debug("altering call id from 0x%04x to 0x%04x\n",
171		 ntohs(REQ_CID(pptpReq, cid_off)), ntohs(new_callid));
172
173	/* mangle packet */
174	if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
175				     cid_off + sizeof(struct pptp_pkt_hdr) +
176				     sizeof(struct PptpControlHeader),
177				     sizeof(new_callid), (char *)&new_callid,
178				     sizeof(new_callid)) == 0)
179		return NF_DROP;
180	return NF_ACCEPT;
181}
182
183static void
184pptp_exp_gre(struct nf_conntrack_expect *expect_orig,
185	     struct nf_conntrack_expect *expect_reply)
186{
187	const struct nf_conn *ct = expect_orig->master;
188	struct nf_ct_pptp_master *ct_pptp_info;
189	struct nf_nat_pptp *nat_pptp_info;
190
191	ct_pptp_info  = &nfct_help(ct)->help.ct_pptp_info;
192	nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info;
193
194	/* save original PAC call ID in nat_info */
195	nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id;
196
197	/* alter expectation for PNS->PAC direction */
198	expect_orig->saved_proto.gre.key = ct_pptp_info->pns_call_id;
199	expect_orig->tuple.src.u.gre.key = nat_pptp_info->pns_call_id;
200	expect_orig->tuple.dst.u.gre.key = ct_pptp_info->pac_call_id;
201	expect_orig->dir = IP_CT_DIR_ORIGINAL;
202
203	/* alter expectation for PAC->PNS direction */
204	expect_reply->saved_proto.gre.key = nat_pptp_info->pns_call_id;
205	expect_reply->tuple.src.u.gre.key = nat_pptp_info->pac_call_id;
206	expect_reply->tuple.dst.u.gre.key = ct_pptp_info->pns_call_id;
207	expect_reply->dir = IP_CT_DIR_REPLY;
208}
209
210/* inbound packets == from PAC to PNS */
211static int
212pptp_inbound_pkt(struct sk_buff *skb,
213		 struct nf_conn *ct,
214		 enum ip_conntrack_info ctinfo,
215		 struct PptpControlHeader *ctlh,
216		 union pptp_ctrl_union *pptpReq)
217{
218	const struct nf_nat_pptp *nat_pptp_info;
219	u_int16_t msg;
220	__be16 new_pcid;
221	unsigned int pcid_off;
222
223	nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info;
224	new_pcid = nat_pptp_info->pns_call_id;
225
226	switch (msg = ntohs(ctlh->messageType)) {
227	case PPTP_OUT_CALL_REPLY:
228		pcid_off = offsetof(union pptp_ctrl_union, ocack.peersCallID);
229		break;
230	case PPTP_IN_CALL_CONNECT:
231		pcid_off = offsetof(union pptp_ctrl_union, iccon.peersCallID);
232		break;
233	case PPTP_IN_CALL_REQUEST:
234		/* only need to nat in case PAC is behind NAT box */
235		return NF_ACCEPT;
236	case PPTP_WAN_ERROR_NOTIFY:
237		pcid_off = offsetof(union pptp_ctrl_union, wanerr.peersCallID);
238		break;
239	case PPTP_CALL_DISCONNECT_NOTIFY:
240		pcid_off = offsetof(union pptp_ctrl_union, disc.callID);
241		break;
242	case PPTP_SET_LINK_INFO:
243		pcid_off = offsetof(union pptp_ctrl_union, setlink.peersCallID);
244		break;
245	default:
246		pr_debug("unknown inbound packet %s\n",
247			 msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] :
248					       pptp_msg_name[0]);
249		/* fall through */
250	case PPTP_START_SESSION_REQUEST:
251	case PPTP_START_SESSION_REPLY:
252	case PPTP_STOP_SESSION_REQUEST:
253	case PPTP_STOP_SESSION_REPLY:
254	case PPTP_ECHO_REQUEST:
255	case PPTP_ECHO_REPLY:
256		/* no need to alter packet */
257		return NF_ACCEPT;
258	}
259
260	/* only OUT_CALL_REPLY, IN_CALL_CONNECT, IN_CALL_REQUEST,
261	 * WAN_ERROR_NOTIFY, CALL_DISCONNECT_NOTIFY pass down here */
262
263	/* mangle packet */
264	pr_debug("altering peer call id from 0x%04x to 0x%04x\n",
265		 ntohs(REQ_CID(pptpReq, pcid_off)), ntohs(new_pcid));
266
267	if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
268				     pcid_off + sizeof(struct pptp_pkt_hdr) +
269				     sizeof(struct PptpControlHeader),
270				     sizeof(new_pcid), (char *)&new_pcid,
271				     sizeof(new_pcid)) == 0)
272		return NF_DROP;
273	return NF_ACCEPT;
274}
275
276static int __init nf_nat_helper_pptp_init(void)
277{
278	nf_nat_need_gre();
279
280	BUG_ON(nf_nat_pptp_hook_outbound != NULL);
281	rcu_assign_pointer(nf_nat_pptp_hook_outbound, pptp_outbound_pkt);
282
283	BUG_ON(nf_nat_pptp_hook_inbound != NULL);
284	rcu_assign_pointer(nf_nat_pptp_hook_inbound, pptp_inbound_pkt);
285
286	BUG_ON(nf_nat_pptp_hook_exp_gre != NULL);
287	rcu_assign_pointer(nf_nat_pptp_hook_exp_gre, pptp_exp_gre);
288
289	BUG_ON(nf_nat_pptp_hook_expectfn != NULL);
290	rcu_assign_pointer(nf_nat_pptp_hook_expectfn, pptp_nat_expected);
291	return 0;
292}
293
294static void __exit nf_nat_helper_pptp_fini(void)
295{
296	rcu_assign_pointer(nf_nat_pptp_hook_expectfn, NULL);
297	rcu_assign_pointer(nf_nat_pptp_hook_exp_gre, NULL);
298	rcu_assign_pointer(nf_nat_pptp_hook_inbound, NULL);
299	rcu_assign_pointer(nf_nat_pptp_hook_outbound, NULL);
300	synchronize_rcu();
301}
302
303module_init(nf_nat_helper_pptp_init);
304module_exit(nf_nat_helper_pptp_fini);
305