]>
Commit | Line | Data |
---|---|---|
7ba0088d A |
1 | /* |
2 | * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
a2c93a76 A |
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. | |
7ba0088d | 11 | * |
a2c93a76 A |
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 | |
7ba0088d A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
a2c93a76 A |
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. | |
7ba0088d A |
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 | } |