xref: /freebsd/sys/netinet/libalias/alias_pptp.c (revision 9608d7e2cd58c1a7fff6562810f2ce519e6ec50a)
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$");
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 
83 struct 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 };
92 typedef 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 
104 enum {
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 
144 static PptpCallId AliasVerifyPptp(struct ip *, u_int16_t *);
145 
146 
147 void
148 AliasHandlePptpOut(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 
211 void
212 AliasHandlePptpIn(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 
277 static PptpCallId
278 AliasVerifyPptp(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 
315 int
316 AliasHandlePptpGreOut(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 
343 int
344 AliasHandlePptpGreIn(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