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