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/* Includes */
43#ifdef _KERNEL
44#include <sys/param.h>
45#include <sys/limits.h>
46#include <sys/kernel.h>
47#include <sys/module.h>
48#else
49#include <errno.h>
50#include <limits.h>
51#include <sys/types.h>
52#include <stdio.h>
53#endif
54
55#include <netinet/tcp.h>
56
57#ifdef _KERNEL
58#include <netinet/libalias/alias.h>
59#include <netinet/libalias/alias_local.h>
60#include <netinet/libalias/alias_mod.h>
61#else
62#include "alias.h"
63#include "alias_local.h"
64#include "alias_mod.h"
65#endif
66
67#define PPTP_CONTROL_PORT_NUMBER 1723
68
69static void
70AliasHandlePptpOut(struct libalias *, struct ip *, struct alias_link *);
71
72static void
73AliasHandlePptpIn(struct libalias *, struct ip *, struct alias_link *);
74
75static int
76AliasHandlePptpGreOut(struct libalias *, struct ip *);
77
78static int
79AliasHandlePptpGreIn(struct libalias *, struct ip *);
80
81static int
82fingerprint(struct libalias *la, struct alias_data *ah)
83{
84	if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL)
85		return (-1);
86	if (ntohs(*ah->dport) == PPTP_CONTROL_PORT_NUMBER
87	    || ntohs(*ah->sport) == PPTP_CONTROL_PORT_NUMBER)
88		return (0);
89	return (-1);
90}
91
92static int
93fingerprintgre(struct libalias *la, struct alias_data *ah)
94{
95	return (0);
96}
97
98static int
99protohandlerin(struct libalias *la, struct ip *pip, struct alias_data *ah)
100{
101	AliasHandlePptpIn(la, pip, ah->lnk);
102	return (0);
103}
104
105static int
106protohandlerout(struct libalias *la, struct ip *pip, struct alias_data *ah)
107{
108	AliasHandlePptpOut(la, pip, ah->lnk);
109	return (0);
110}
111
112static int
113protohandlergrein(struct libalias *la, struct ip *pip, struct alias_data *ah)
114{
115	if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY ||
116	    AliasHandlePptpGreIn(la, pip) == 0)
117		return (0);
118	return (-1);
119}
120
121static int
122protohandlergreout(struct libalias *la, struct ip *pip, struct alias_data *ah)
123{
124	if (AliasHandlePptpGreOut(la, pip) == 0)
125		return (0);
126	return (-1);
127}
128
129/* Kernel module definition. */
130struct proto_handler handlers[] = {
131	{
132	  .pri = 200,
133	  .dir = IN,
134	  .proto = TCP,
135	  .fingerprint = &fingerprint,
136	  .protohandler = &protohandlerin
137	},
138	{
139	  .pri = 210,
140	  .dir = OUT,
141	  .proto = TCP,
142	  .fingerprint = &fingerprint,
143	  .protohandler = &protohandlerout
144	},
145/*
146 * WATCH OUT!!! these 2 handlers NEED a priority of INT_MAX (highest possible)
147 * cause they will ALWAYS process packets, so they must be the last one
148 * in chain: look fingerprintgre() above.
149 */
150	{
151	  .pri = INT_MAX,
152	  .dir = IN,
153	  .proto = IP,
154	  .fingerprint = &fingerprintgre,
155	  .protohandler = &protohandlergrein
156	},
157	{
158	  .pri = INT_MAX,
159	  .dir = OUT,
160	  .proto = IP,
161	  .fingerprint = &fingerprintgre,
162	  .protohandler = &protohandlergreout
163	},
164	{ EOH }
165};
166static int
167mod_handler(module_t mod, int type, void *data)
168{
169	int error;
170
171	switch (type) {
172	case MOD_LOAD:
173		error = 0;
174		LibAliasAttachHandlers(handlers);
175		break;
176	case MOD_UNLOAD:
177		error = 0;
178		LibAliasDetachHandlers(handlers);
179		break;
180	default:
181		error = EINVAL;
182	}
183	return (error);
184}
185
186#ifdef _KERNEL
187static
188#endif
189moduledata_t alias_mod = {
190       "alias_pptp", mod_handler, NULL
191};
192
193#ifdef _KERNEL
194DECLARE_MODULE(alias_pptp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
195MODULE_VERSION(alias_pptp, 1);
196MODULE_DEPEND(alias_pptp, libalias, 1, 1, 1);
197#endif
198
199/*
200   Alias_pptp.c performs special processing for PPTP sessions under TCP.
201   Specifically, watch PPTP control messages and alias the Call ID or the
202   Peer's Call ID in the appropriate messages.  Note, PPTP requires
203   "de-aliasing" of incoming packets, this is different than any other
204   TCP applications that are currently (ie. FTP, IRC and RTSP) aliased.
205
206   For Call IDs encountered for the first time, a PPTP alias link is created.
207   The PPTP alias link uses the Call ID in place of the original port number.
208   An alias Call ID is created.
209
210   For this routine to work, the PPTP control messages must fit entirely
211   into a single TCP packet.  This is typically the case, but is not
212   required by the spec.
213
214   Unlike some of the other TCP applications that are aliased (ie. FTP,
215   IRC and RTSP), the PPTP control messages that need to be aliased are
216   guaranteed to remain the same length.  The aliased Call ID is a fixed
217   length field.
218
219   Reference: RFC 2637
220
221   Initial version:  May, 2000 (eds)
222*/
223
224/*
225 * PPTP definitions
226 */
227
228struct grehdr {				/* Enhanced GRE header. */
229	u_int16_t	gh_flags;	/* Flags. */
230	u_int16_t	gh_protocol;	/* Protocol type. */
231	u_int16_t	gh_length;	/* Payload length. */
232	u_int16_t	gh_call_id;	/* Call ID. */
233	u_int32_t	gh_seq_no;	/* Sequence number (optional). */
234	u_int32_t	gh_ack_no;	/* Acknowledgment number
235					 * (optional). */
236};
237typedef struct grehdr GreHdr;
238
239/* The PPTP protocol ID used in the GRE 'proto' field. */
240#define PPTP_GRE_PROTO          0x880b
241
242/* Bits that must be set a certain way in all PPTP/GRE packets. */
243#define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
244#define PPTP_INIT_MASK		0xef7fffff
245
246#define PPTP_MAGIC		0x1a2b3c4d
247#define PPTP_CTRL_MSG_TYPE	1
248
249enum {
250	PPTP_StartCtrlConnRequest = 1,
251	PPTP_StartCtrlConnReply = 2,
252	PPTP_StopCtrlConnRequest = 3,
253	PPTP_StopCtrlConnReply = 4,
254	PPTP_EchoRequest = 5,
255	PPTP_EchoReply = 6,
256	PPTP_OutCallRequest = 7,
257	PPTP_OutCallReply = 8,
258	PPTP_InCallRequest = 9,
259	PPTP_InCallReply = 10,
260	PPTP_InCallConn = 11,
261	PPTP_CallClearRequest = 12,
262	PPTP_CallDiscNotify = 13,
263	PPTP_WanErrorNotify = 14,
264	PPTP_SetLinkInfo = 15
265};
266
267/* Message structures */
268struct pptpMsgHead {
269	u_int16_t	length;	/* total length */
270	u_int16_t	msgType;/* PPTP message type */
271	u_int32_t	magic;	/* magic cookie */
272	u_int16_t	type;	/* control message type */
273	u_int16_t	resv0;	/* reserved */
274};
275typedef struct pptpMsgHead *PptpMsgHead;
276
277struct pptpCodes {
278	u_int8_t	resCode;/* Result Code */
279	u_int8_t	errCode;/* Error Code */
280};
281typedef struct pptpCodes *PptpCode;
282
283struct pptpCallIds {
284	u_int16_t	cid1;	/* Call ID field #1 */
285	u_int16_t	cid2;	/* Call ID field #2 */
286};
287typedef struct pptpCallIds *PptpCallId;
288
289static PptpCallId AliasVerifyPptp(struct ip *, u_int16_t *);
290
291static void
292AliasHandlePptpOut(struct libalias *la,
293    struct ip *pip,		/* IP packet to examine/patch */
294    struct alias_link *lnk)	/* The PPTP control link */
295{
296	struct alias_link *pptp_lnk;
297	PptpCallId cptr;
298	PptpCode codes;
299	u_int16_t ctl_type;	/* control message type */
300	struct tcphdr *tc;
301
302	/* Verify valid PPTP control message */
303	if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
304		return;
305
306	/* Modify certain PPTP messages */
307	switch (ctl_type) {
308	case PPTP_OutCallRequest:
309	case PPTP_OutCallReply:
310	case PPTP_InCallRequest:
311	case PPTP_InCallReply:
312		/*
313		 * Establish PPTP link for address and Call ID found in
314		 * control message.
315		 */
316		pptp_lnk = AddPptp(la, GetOriginalAddress(lnk), GetDestAddress(lnk),
317		    GetAliasAddress(lnk), cptr->cid1);
318		break;
319	case PPTP_CallClearRequest:
320	case PPTP_CallDiscNotify:
321		/*
322		 * Find PPTP link for address and Call ID found in control
323		 * message.
324		 */
325		pptp_lnk = FindPptpOutByCallId(la, GetOriginalAddress(lnk),
326		    GetDestAddress(lnk), cptr->cid1);
327		break;
328	default:
329		return;
330	}
331
332	if (pptp_lnk != NULL) {
333		int accumulate = cptr->cid1;
334
335		/* alias the Call Id */
336		cptr->cid1 = GetAliasPort(pptp_lnk);
337
338		/* Compute TCP checksum for revised packet */
339		tc = (struct tcphdr *)ip_next(pip);
340		accumulate -= cptr->cid1;
341		ADJUST_CHECKSUM(accumulate, tc->th_sum);
342
343		switch (ctl_type) {
344		case PPTP_OutCallReply:
345		case PPTP_InCallReply:
346			codes = (PptpCode)(cptr + 1);
347			if (codes->resCode == 1)
348				/* Connection established,
349				 * note the Peer's Call ID. */
350				SetDestCallId(pptp_lnk, cptr->cid2);
351			else
352				/* Connection refused. */
353				SetExpire(pptp_lnk, 0);
354			break;
355		case PPTP_CallDiscNotify:
356			/* Connection closed. */
357			SetExpire(pptp_lnk, 0);
358			break;
359		}
360	}
361}
362
363static void
364AliasHandlePptpIn(struct libalias *la,
365    struct ip *pip,		/* IP packet to examine/patch */
366    struct alias_link *lnk)	/* The PPTP control link */
367{
368	struct alias_link *pptp_lnk;
369	PptpCallId cptr;
370	u_int16_t *pcall_id;
371	u_int16_t ctl_type;	/* control message type */
372	struct tcphdr *tc;
373
374	/* Verify valid PPTP control message */
375	if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
376		return;
377
378	/* Modify certain PPTP messages */
379	switch (ctl_type) {
380	case PPTP_InCallConn:
381	case PPTP_WanErrorNotify:
382	case PPTP_SetLinkInfo:
383		pcall_id = &cptr->cid1;
384		break;
385	case PPTP_OutCallReply:
386	case PPTP_InCallReply:
387		pcall_id = &cptr->cid2;
388		break;
389	case PPTP_CallDiscNotify:
390		/* Connection closed. */
391		pptp_lnk = FindPptpInByCallId(la, GetDestAddress(lnk),
392		    GetAliasAddress(lnk), cptr->cid1);
393		if (pptp_lnk != NULL)
394			SetExpire(pptp_lnk, 0);
395		return;
396	default:
397		return;
398	}
399
400	/* Find PPTP link for address and Call ID found in PPTP Control Msg */
401	pptp_lnk = FindPptpInByPeerCallId(la, GetDestAddress(lnk),
402	    GetAliasAddress(lnk), *pcall_id);
403
404	if (pptp_lnk != NULL) {
405		int accumulate = *pcall_id;
406
407		/* De-alias the Peer's Call Id. */
408		*pcall_id = GetOriginalPort(pptp_lnk);
409
410		/* Compute TCP checksum for modified packet */
411		tc = (struct tcphdr *)ip_next(pip);
412		accumulate -= *pcall_id;
413		ADJUST_CHECKSUM(accumulate, tc->th_sum);
414
415		if (ctl_type == PPTP_OutCallReply ||
416		    ctl_type == PPTP_InCallReply) {
417			PptpCode codes = (PptpCode)(cptr + 1);
418
419			if (codes->resCode == 1)
420				/* Connection established,
421				 * note the Call ID. */
422				SetDestCallId(pptp_lnk, cptr->cid1);
423			else
424				/* Connection refused. */
425				SetExpire(pptp_lnk, 0);
426		}
427	}
428}
429
430static PptpCallId
431AliasVerifyPptp(struct ip *pip, u_int16_t * ptype) /* IP packet to examine/patch */
432{
433	int hlen, tlen, dlen;
434	PptpMsgHead hptr;
435	struct tcphdr *tc;
436
437	/* Calculate some lengths */
438	tc = (struct tcphdr *)ip_next(pip);
439	hlen = (pip->ip_hl + tc->th_off) << 2;
440	tlen = ntohs(pip->ip_len);
441	dlen = tlen - hlen;
442
443	/* Verify data length */
444	if (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds)))
445		return (NULL);
446
447	/* Move up to PPTP message header */
448	hptr = (PptpMsgHead)tcp_next(tc);
449
450	/* Return the control message type */
451	*ptype = ntohs(hptr->type);
452
453	/* Verify PPTP Control Message */
454	if ((ntohs(hptr->msgType) != PPTP_CTRL_MSG_TYPE) ||
455	    (ntohl(hptr->magic) != PPTP_MAGIC))
456		return (NULL);
457
458	/* Verify data length. */
459	if ((*ptype == PPTP_OutCallReply || *ptype == PPTP_InCallReply) &&
460	    (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds) +
461		sizeof(struct pptpCodes))))
462		return (NULL);
463	else
464		return ((PptpCallId)(hptr + 1));
465}
466
467static int
468AliasHandlePptpGreOut(struct libalias *la, struct ip *pip)
469{
470	GreHdr *gr;
471	struct alias_link *lnk;
472
473	gr = (GreHdr *)ip_next(pip);
474
475	/* Check GRE header bits. */
476	if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
477		return (-1);
478
479	lnk = FindPptpOutByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
480	if (lnk != NULL) {
481		struct in_addr alias_addr = GetAliasAddress(lnk);
482
483		/* Change source IP address. */
484		DifferentialChecksum(&pip->ip_sum,
485		    &alias_addr, &pip->ip_src, 2);
486		pip->ip_src = alias_addr;
487	}
488	return (0);
489}
490
491static int
492AliasHandlePptpGreIn(struct libalias *la, struct ip *pip)
493{
494	GreHdr *gr;
495	struct alias_link *lnk;
496
497	gr = (GreHdr *)ip_next(pip);
498
499	/* Check GRE header bits. */
500	if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
501		return (-1);
502
503	lnk = FindPptpInByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
504	if (lnk != NULL) {
505		struct in_addr src_addr = GetOriginalAddress(lnk);
506
507		/* De-alias the Peer's Call Id. */
508		gr->gh_call_id = GetOriginalPort(lnk);
509
510		/* Restore original IP address. */
511		DifferentialChecksum(&pip->ip_sum,
512		    &src_addr, &pip->ip_dst, 2);
513		pip->ip_dst = src_addr;
514	}
515	return (0);
516}
517