Merge from vendor branch CVS:
[dragonfly.git] / lib / libalias / alias_pptp.c
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: src/lib/libalias/alias_pptp.c,v 1.1.2.4 2001/08/01 09:52:27 obrien Exp $
39  * $DragonFly: src/lib/libalias/alias_pptp.c,v 1.3 2004/08/20 00:08:17 joerg Exp $
40  */
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/param.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 }