xref: /freebsd/sys/netinet/libalias/alias_pptp.c (revision 2a4a1db342263067035ce69a4017c645da63455d)
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 ip *pip,	    /* IP packet to examine/patch */
149                    struct alias_link *link) /* The PPTP control link */
150 {
151     struct alias_link   *pptp_link;
152     PptpCallId    	cptr;
153     PptpCode            codes;
154     u_int16_t           ctl_type;           /* control message type */
155     struct tcphdr 	*tc;
156 
157     /* Verify valid PPTP control message */
158     if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
159       return;
160 
161     /* Modify certain PPTP messages */
162     switch (ctl_type) {
163     case PPTP_OutCallRequest:
164     case PPTP_OutCallReply:
165     case PPTP_InCallRequest:
166     case PPTP_InCallReply:
167 	/* Establish PPTP link for address and Call ID found in control message. */
168 	pptp_link = AddPptp(GetOriginalAddress(link), GetDestAddress(link),
169 			    GetAliasAddress(link), cptr->cid1);
170 	break;
171     case PPTP_CallClearRequest:
172     case PPTP_CallDiscNotify:
173 	/* Find PPTP link for address and Call ID found in control message. */
174 	pptp_link = FindPptpOutByCallId(GetOriginalAddress(link),
175 					GetDestAddress(link),
176 					cptr->cid1);
177 	break;
178     default:
179 	return;
180     }
181 
182       if (pptp_link != NULL) {
183 	int accumulate = cptr->cid1;
184 
185 	/* alias the Call Id */
186 	cptr->cid1 = GetAliasPort(pptp_link);
187 
188 	/* Compute TCP checksum for revised packet */
189 	tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
190 	accumulate -= cptr->cid1;
191 	ADJUST_CHECKSUM(accumulate, tc->th_sum);
192 
193 	switch (ctl_type) {
194 	case PPTP_OutCallReply:
195 	case PPTP_InCallReply:
196 	    codes = (PptpCode)(cptr + 1);
197 	    if (codes->resCode == 1)		/* Connection established, */
198 		SetDestCallId(pptp_link,	/* note the Peer's Call ID. */
199 			      cptr->cid2);
200 	    else
201 		SetExpire(pptp_link, 0);	/* Connection refused. */
202 	    break;
203 	case PPTP_CallDiscNotify:		/* Connection closed. */
204 	    SetExpire(pptp_link, 0);
205 	    break;
206 	}
207       }
208 }
209 
210 void
211 AliasHandlePptpIn(struct ip *pip,	   /* IP packet to examine/patch */
212                   struct alias_link *link) /* The PPTP control link */
213 {
214     struct alias_link   *pptp_link;
215     PptpCallId    	cptr;
216     u_int16_t     	*pcall_id;
217     u_int16_t           ctl_type;           /* control message type */
218     struct tcphdr 	*tc;
219 
220     /* Verify valid PPTP control message */
221     if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
222       return;
223 
224     /* Modify certain PPTP messages */
225     switch (ctl_type)
226     {
227     case PPTP_InCallConn:
228     case PPTP_WanErrorNotify:
229     case PPTP_SetLinkInfo:
230       pcall_id = &cptr->cid1;
231       break;
232     case PPTP_OutCallReply:
233     case PPTP_InCallReply:
234       pcall_id = &cptr->cid2;
235       break;
236     case PPTP_CallDiscNotify:			/* Connection closed. */
237       pptp_link = FindPptpInByCallId(GetDestAddress(link),
238 				     GetAliasAddress(link),
239 				     cptr->cid1);
240       if (pptp_link != NULL)
241 	    SetExpire(pptp_link, 0);
242       return;
243     default:
244       return;
245     }
246 
247     /* Find PPTP link for address and Call ID found in PPTP Control Msg */
248     pptp_link = FindPptpInByPeerCallId(GetDestAddress(link),
249 				       GetAliasAddress(link),
250 				       *pcall_id);
251 
252     if (pptp_link != NULL) {
253       int accumulate = *pcall_id;
254 
255       /* De-alias the Peer's Call Id. */
256       *pcall_id = GetOriginalPort(pptp_link);
257 
258       /* Compute TCP checksum for modified packet */
259       tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
260       accumulate -= *pcall_id;
261       ADJUST_CHECKSUM(accumulate, tc->th_sum);
262 
263       if (ctl_type == PPTP_OutCallReply || ctl_type == PPTP_InCallReply) {
264 	    PptpCode codes = (PptpCode)(cptr + 1);
265 
266 	    if (codes->resCode == 1)		/* Connection established, */
267 		SetDestCallId(pptp_link,	/* note the Call ID. */
268 			      cptr->cid1);
269 	    else
270 		SetExpire(pptp_link, 0);	/* Connection refused. */
271       }
272     }
273 }
274 
275 static PptpCallId
276 AliasVerifyPptp(struct ip *pip, u_int16_t *ptype) /* IP packet to examine/patch */
277 {
278     int           	hlen, tlen, dlen;
279     PptpMsgHead   	hptr;
280     struct tcphdr 	*tc;
281 
282     /* Calculate some lengths */
283     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
284     hlen = (pip->ip_hl + tc->th_off) << 2;
285     tlen = ntohs(pip->ip_len);
286     dlen = tlen - hlen;
287 
288     /* Verify data length */
289     if (dlen < (sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds)))
290       return(NULL);
291 
292     /* Move up to PPTP message header */
293     hptr = (PptpMsgHead)(((char *) pip) + hlen);
294 
295     /* Return the control message type */
296     *ptype = ntohs(hptr->type);
297 
298     /* Verify PPTP Control Message */
299     if ((ntohs(hptr->msgType) != PPTP_CTRL_MSG_TYPE) ||
300         (ntohl(hptr->magic) != PPTP_MAGIC))
301       return(NULL);
302 
303     /* Verify data length. */
304     if ((*ptype == PPTP_OutCallReply || *ptype == PPTP_InCallReply) &&
305 	(dlen < sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds) +
306 		sizeof(struct pptpCodes)))
307 	return (NULL);
308     else
309 	return (PptpCallId)(hptr + 1);
310 }
311 
312 
313 int
314 AliasHandlePptpGreOut(struct ip *pip)
315 {
316     GreHdr		*gr;
317     struct alias_link	*link;
318 
319     gr = (GreHdr *)((char *)pip + (pip->ip_hl << 2));
320 
321     /* Check GRE header bits. */
322     if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
323 	return (-1);
324 
325     link = FindPptpOutByPeerCallId(pip->ip_src, pip->ip_dst, gr->gh_call_id);
326     if (link != NULL) {
327 	struct in_addr alias_addr = GetAliasAddress(link);
328 
329 	/* Change source IP address. */
330 	DifferentialChecksum(&pip->ip_sum,
331 			     (u_short *)&alias_addr,
332 			     (u_short *)&pip->ip_src,
333 			     2);
334 	pip->ip_src = alias_addr;
335     }
336 
337     return (0);
338 }
339 
340 
341 int
342 AliasHandlePptpGreIn(struct ip *pip)
343 {
344     GreHdr		*gr;
345     struct alias_link	*link;
346 
347     gr = (GreHdr *)((char *)pip + (pip->ip_hl << 2));
348 
349     /* Check GRE header bits. */
350     if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
351 	return (-1);
352 
353     link = FindPptpInByPeerCallId(pip->ip_src, pip->ip_dst, gr->gh_call_id);
354     if (link != NULL) {
355 	struct in_addr src_addr = GetOriginalAddress(link);
356 
357 	/* De-alias the Peer's Call Id. */
358 	gr->gh_call_id = GetOriginalPort(link);
359 
360 	/* Restore original IP address. */
361 	DifferentialChecksum(&pip->ip_sum,
362 			     (u_short *)&src_addr,
363 			     (u_short *)&pip->ip_dst,
364 			     2);
365 	pip->ip_dst = src_addr;
366     }
367 
368     return (0);
369 }
370