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