xref: /dragonfly/lib/libalias/alias_pptp.c (revision 984263bc)
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  * $FreeBSD: src/lib/libalias/alias_pptp.c,v 1.1.2.4 2001/08/01 09:52:27 obrien Exp $
39  */
40 
41 /*
42    Alias_pptp.c performs special processing for PPTP sessions under TCP.
43    Specifically, watch PPTP control messages and alias the Call ID or the
44    Peer's Call ID in the appropriate messages.  Note, PPTP requires
45    "de-aliasing" of incoming packets, this is different than any other
46    TCP applications that are currently (ie. FTP, IRC and RTSP) aliased.
47 
48    For Call IDs encountered for the first time, a PPTP alias link is created.
49    The PPTP alias link uses the Call ID in place of the original port number.
50    An alias Call ID is created.
51 
52    For this routine to work, the PPTP control messages must fit entirely
53    into a single TCP packet.  This is typically the case, but is not
54    required by the spec.
55 
56    Unlike some of the other TCP applications that are aliased (ie. FTP,
57    IRC and RTSP), the PPTP control messages that need to be aliased are
58    guaranteed to remain the same length.  The aliased Call ID is a fixed
59    length field.
60 
61    Reference: RFC 2637
62 
63    Initial version:  May, 2000 (eds)
64 
65 */
66 
67 /* Includes */
68 #include <sys/types.h>
69 #include <netinet/in_systm.h>
70 #include <netinet/in.h>
71 #include <netinet/ip.h>
72 #include <netinet/tcp.h>
73 
74 #include <stdio.h>
75 
76 #include "alias_local.h"
77 
78 /*
79  * PPTP definitions
80  */
81 
82 struct grehdr			/* Enhanced GRE header. */
83 {
84     u_int16_t gh_flags;		/* Flags. */
85     u_int16_t gh_protocol;	/* Protocol type. */
86     u_int16_t gh_length;	/* Payload length. */
87     u_int16_t gh_call_id;	/* Call ID. */
88     u_int32_t gh_seq_no;	/* Sequence number (optional). */
89     u_int32_t gh_ack_no;	/* Acknowledgment number (optional). */
90 };
91 typedef struct grehdr		GreHdr;
92 
93 /* The PPTP protocol ID used in the GRE 'proto' field. */
94 #define PPTP_GRE_PROTO          0x880b
95 
96 /* Bits that must be set a certain way in all PPTP/GRE packets. */
97 #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
98 #define PPTP_INIT_MASK		0xef7fffff
99 
100 #define PPTP_MAGIC		0x1a2b3c4d
101 #define PPTP_CTRL_MSG_TYPE	1
102 
103 enum {
104   PPTP_StartCtrlConnRequest = 1,
105   PPTP_StartCtrlConnReply = 2,
106   PPTP_StopCtrlConnRequest = 3,
107   PPTP_StopCtrlConnReply = 4,
108   PPTP_EchoRequest = 5,
109   PPTP_EchoReply = 6,
110   PPTP_OutCallRequest = 7,
111   PPTP_OutCallReply = 8,
112   PPTP_InCallRequest = 9,
113   PPTP_InCallReply = 10,
114   PPTP_InCallConn = 11,
115   PPTP_CallClearRequest = 12,
116   PPTP_CallDiscNotify = 13,
117   PPTP_WanErrorNotify = 14,
118   PPTP_SetLinkInfo = 15
119 };
120 
121   /* Message structures */
122   struct pptpMsgHead {
123     u_int16_t   length;         /* total length */
124     u_int16_t   msgType;        /* PPTP message type */
125     u_int32_t   magic;          /* magic cookie */
126     u_int16_t   type;           /* control message type */
127     u_int16_t   resv0;          /* reserved */
128   };
129   typedef struct pptpMsgHead    *PptpMsgHead;
130 
131   struct pptpCodes {
132     u_int8_t    resCode;        /* Result Code */
133     u_int8_t    errCode;        /* Error Code */
134   };
135   typedef struct pptpCodes      *PptpCode;
136 
137   struct pptpCallIds {
138     u_int16_t   cid1;           /* Call ID field #1 */
139     u_int16_t   cid2;           /* Call ID field #2 */
140   };
141   typedef struct pptpCallIds    *PptpCallId;
142 
143 static PptpCallId AliasVerifyPptp(struct ip *, u_int16_t *);
144 
145 
146 void
147 AliasHandlePptpOut(struct ip *pip,	    /* IP packet to examine/patch */
148                    struct alias_link *link) /* The PPTP control link */
149 {
150     struct alias_link   *pptp_link;
151     PptpCallId    	cptr;
152     PptpCode            codes;
153     u_int16_t           ctl_type;           /* control message type */
154     struct tcphdr 	*tc;
155 
156     /* Verify valid PPTP control message */
157     if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
158       return;
159 
160     /* Modify certain PPTP messages */
161     switch (ctl_type) {
162     case PPTP_OutCallRequest:
163     case PPTP_OutCallReply:
164     case PPTP_InCallRequest:
165     case PPTP_InCallReply:
166 	/* Establish PPTP link for address and Call ID found in control message. */
167 	pptp_link = AddPptp(GetOriginalAddress(link), GetDestAddress(link),
168 			    GetAliasAddress(link), cptr->cid1);
169 	break;
170     case PPTP_CallClearRequest:
171     case PPTP_CallDiscNotify:
172 	/* Find PPTP link for address and Call ID found in control message. */
173 	pptp_link = FindPptpOutByCallId(GetOriginalAddress(link),
174 					GetDestAddress(link),
175 					cptr->cid1);
176 	break;
177     default:
178 	return;
179     }
180 
181       if (pptp_link != NULL) {
182 	int accumulate = cptr->cid1;
183 
184 	/* alias the Call Id */
185 	cptr->cid1 = GetAliasPort(pptp_link);
186 
187 	/* Compute TCP checksum for revised packet */
188 	tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
189 	accumulate -= cptr->cid1;
190 	ADJUST_CHECKSUM(accumulate, tc->th_sum);
191 
192 	switch (ctl_type) {
193 	case PPTP_OutCallReply:
194 	case PPTP_InCallReply:
195 	    codes = (PptpCode)(cptr + 1);
196 	    if (codes->resCode == 1)		/* Connection established, */
197 		SetDestCallId(pptp_link,	/* note the Peer's Call ID. */
198 			      cptr->cid2);
199 	    else
200 		SetExpire(pptp_link, 0);	/* Connection refused. */
201 	    break;
202 	case PPTP_CallDiscNotify:		/* Connection closed. */
203 	    SetExpire(pptp_link, 0);
204 	    break;
205 	}
206       }
207 }
208 
209 void
210 AliasHandlePptpIn(struct ip *pip,	   /* IP packet to examine/patch */
211                   struct alias_link *link) /* The PPTP control link */
212 {
213     struct alias_link   *pptp_link;
214     PptpCallId    	cptr;
215     u_int16_t     	*pcall_id;
216     u_int16_t           ctl_type;           /* control message type */
217     struct tcphdr 	*tc;
218 
219     /* Verify valid PPTP control message */
220     if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
221       return;
222 
223     /* Modify certain PPTP messages */
224     switch (ctl_type)
225     {
226     case PPTP_InCallConn:
227     case PPTP_WanErrorNotify:
228     case PPTP_SetLinkInfo:
229       pcall_id = &cptr->cid1;
230       break;
231     case PPTP_OutCallReply:
232     case PPTP_InCallReply:
233       pcall_id = &cptr->cid2;
234       break;
235     case PPTP_CallDiscNotify:			/* Connection closed. */
236       pptp_link = FindPptpInByCallId(GetDestAddress(link),
237 				     GetAliasAddress(link),
238 				     cptr->cid1);
239       if (pptp_link != NULL)
240 	    SetExpire(pptp_link, 0);
241       return;
242     default:
243       return;
244     }
245 
246     /* Find PPTP link for address and Call ID found in PPTP Control Msg */
247     pptp_link = FindPptpInByPeerCallId(GetDestAddress(link),
248 				       GetAliasAddress(link),
249 				       *pcall_id);
250 
251     if (pptp_link != NULL) {
252       int accumulate = *pcall_id;
253 
254       /* De-alias the Peer's Call Id. */
255       *pcall_id = GetOriginalPort(pptp_link);
256 
257       /* Compute TCP checksum for modified packet */
258       tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
259       accumulate -= *pcall_id;
260       ADJUST_CHECKSUM(accumulate, tc->th_sum);
261 
262       if (ctl_type == PPTP_OutCallReply || ctl_type == PPTP_InCallReply) {
263 	    PptpCode codes = (PptpCode)(cptr + 1);
264 
265 	    if (codes->resCode == 1)		/* Connection established, */
266 		SetDestCallId(pptp_link,	/* note the Call ID. */
267 			      cptr->cid1);
268 	    else
269 		SetExpire(pptp_link, 0);	/* Connection refused. */
270       }
271     }
272 }
273 
274 static PptpCallId
275 AliasVerifyPptp(struct ip *pip, u_int16_t *ptype) /* IP packet to examine/patch */
276 {
277     int           	hlen, tlen, dlen;
278     PptpMsgHead   	hptr;
279     struct tcphdr 	*tc;
280 
281     /* Calculate some lengths */
282     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
283     hlen = (pip->ip_hl + tc->th_off) << 2;
284     tlen = ntohs(pip->ip_len);
285     dlen = tlen - hlen;
286 
287     /* Verify data length */
288     if (dlen < (sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds)))
289       return(NULL);
290 
291     /* Move up to PPTP message header */
292     hptr = (PptpMsgHead)(((char *) pip) + hlen);
293 
294     /* Return the control message type */
295     *ptype = ntohs(hptr->type);
296 
297     /* Verify PPTP Control Message */
298     if ((ntohs(hptr->msgType) != PPTP_CTRL_MSG_TYPE) ||
299         (ntohl(hptr->magic) != PPTP_MAGIC))
300       return(NULL);
301 
302     /* Verify data length. */
303     if ((*ptype == PPTP_OutCallReply || *ptype == PPTP_InCallReply) &&
304 	(dlen < sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds) +
305 		sizeof(struct pptpCodes)))
306 	return (NULL);
307     else
308 	return (PptpCallId)(hptr + 1);
309 }
310 
311 
312 int
313 AliasHandlePptpGreOut(struct ip *pip)
314 {
315     GreHdr		*gr;
316     struct alias_link	*link;
317 
318     gr = (GreHdr *)((char *)pip + (pip->ip_hl << 2));
319 
320     /* Check GRE header bits. */
321     if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
322 	return (-1);
323 
324     link = FindPptpOutByPeerCallId(pip->ip_src, pip->ip_dst, gr->gh_call_id);
325     if (link != NULL) {
326 	struct in_addr alias_addr = GetAliasAddress(link);
327 
328 	/* Change source IP address. */
329 	DifferentialChecksum(&pip->ip_sum,
330 			     (u_short *)&alias_addr,
331 			     (u_short *)&pip->ip_src,
332 			     2);
333 	pip->ip_src = alias_addr;
334     }
335 
336     return (0);
337 }
338 
339 
340 int
341 AliasHandlePptpGreIn(struct ip *pip)
342 {
343     GreHdr		*gr;
344     struct alias_link	*link;
345 
346     gr = (GreHdr *)((char *)pip + (pip->ip_hl << 2));
347 
348     /* Check GRE header bits. */
349     if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
350 	return (-1);
351 
352     link = FindPptpInByPeerCallId(pip->ip_src, pip->ip_dst, gr->gh_call_id);
353     if (link != NULL) {
354 	struct in_addr src_addr = GetOriginalAddress(link);
355 
356 	/* De-alias the Peer's Call Id. */
357 	gr->gh_call_id = GetOriginalPort(link);
358 
359 	/* Restore original IP address. */
360 	DifferentialChecksum(&pip->ip_sum,
361 			     (u_short *)&src_addr,
362 			     (u_short *)&pip->ip_dst,
363 			     2);
364 	pip->ip_dst = src_addr;
365     }
366 
367     return (0);
368 }
369