Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[sfrench/cifs-2.6.git] / net / ipv4 / netfilter / nf_nat_pptp.c
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);