ip_pptp_pxy.c revision 161351
1/* 2 * Copyright (C) 2002-2003 by Darren Reed 3 * 4 * Simple PPTP transparent proxy for in-kernel use. For use with the NAT 5 * code. 6 * 7 * $Id: ip_pptp_pxy.c,v 2.10.2.13 2006/03/17 10:40:05 darrenr Exp $ 8 * 9 */ 10#define IPF_PPTP_PROXY 11 12typedef struct pptp_hdr { 13 u_short pptph_len; 14 u_short pptph_type; 15 u_32_t pptph_cookie; 16} pptp_hdr_t; 17 18#define PPTP_MSGTYPE_CTL 1 19#define PPTP_MTCTL_STARTREQ 1 20#define PPTP_MTCTL_STARTREP 2 21#define PPTP_MTCTL_STOPREQ 3 22#define PPTP_MTCTL_STOPREP 4 23#define PPTP_MTCTL_ECHOREQ 5 24#define PPTP_MTCTL_ECHOREP 6 25#define PPTP_MTCTL_OUTREQ 7 26#define PPTP_MTCTL_OUTREP 8 27#define PPTP_MTCTL_INREQ 9 28#define PPTP_MTCTL_INREP 10 29#define PPTP_MTCTL_INCONNECT 11 30#define PPTP_MTCTL_CLEAR 12 31#define PPTP_MTCTL_DISCONNECT 13 32#define PPTP_MTCTL_WANERROR 14 33#define PPTP_MTCTL_LINKINFO 15 34 35 36int ippr_pptp_init __P((void)); 37void ippr_pptp_fini __P((void)); 38int ippr_pptp_new __P((fr_info_t *, ap_session_t *, nat_t *)); 39void ippr_pptp_del __P((ap_session_t *)); 40int ippr_pptp_inout __P((fr_info_t *, ap_session_t *, nat_t *)); 41void ippr_pptp_donatstate __P((fr_info_t *, nat_t *, pptp_pxy_t *)); 42int ippr_pptp_message __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); 43int ippr_pptp_nextmessage __P((fr_info_t *, nat_t *, pptp_pxy_t *, int)); 44int ippr_pptp_mctl __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); 45 46static frentry_t pptpfr; 47 48int pptp_proxy_init = 0; 49int ippr_pptp_debug = 0; 50int ippr_pptp_gretimeout = IPF_TTLVAL(120); /* 2 minutes */ 51 52 53/* 54 * PPTP application proxy initialization. 55 */ 56int ippr_pptp_init() 57{ 58 bzero((char *)&pptpfr, sizeof(pptpfr)); 59 pptpfr.fr_ref = 1; 60 pptpfr.fr_age[0] = ippr_pptp_gretimeout; 61 pptpfr.fr_age[1] = ippr_pptp_gretimeout; 62 pptpfr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 63 MUTEX_INIT(&pptpfr.fr_lock, "PPTP proxy rule lock"); 64 pptp_proxy_init = 1; 65 66 return 0; 67} 68 69 70void ippr_pptp_fini() 71{ 72 if (pptp_proxy_init == 1) { 73 MUTEX_DESTROY(&pptpfr.fr_lock); 74 pptp_proxy_init = 0; 75 } 76} 77 78 79/* 80 * Setup for a new PPTP proxy. 81 */ 82int ippr_pptp_new(fin, aps, nat) 83fr_info_t *fin; 84ap_session_t *aps; 85nat_t *nat; 86{ 87 pptp_pxy_t *pptp; 88 ipnat_t *ipn; 89 ip_t *ip; 90 91 ip = fin->fin_ip; 92 93 if (nat_outlookup(fin, 0, IPPROTO_GRE, nat->nat_inip, 94 ip->ip_dst) != NULL) { 95 if (ippr_pptp_debug > 0) 96 printf("ippr_pptp_new: GRE session %s\n", 97 "already exists"); 98 return -1; 99 } 100 101 aps->aps_psiz = sizeof(*pptp); 102 KMALLOCS(aps->aps_data, pptp_pxy_t *, sizeof(*pptp)); 103 if (aps->aps_data == NULL) { 104 if (ippr_pptp_debug > 0) 105 printf("ippr_pptp_new: malloc for aps_data %s\n", 106 "failed"); 107 return -1; 108 } 109 110 /* 111 * Create NAT rule against which the tunnel/transport mapping is 112 * created. This is required because the current NAT rule does not 113 * describe GRE but TCP instead. 114 */ 115 pptp = aps->aps_data; 116 bzero((char *)pptp, sizeof(*pptp)); 117 ipn = &pptp->pptp_rule; 118 ipn->in_ifps[0] = fin->fin_ifp; 119 ipn->in_apr = NULL; 120 ipn->in_use = 1; 121 ipn->in_hits = 1; 122 ipn->in_ippip = 1; 123 if (nat->nat_dir == NAT_OUTBOUND) { 124 ipn->in_nip = ntohl(nat->nat_outip.s_addr); 125 ipn->in_outip = fin->fin_saddr; 126 ipn->in_redir = NAT_MAP; 127 } else if (nat->nat_dir == NAT_INBOUND) { 128 ipn->in_nip = 0; 129 ipn->in_outip = nat->nat_outip.s_addr; 130 ipn->in_redir = NAT_REDIRECT; 131 } 132 ipn->in_inip = nat->nat_inip.s_addr; 133 ipn->in_inmsk = 0xffffffff; 134 ipn->in_outmsk = 0xffffffff; 135 ipn->in_srcip = fin->fin_saddr; 136 ipn->in_srcmsk = 0xffffffff; 137 bcopy(nat->nat_ptr->in_ifnames[0], ipn->in_ifnames[0], 138 sizeof(ipn->in_ifnames[0])); 139 ipn->in_p = IPPROTO_GRE; 140 141 pptp->pptp_side[0].pptps_wptr = pptp->pptp_side[0].pptps_buffer; 142 pptp->pptp_side[1].pptps_wptr = pptp->pptp_side[1].pptps_buffer; 143 return 0; 144} 145 146 147void ippr_pptp_donatstate(fin, nat, pptp) 148fr_info_t *fin; 149nat_t *nat; 150pptp_pxy_t *pptp; 151{ 152 fr_info_t fi; 153 grehdr_t gre; 154 nat_t *nat2; 155 u_char p; 156 ip_t *ip; 157 158 ip = fin->fin_ip; 159 p = ip->ip_p; 160 161 nat2 = pptp->pptp_nat; 162 if ((nat2 == NULL) || (pptp->pptp_state == NULL)) { 163 bcopy((char *)fin, (char *)&fi, sizeof(fi)); 164 bzero((char *)&gre, sizeof(gre)); 165 fi.fin_state = NULL; 166 fi.fin_nat = NULL; 167 fi.fin_fi.fi_p = IPPROTO_GRE; 168 fi.fin_fr = &pptpfr; 169 if ((nat->nat_dir == NAT_OUTBOUND && fin->fin_out) || 170 (nat->nat_dir == NAT_INBOUND && !fin->fin_out)) { 171 fi.fin_data[0] = pptp->pptp_call[0]; 172 fi.fin_data[1] = pptp->pptp_call[1]; 173 } else { 174 fi.fin_data[0] = pptp->pptp_call[1]; 175 fi.fin_data[1] = pptp->pptp_call[0]; 176 } 177 ip = fin->fin_ip; 178 ip->ip_p = IPPROTO_GRE; 179 fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); 180 fi.fin_flx |= FI_IGNORE; 181 fi.fin_dp = &gre; 182 gre.gr_flags = htons(1 << 13); 183 if (fin->fin_out && nat->nat_dir == NAT_INBOUND) { 184 fi.fin_fi.fi_saddr = fin->fin_fi.fi_daddr; 185 fi.fin_fi.fi_daddr = nat->nat_outip.s_addr; 186 } else if (!fin->fin_out && nat->nat_dir == NAT_OUTBOUND) { 187 fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; 188 fi.fin_fi.fi_daddr = fin->fin_fi.fi_saddr; 189 } 190 } 191 192 /* 193 * Update NAT timeout/create NAT if missing. 194 */ 195 if (nat2 != NULL) 196 fr_queueback(&nat2->nat_tqe); 197 else { 198 nat2 = nat_new(&fi, &pptp->pptp_rule, &pptp->pptp_nat, 199 NAT_SLAVE, nat->nat_dir); 200 pptp->pptp_nat = nat2; 201 if (nat2 != NULL) { 202 (void) nat_proto(&fi, nat2, 0); 203 nat_update(&fi, nat2, nat2->nat_ptr); 204 } 205 } 206 207 READ_ENTER(&ipf_state); 208 if (pptp->pptp_state != NULL) { 209 fr_queueback(&pptp->pptp_state->is_sti); 210 RWLOCK_EXIT(&ipf_state); 211 } else { 212 RWLOCK_EXIT(&ipf_state); 213 if (nat2 != NULL) { 214 if (nat->nat_dir == NAT_INBOUND) 215 fi.fin_fi.fi_daddr = nat2->nat_inip.s_addr; 216 else 217 fi.fin_fi.fi_saddr = nat2->nat_inip.s_addr; 218 } 219 fi.fin_ifp = NULL; 220 pptp->pptp_state = fr_addstate(&fi, &pptp->pptp_state, 221 0); 222 if (fi.fin_state != NULL) 223 fr_statederef(&fi, (ipstate_t **)&fi.fin_state); 224 } 225 ip->ip_p = p; 226 return; 227} 228 229 230/* 231 * Try and build up the next PPTP message in the TCP stream and if we can 232 * build it up completely (fits in our buffer) then pass it off to the message 233 * parsing function. 234 */ 235int ippr_pptp_nextmessage(fin, nat, pptp, rev) 236fr_info_t *fin; 237nat_t *nat; 238pptp_pxy_t *pptp; 239int rev; 240{ 241 static const char *funcname = "ippr_pptp_nextmessage"; 242 pptp_side_t *pptps; 243 u_32_t start, end; 244 pptp_hdr_t *hdr; 245 tcphdr_t *tcp; 246 int dlen, off; 247 u_short len; 248 char *msg; 249 250 tcp = fin->fin_dp; 251 dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2); 252 start = ntohl(tcp->th_seq); 253 pptps = &pptp->pptp_side[rev]; 254 off = (char *)tcp - (char *)fin->fin_ip + (TCP_OFF(tcp) << 2) + 255 fin->fin_ipoff; 256 257 if (dlen <= 0) 258 return 0; 259 /* 260 * If the complete data packet is before what we expect to see 261 * "next", just ignore it as the chances are we've already seen it. 262 * The next if statement following this one really just causes packets 263 * ahead of what we've seen to be dropped, implying that something in 264 * the middle went missing and we want to see that first. 265 */ 266 end = start + dlen; 267 if (pptps->pptps_next > end && pptps->pptps_next > start) 268 return 0; 269 270 if (pptps->pptps_next != start) { 271 if (ippr_pptp_debug > 5) 272 printf("%s: next (%x) != start (%x)\n", funcname, 273 pptps->pptps_next, start); 274 return -1; 275 } 276 277 msg = (char *)fin->fin_dp + (TCP_OFF(tcp) << 2); 278 279 while (dlen > 0) { 280 off += pptps->pptps_bytes; 281 if (pptps->pptps_gothdr == 0) { 282 /* 283 * PPTP has an 8 byte header that inclues the cookie. 284 * The start of every message should include one and 285 * it should match 1a2b3c4d. Byte order is ignored, 286 * deliberately, when printing out the error. 287 */ 288 len = MIN(8 - pptps->pptps_bytes, dlen); 289 COPYDATA(fin->fin_m, off, len, pptps->pptps_wptr); 290 pptps->pptps_bytes += len; 291 pptps->pptps_wptr += len; 292 hdr = (pptp_hdr_t *)pptps->pptps_buffer; 293 if (pptps->pptps_bytes == 8) { 294 pptps->pptps_next += 8; 295 if (ntohl(hdr->pptph_cookie) != 0x1a2b3c4d) { 296 if (ippr_pptp_debug > 1) 297 printf("%s: bad cookie (%x)\n", 298 funcname, 299 hdr->pptph_cookie); 300 return -1; 301 } 302 } 303 dlen -= len; 304 msg += len; 305 off += len; 306 307 pptps->pptps_gothdr = 1; 308 len = ntohs(hdr->pptph_len); 309 pptps->pptps_len = len; 310 pptps->pptps_nexthdr += len; 311 312 /* 313 * If a message is too big for the buffer, just set 314 * the fields for the next message to come along. 315 * The messages defined in RFC 2637 will not exceed 316 * 512 bytes (in total length) so this is likely a 317 * bad data packet, anyway. 318 */ 319 if (len > sizeof(pptps->pptps_buffer)) { 320 if (ippr_pptp_debug > 3) 321 printf("%s: message too big (%d)\n", 322 funcname, len); 323 pptps->pptps_next = pptps->pptps_nexthdr; 324 pptps->pptps_wptr = pptps->pptps_buffer; 325 pptps->pptps_gothdr = 0; 326 pptps->pptps_bytes = 0; 327 pptps->pptps_len = 0; 328 break; 329 } 330 } 331 332 len = MIN(pptps->pptps_len - pptps->pptps_bytes, dlen); 333 COPYDATA(fin->fin_m, off, len, pptps->pptps_wptr); 334 pptps->pptps_bytes += len; 335 pptps->pptps_wptr += len; 336 pptps->pptps_next += len; 337 338 if (pptps->pptps_len > pptps->pptps_bytes) 339 break; 340 341 ippr_pptp_message(fin, nat, pptp, pptps); 342 pptps->pptps_wptr = pptps->pptps_buffer; 343 pptps->pptps_gothdr = 0; 344 pptps->pptps_bytes = 0; 345 pptps->pptps_len = 0; 346 347 start += len; 348 msg += len; 349 dlen -= len; 350 } 351 352 return 0; 353} 354 355 356/* 357 * handle a complete PPTP message 358 */ 359int ippr_pptp_message(fin, nat, pptp, pptps) 360fr_info_t *fin; 361nat_t *nat; 362pptp_pxy_t *pptp; 363pptp_side_t *pptps; 364{ 365 pptp_hdr_t *hdr = (pptp_hdr_t *)pptps->pptps_buffer; 366 367 switch (ntohs(hdr->pptph_type)) 368 { 369 case PPTP_MSGTYPE_CTL : 370 ippr_pptp_mctl(fin, nat, pptp, pptps); 371 break; 372 373 default : 374 break; 375 } 376 return 0; 377} 378 379 380/* 381 * handle a complete PPTP control message 382 */ 383int ippr_pptp_mctl(fin, nat, pptp, pptps) 384fr_info_t *fin; 385nat_t *nat; 386pptp_pxy_t *pptp; 387pptp_side_t *pptps; 388{ 389 u_short *buffer = (u_short *)(pptps->pptps_buffer); 390 pptp_side_t *pptpo; 391 392 if (pptps == &pptp->pptp_side[0]) 393 pptpo = &pptp->pptp_side[1]; 394 else 395 pptpo = &pptp->pptp_side[0]; 396 397 /* 398 * Breakout to handle all the various messages. Most are just state 399 * transition. 400 */ 401 switch (ntohs(buffer[4])) 402 { 403 case PPTP_MTCTL_STARTREQ : 404 pptps->pptps_state = PPTP_MTCTL_STARTREQ; 405 break; 406 case PPTP_MTCTL_STARTREP : 407 if (pptpo->pptps_state == PPTP_MTCTL_STARTREQ) 408 pptps->pptps_state = PPTP_MTCTL_STARTREP; 409 break; 410 case PPTP_MTCTL_STOPREQ : 411 pptps->pptps_state = PPTP_MTCTL_STOPREQ; 412 break; 413 case PPTP_MTCTL_STOPREP : 414 if (pptpo->pptps_state == PPTP_MTCTL_STOPREQ) 415 pptps->pptps_state = PPTP_MTCTL_STOPREP; 416 break; 417 case PPTP_MTCTL_ECHOREQ : 418 pptps->pptps_state = PPTP_MTCTL_ECHOREQ; 419 break; 420 case PPTP_MTCTL_ECHOREP : 421 if (pptpo->pptps_state == PPTP_MTCTL_ECHOREQ) 422 pptps->pptps_state = PPTP_MTCTL_ECHOREP; 423 break; 424 case PPTP_MTCTL_OUTREQ : 425 pptps->pptps_state = PPTP_MTCTL_OUTREQ; 426 break; 427 case PPTP_MTCTL_OUTREP : 428 if (pptpo->pptps_state == PPTP_MTCTL_OUTREQ) { 429 pptps->pptps_state = PPTP_MTCTL_OUTREP; 430 pptp->pptp_call[0] = buffer[7]; 431 pptp->pptp_call[1] = buffer[6]; 432 ippr_pptp_donatstate(fin, nat, pptp); 433 } 434 break; 435 case PPTP_MTCTL_INREQ : 436 pptps->pptps_state = PPTP_MTCTL_INREQ; 437 break; 438 case PPTP_MTCTL_INREP : 439 if (pptpo->pptps_state == PPTP_MTCTL_INREQ) { 440 pptps->pptps_state = PPTP_MTCTL_INREP; 441 pptp->pptp_call[0] = buffer[7]; 442 pptp->pptp_call[1] = buffer[6]; 443 ippr_pptp_donatstate(fin, nat, pptp); 444 } 445 break; 446 case PPTP_MTCTL_INCONNECT : 447 pptps->pptps_state = PPTP_MTCTL_INCONNECT; 448 break; 449 case PPTP_MTCTL_CLEAR : 450 pptps->pptps_state = PPTP_MTCTL_CLEAR; 451 break; 452 case PPTP_MTCTL_DISCONNECT : 453 pptps->pptps_state = PPTP_MTCTL_DISCONNECT; 454 break; 455 case PPTP_MTCTL_WANERROR : 456 pptps->pptps_state = PPTP_MTCTL_WANERROR; 457 break; 458 case PPTP_MTCTL_LINKINFO : 459 pptps->pptps_state = PPTP_MTCTL_LINKINFO; 460 break; 461 } 462 463 return 0; 464} 465 466 467/* 468 * For outgoing PPTP packets. refresh timeouts for NAT & state entries, if 469 * we can. If they have disappeared, recreate them. 470 */ 471int ippr_pptp_inout(fin, aps, nat) 472fr_info_t *fin; 473ap_session_t *aps; 474nat_t *nat; 475{ 476 pptp_pxy_t *pptp; 477 tcphdr_t *tcp; 478 int rev; 479 480 if ((fin->fin_out == 1) && (nat->nat_dir == NAT_INBOUND)) 481 rev = 1; 482 else if ((fin->fin_out == 0) && (nat->nat_dir == NAT_OUTBOUND)) 483 rev = 1; 484 else 485 rev = 0; 486 487 tcp = (tcphdr_t *)fin->fin_dp; 488 if ((tcp->th_flags & TH_OPENING) == TH_OPENING) { 489 pptp = (pptp_pxy_t *)aps->aps_data; 490 pptp->pptp_side[1 - rev].pptps_next = ntohl(tcp->th_ack); 491 pptp->pptp_side[1 - rev].pptps_nexthdr = ntohl(tcp->th_ack); 492 pptp->pptp_side[rev].pptps_next = ntohl(tcp->th_seq) + 1; 493 pptp->pptp_side[rev].pptps_nexthdr = ntohl(tcp->th_seq) + 1; 494 } 495 return ippr_pptp_nextmessage(fin, nat, (pptp_pxy_t *)aps->aps_data, 496 rev); 497} 498 499 500/* 501 * clean up after ourselves. 502 */ 503void ippr_pptp_del(aps) 504ap_session_t *aps; 505{ 506 pptp_pxy_t *pptp; 507 508 pptp = aps->aps_data; 509 510 if (pptp != NULL) { 511 /* 512 * Don't bother changing any of the NAT structure details, 513 * *_del() is on a callback from aps_free(), from nat_delete() 514 */ 515 516 READ_ENTER(&ipf_state); 517 if (pptp->pptp_state != NULL) { 518 pptp->pptp_state->is_die = fr_ticks + 1; 519 pptp->pptp_state->is_me = NULL; 520 fr_queuefront(&pptp->pptp_state->is_sti); 521 } 522 RWLOCK_EXIT(&ipf_state); 523 524 pptp->pptp_state = NULL; 525 pptp->pptp_nat = NULL; 526 } 527} 528