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