]> git.saurik.com Git - apple/network_cmds.git/blob - alias/alias_pptp.c
b463c386a5be6da99f1650d8496e438ba83a5b38
[apple/network_cmds.git] / alias / alias_pptp.c
1 /*
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 * alias_pptp.c
25 *
26 * Copyright (c) 2000 Whistle Communications, Inc.
27 * All rights reserved.
28 *
29 * Subject to the following obligations and disclaimer of warranty, use and
30 * redistribution of this software, in source or object code forms, with or
31 * without modifications are expressly permitted by Whistle Communications;
32 * provided, however, that:
33 * 1. Any and all reproductions of the source or object code must include the
34 * copyright notice above and the following disclaimer of warranties; and
35 * 2. No rights are granted, in any manner or form, to use Whistle
36 * Communications, Inc. trademarks, including the mark "WHISTLE
37 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
38 * such appears in the above copyright notice or in the software.
39 *
40 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
41 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
42 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
43 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
44 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
45 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
46 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
47 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
48 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
49 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
50 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
51 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
52 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
53 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
54 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
55 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
56 * OF SUCH DAMAGE.
57 *
58 * Author: Erik Salander <erik@whistle.com>
59 *
60 * Based upon:
61 * $FreeBSD: src/lib/libalias/alias_pptp.c,v 1.1.2.4 2001/08/01 09:52:27 obrien Exp $
62 */
63
64 /*
65 Alias_pptp.c performs special processing for PPTP sessions under TCP.
66 Specifically, watch PPTP control messages and alias the Call ID or the
67 Peer's Call ID in the appropriate messages. Note, PPTP requires
68 "de-aliasing" of incoming packets, this is different than any other
69 TCP applications that are currently (ie. FTP, IRC and RTSP) aliased.
70
71 For Call IDs encountered for the first time, a PPTP alias link is created.
72 The PPTP alias link uses the Call ID in place of the original port number.
73 An alias Call ID is created.
74
75 For this routine to work, the PPTP control messages must fit entirely
76 into a single TCP packet. This is typically the case, but is not
77 required by the spec.
78
79 Unlike some of the other TCP applications that are aliased (ie. FTP,
80 IRC and RTSP), the PPTP control messages that need to be aliased are
81 guaranteed to remain the same length. The aliased Call ID is a fixed
82 length field.
83
84 Reference: RFC 2637
85
86 Initial version: May, 2000 (eds)
87
88 */
89
90 /* Includes */
91 #include <sys/types.h>
92 #include <netinet/in_systm.h>
93 #include <netinet/in.h>
94 #include <netinet/ip.h>
95 #include <netinet/tcp.h>
96
97 #include <stdio.h>
98
99 #include "alias_local.h"
100
101 /*
102 * PPTP definitions
103 */
104
105 struct grehdr /* Enhanced GRE header. */
106 {
107 u_int16_t gh_flags; /* Flags. */
108 u_int16_t gh_protocol; /* Protocol type. */
109 u_int16_t gh_length; /* Payload length. */
110 u_int16_t gh_call_id; /* Call ID. */
111 u_int32_t gh_seq_no; /* Sequence number (optional). */
112 u_int32_t gh_ack_no; /* Acknowledgment number (optional). */
113 };
114 typedef struct grehdr GreHdr;
115
116 /* The PPTP protocol ID used in the GRE 'proto' field. */
117 #define PPTP_GRE_PROTO 0x880b
118
119 /* Bits that must be set a certain way in all PPTP/GRE packets. */
120 #define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO)
121 #define PPTP_INIT_MASK 0xef7fffff
122
123 #define PPTP_MAGIC 0x1a2b3c4d
124 #define PPTP_CTRL_MSG_TYPE 1
125
126 enum {
127 PPTP_StartCtrlConnRequest = 1,
128 PPTP_StartCtrlConnReply = 2,
129 PPTP_StopCtrlConnRequest = 3,
130 PPTP_StopCtrlConnReply = 4,
131 PPTP_EchoRequest = 5,
132 PPTP_EchoReply = 6,
133 PPTP_OutCallRequest = 7,
134 PPTP_OutCallReply = 8,
135 PPTP_InCallRequest = 9,
136 PPTP_InCallReply = 10,
137 PPTP_InCallConn = 11,
138 PPTP_CallClearRequest = 12,
139 PPTP_CallDiscNotify = 13,
140 PPTP_WanErrorNotify = 14,
141 PPTP_SetLinkInfo = 15
142 };
143
144 /* Message structures */
145 struct pptpMsgHead {
146 u_int16_t length; /* total length */
147 u_int16_t msgType; /* PPTP message type */
148 u_int32_t magic; /* magic cookie */
149 u_int16_t type; /* control message type */
150 u_int16_t resv0; /* reserved */
151 };
152 typedef struct pptpMsgHead *PptpMsgHead;
153
154 struct pptpCodes {
155 u_int8_t resCode; /* Result Code */
156 u_int8_t errCode; /* Error Code */
157 };
158 typedef struct pptpCodes *PptpCode;
159
160 struct pptpCallIds {
161 u_int16_t cid1; /* Call ID field #1 */
162 u_int16_t cid2; /* Call ID field #2 */
163 };
164 typedef struct pptpCallIds *PptpCallId;
165
166 static PptpCallId AliasVerifyPptp(struct ip *, u_int16_t *);
167
168
169 void
170 AliasHandlePptpOut(struct ip *pip, /* IP packet to examine/patch */
171 struct alias_link *link) /* The PPTP control link */
172 {
173 struct alias_link *pptp_link;
174 PptpCallId cptr;
175 PptpCode codes;
176 u_int16_t ctl_type; /* control message type */
177 struct tcphdr *tc;
178
179 /* Verify valid PPTP control message */
180 if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
181 return;
182
183 /* Modify certain PPTP messages */
184 switch (ctl_type) {
185 case PPTP_OutCallRequest:
186 case PPTP_OutCallReply:
187 case PPTP_InCallRequest:
188 case PPTP_InCallReply:
189 /* Establish PPTP link for address and Call ID found in control message. */
190 pptp_link = AddPptp(GetOriginalAddress(link), GetDestAddress(link),
191 GetAliasAddress(link), cptr->cid1);
192 break;
193 case PPTP_CallClearRequest:
194 case PPTP_CallDiscNotify:
195 /* Find PPTP link for address and Call ID found in control message. */
196 pptp_link = FindPptpOutByCallId(GetOriginalAddress(link),
197 GetDestAddress(link),
198 cptr->cid1);
199 break;
200 default:
201 return;
202 }
203
204 if (pptp_link != NULL) {
205 int accumulate = cptr->cid1;
206
207 /* alias the Call Id */
208 cptr->cid1 = GetAliasPort(pptp_link);
209
210 /* Compute TCP checksum for revised packet */
211 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
212 accumulate -= cptr->cid1;
213 ADJUST_CHECKSUM(accumulate, tc->th_sum);
214
215 switch (ctl_type) {
216 case PPTP_OutCallReply:
217 case PPTP_InCallReply:
218 codes = (PptpCode)(cptr + 1);
219 if (codes->resCode == 1) /* Connection established, */
220 SetDestCallId(pptp_link, /* note the Peer's Call ID. */
221 cptr->cid2);
222 else
223 SetExpire(pptp_link, 0); /* Connection refused. */
224 break;
225 case PPTP_CallDiscNotify: /* Connection closed. */
226 SetExpire(pptp_link, 0);
227 break;
228 }
229 }
230 }
231
232 void
233 AliasHandlePptpIn(struct ip *pip, /* IP packet to examine/patch */
234 struct alias_link *link) /* The PPTP control link */
235 {
236 struct alias_link *pptp_link;
237 PptpCallId cptr;
238 u_int16_t *pcall_id;
239 u_int16_t ctl_type; /* control message type */
240 struct tcphdr *tc;
241
242 /* Verify valid PPTP control message */
243 if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
244 return;
245
246 /* Modify certain PPTP messages */
247 switch (ctl_type)
248 {
249 case PPTP_InCallConn:
250 case PPTP_WanErrorNotify:
251 case PPTP_SetLinkInfo:
252 pcall_id = &cptr->cid1;
253 break;
254 case PPTP_OutCallReply:
255 case PPTP_InCallReply:
256 pcall_id = &cptr->cid2;
257 break;
258 case PPTP_CallDiscNotify: /* Connection closed. */
259 pptp_link = FindPptpInByCallId(GetDestAddress(link),
260 GetAliasAddress(link),
261 cptr->cid1);
262 if (pptp_link != NULL)
263 SetExpire(pptp_link, 0);
264 return;
265 default:
266 return;
267 }
268
269 /* Find PPTP link for address and Call ID found in PPTP Control Msg */
270 pptp_link = FindPptpInByPeerCallId(GetDestAddress(link),
271 GetAliasAddress(link),
272 *pcall_id);
273
274 if (pptp_link != NULL) {
275 int accumulate = *pcall_id;
276
277 /* De-alias the Peer's Call Id. */
278 *pcall_id = GetOriginalPort(pptp_link);
279
280 /* Compute TCP checksum for modified packet */
281 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
282 accumulate -= *pcall_id;
283 ADJUST_CHECKSUM(accumulate, tc->th_sum);
284
285 if (ctl_type == PPTP_OutCallReply || ctl_type == PPTP_InCallReply) {
286 PptpCode codes = (PptpCode)(cptr + 1);
287
288 if (codes->resCode == 1) /* Connection established, */
289 SetDestCallId(pptp_link, /* note the Call ID. */
290 cptr->cid1);
291 else
292 SetExpire(pptp_link, 0); /* Connection refused. */
293 }
294 }
295 }
296
297 static PptpCallId
298 AliasVerifyPptp(struct ip *pip, u_int16_t *ptype) /* IP packet to examine/patch */
299 {
300 int hlen, tlen, dlen;
301 PptpMsgHead hptr;
302 struct tcphdr *tc;
303
304 /* Calculate some lengths */
305 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
306 hlen = (pip->ip_hl + tc->th_off) << 2;
307 tlen = ntohs(pip->ip_len);
308 dlen = tlen - hlen;
309
310 /* Verify data length */
311 if (dlen < (sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds)))
312 return(NULL);
313
314 /* Move up to PPTP message header */
315 hptr = (PptpMsgHead)(((char *) pip) + hlen);
316
317 /* Return the control message type */
318 *ptype = ntohs(hptr->type);
319
320 /* Verify PPTP Control Message */
321 if ((ntohs(hptr->msgType) != PPTP_CTRL_MSG_TYPE) ||
322 (ntohl(hptr->magic) != PPTP_MAGIC))
323 return(NULL);
324
325 /* Verify data length. */
326 if ((*ptype == PPTP_OutCallReply || *ptype == PPTP_InCallReply) &&
327 (dlen < sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds) +
328 sizeof(struct pptpCodes)))
329 return (NULL);
330 else
331 return (PptpCallId)(hptr + 1);
332 }
333
334
335 int
336 AliasHandlePptpGreOut(struct ip *pip)
337 {
338 GreHdr *gr;
339 struct alias_link *link;
340
341 gr = (GreHdr *)((char *)pip + (pip->ip_hl << 2));
342
343 /* Check GRE header bits. */
344 if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
345 return (-1);
346
347 link = FindPptpOutByPeerCallId(pip->ip_src, pip->ip_dst, gr->gh_call_id);
348 if (link != NULL) {
349 struct in_addr alias_addr = GetAliasAddress(link);
350
351 /* Change source IP address. */
352 DifferentialChecksum(&pip->ip_sum,
353 (u_short *)&alias_addr,
354 (u_short *)&pip->ip_src,
355 2);
356 pip->ip_src = alias_addr;
357 }
358
359 return (0);
360 }
361
362
363 int
364 AliasHandlePptpGreIn(struct ip *pip)
365 {
366 GreHdr *gr;
367 struct alias_link *link;
368
369 gr = (GreHdr *)((char *)pip + (pip->ip_hl << 2));
370
371 /* Check GRE header bits. */
372 if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
373 return (-1);
374
375 link = FindPptpInByPeerCallId(pip->ip_src, pip->ip_dst, gr->gh_call_id);
376 if (link != NULL) {
377 struct in_addr src_addr = GetOriginalAddress(link);
378
379 /* De-alias the Peer's Call Id. */
380 gr->gh_call_id = GetOriginalPort(link);
381
382 /* Restore original IP address. */
383 DifferentialChecksum(&pip->ip_sum,
384 (u_short *)&src_addr,
385 (u_short *)&pip->ip_dst,
386 2);
387 pip->ip_dst = src_addr;
388 }
389
390 return (0);
391 }