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
104struct 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};
113typedef 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
125enum {
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
165static PptpCallId AliasVerifyPptp(struct ip *, u_int16_t *);
166
167
168void
169AliasHandlePptpOut(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
231void
232AliasHandlePptpIn(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
296static PptpCallId
297AliasVerifyPptp(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
334int
335AliasHandlePptpGreOut(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
362int
363AliasHandlePptpGreIn(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}
391