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