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