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