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