Home | History | Annotate | Line # | Download | only in netinet
ip_pptp_pxy.c revision 1.2
      1 /*	$NetBSD: ip_pptp_pxy.c,v 1.2 2012/03/23 20:39:50 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2011 by Darren Reed.
      5  *
      6  * Simple PPTP transparent proxy for in-kernel use.  For use with the NAT
      7  * code.
      8  *
      9  * Id: ip_pptp_pxy.c,v 2.38.2.2 2012/01/26 05:29:12 darrenr Exp
     10  *
     11  */
     12 
     13 #include <sys/cdefs.h>
     14 __KERNEL_RCSID(1, "$NetBSD: ip_pptp_pxy.c,v 1.2 2012/03/23 20:39:50 christos Exp $");
     15 
     16 #define	IPF_PPTP_PROXY
     17 
     18 typedef	struct pptp_hdr {
     19 	u_short	pptph_len;
     20 	u_short	pptph_type;
     21 	u_32_t	pptph_cookie;
     22 } pptp_hdr_t;
     23 
     24 #define	PPTP_MSGTYPE_CTL	1
     25 #define	PPTP_MTCTL_STARTREQ	1
     26 #define	PPTP_MTCTL_STARTREP	2
     27 #define	PPTP_MTCTL_STOPREQ	3
     28 #define	PPTP_MTCTL_STOPREP	4
     29 #define	PPTP_MTCTL_ECHOREQ	5
     30 #define	PPTP_MTCTL_ECHOREP	6
     31 #define	PPTP_MTCTL_OUTREQ	7
     32 #define	PPTP_MTCTL_OUTREP	8
     33 #define	PPTP_MTCTL_INREQ	9
     34 #define	PPTP_MTCTL_INREP	10
     35 #define	PPTP_MTCTL_INCONNECT	11
     36 #define	PPTP_MTCTL_CLEAR	12
     37 #define	PPTP_MTCTL_DISCONNECT	13
     38 #define	PPTP_MTCTL_WANERROR	14
     39 #define	PPTP_MTCTL_LINKINFO	15
     40 
     41 
     42 void ipf_p_pptp_main_load(void);
     43 void ipf_p_pptp_main_unload(void);
     44 int ipf_p_pptp_new(void *, fr_info_t *, ap_session_t *, nat_t *);
     45 void ipf_p_pptp_del(ipf_main_softc_t *, ap_session_t *);
     46 int ipf_p_pptp_inout(void *, fr_info_t *, ap_session_t *, nat_t *);
     47 void ipf_p_pptp_donatstate(fr_info_t *, nat_t *, pptp_pxy_t *);
     48 int ipf_p_pptp_message(fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *);
     49 int ipf_p_pptp_nextmessage(fr_info_t *, nat_t *, pptp_pxy_t *, int);
     50 int ipf_p_pptp_mctl(fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *);
     51 
     52 static	frentry_t	pptpfr;
     53 
     54 static	int	pptp_proxy_init = 0;
     55 static	int	ipf_p_pptp_debug = 0;
     56 static	int	ipf_p_pptp_gretimeout = IPF_TTLVAL(120);	/* 2 minutes */
     57 
     58 
     59 /*
     60  * PPTP application proxy initialization.
     61  */
     62 void
     63 ipf_p_pptp_main_load(void)
     64 {
     65 	bzero((char *)&pptpfr, sizeof(pptpfr));
     66 	pptpfr.fr_ref = 1;
     67 	pptpfr.fr_age[0] = ipf_p_pptp_gretimeout;
     68 	pptpfr.fr_age[1] = ipf_p_pptp_gretimeout;
     69 	pptpfr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
     70 	MUTEX_INIT(&pptpfr.fr_lock, "PPTP proxy rule lock");
     71 	pptp_proxy_init = 1;
     72 }
     73 
     74 
     75 void
     76 ipf_p_pptp_main_unload(void)
     77 {
     78 	if (pptp_proxy_init == 1) {
     79 		MUTEX_DESTROY(&pptpfr.fr_lock);
     80 		pptp_proxy_init = 0;
     81 	}
     82 }
     83 
     84 
     85 /*
     86  * Setup for a new PPTP proxy.
     87  *
     88  * NOTE: The printf's are broken up with %s in them to prevent them being
     89  * optimised into puts statements on FreeBSD (this doesn't exist in the kernel)
     90  */
     91 int
     92 ipf_p_pptp_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
     93 {
     94 	pptp_pxy_t *pptp;
     95 	ipnat_t *ipn, *np;
     96 	ip_t *ip;
     97 
     98 	ip = fin->fin_ip;
     99 
    100 	if (ipf_nat_outlookup(fin, 0, IPPROTO_GRE, nat->nat_osrcip,
    101 			  ip->ip_dst) != NULL) {
    102 		if (ipf_p_pptp_debug > 0)
    103 			printf("ipf_p_pptp_new: GRE session already exists\n");
    104 		return -1;
    105 	}
    106 	np = nat->nat_ptr;
    107 
    108 	aps->aps_psiz = sizeof(*pptp) + np->in_namelen;
    109 	KMALLOCS(aps->aps_data, pptp_pxy_t *, aps->aps_psiz);
    110 	if (aps->aps_data == NULL) {
    111 		if (ipf_p_pptp_debug > 0)
    112 			printf("ipf_p_pptp_new: malloc for aps_data failed\n");
    113 		return -1;
    114 	}
    115 
    116 	/*
    117 	 * Create NAT rule against which the tunnel/transport mapping is
    118 	 * created.  This is required because the current NAT rule does not
    119 	 * describe GRE but TCP instead.
    120 	 */
    121 	pptp = aps->aps_data;
    122 	bzero((char *)pptp, sizeof(*pptp));
    123 	ipn = &pptp->pptp_rule;
    124 	ipn->in_ifps[0] = fin->fin_ifp;
    125 	ipn->in_apr = NULL;
    126 	ipn->in_use = 1;
    127 	ipn->in_hits = 1;
    128 	ipn->in_ippip = 1;
    129 	ipn->in_snip = ntohl(nat->nat_nsrcaddr);
    130 	ipn->in_nsrcaddr = fin->fin_saddr;
    131 	ipn->in_dnip = ntohl(nat->nat_ndstaddr);
    132 	ipn->in_ndstaddr = nat->nat_ndstaddr;
    133 	ipn->in_redir = np->in_redir;
    134 	ipn->in_osrcaddr = nat->nat_osrcaddr;
    135 	ipn->in_odstaddr = nat->nat_odstaddr;
    136 	ipn->in_osrcmsk = 0xffffffff;
    137 	ipn->in_nsrcmsk = 0xffffffff;
    138 	ipn->in_odstmsk = 0xffffffff;
    139 	ipn->in_ndstmsk = 0xffffffff;
    140 	MUTEX_INIT(&ipn->in_lock, "pptp proxy NAT rule");
    141 
    142 	ipn->in_namelen = np->in_namelen;
    143 	bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen);
    144 	ipn->in_ifnames[0] = np->in_ifnames[0];
    145 	ipn->in_ifnames[1] = np->in_ifnames[1];
    146 
    147 	ipn->in_pr[0] = IPPROTO_GRE;
    148 	ipn->in_pr[1] = IPPROTO_GRE;
    149 
    150 	pptp->pptp_side[0].pptps_wptr = pptp->pptp_side[0].pptps_buffer;
    151 	pptp->pptp_side[1].pptps_wptr = pptp->pptp_side[1].pptps_buffer;
    152 	return 0;
    153 }
    154 
    155 
    156 void
    157 ipf_p_pptp_donatstate(fr_info_t *fin, nat_t *nat, pptp_pxy_t *pptp)
    158 {
    159 	ipf_main_softc_t *softc = fin->fin_main_soft;
    160 	fr_info_t fi;
    161 	grehdr_t gre;
    162 	nat_t *nat2;
    163 	u_char p;
    164 	ip_t *ip;
    165 
    166 	ip = fin->fin_ip;
    167 	p = ip->ip_p;
    168 
    169 	nat2 = pptp->pptp_nat;
    170 	if ((nat2 == NULL) || (pptp->pptp_state == NULL)) {
    171 		bcopy((char *)fin, (char *)&fi, sizeof(fi));
    172 		bzero((char *)&gre, sizeof(gre));
    173 		fi.fin_fi.fi_p = IPPROTO_GRE;
    174 		fi.fin_fr = &pptpfr;
    175 		if ((nat->nat_dir == NAT_OUTBOUND && fin->fin_out) ||
    176 		    (nat->nat_dir == NAT_INBOUND && !fin->fin_out)) {
    177 			fi.fin_data[0] = pptp->pptp_call[0];
    178 			fi.fin_data[1] = pptp->pptp_call[1];
    179 		} else {
    180 			fi.fin_data[0] = pptp->pptp_call[1];
    181 			fi.fin_data[1] = pptp->pptp_call[0];
    182 		}
    183 		ip = fin->fin_ip;
    184 		ip->ip_p = IPPROTO_GRE;
    185 		fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG);
    186 		fi.fin_flx |= FI_IGNORE;
    187 		fi.fin_dp = &gre;
    188 		gre.gr_flags = htons(1 << 13);
    189 
    190 		fi.fin_fi.fi_saddr = nat->nat_osrcaddr;
    191 		fi.fin_fi.fi_daddr = nat->nat_odstaddr;
    192 	}
    193 
    194 	/*
    195 	 * Update NAT timeout/create NAT if missing.
    196 	 */
    197 	if (nat2 != NULL)
    198 		ipf_queueback(softc->ipf_ticks, &nat2->nat_tqe);
    199 	else {
    200 #ifdef USE_MUTEXES
    201 		ipf_nat_softc_t *softn = softc->ipf_nat_soft;
    202 #endif
    203 
    204 		MUTEX_ENTER(&softn->ipf_nat_new);
    205 		nat2 = ipf_nat_add(&fi, &pptp->pptp_rule, &pptp->pptp_nat,
    206 				   NAT_SLAVE, nat->nat_dir);
    207 		MUTEX_EXIT(&softn->ipf_nat_new);
    208 		pptp->pptp_nat = nat2;
    209 		if (nat2 != NULL) {
    210 			(void) ipf_nat_proto(&fi, nat2, 0);
    211 			MUTEX_ENTER(&nat2->nat_lock);
    212 			ipf_nat_update(&fi, nat2);
    213 			MUTEX_EXIT(&nat2->nat_lock);
    214 		}
    215 	}
    216 
    217 	READ_ENTER(&softc->ipf_state);
    218 	if (pptp->pptp_state != NULL) {
    219 		ipf_queueback(softc->ipf_ticks, &pptp->pptp_state->is_sti);
    220 		RWLOCK_EXIT(&softc->ipf_state);
    221 	} else {
    222 		RWLOCK_EXIT(&softc->ipf_state);
    223 		if (nat2 != NULL) {
    224 			if (nat->nat_dir == NAT_INBOUND)
    225 				fi.fin_fi.fi_daddr = nat2->nat_ndstaddr;
    226 			else
    227 				fi.fin_fi.fi_saddr = nat2->nat_osrcaddr;
    228 		}
    229 		fi.fin_ifp = NULL;
    230 		(void) ipf_state_add(softc, &fi, &pptp->pptp_state, 0);
    231 	}
    232 	ip->ip_p = p;
    233 	return;
    234 }
    235 
    236 
    237 /*
    238  * Try and build up the next PPTP message in the TCP stream and if we can
    239  * build it up completely (fits in our buffer) then pass it off to the message
    240  * parsing function.
    241  */
    242 int
    243 ipf_p_pptp_nextmessage(fr_info_t *fin, nat_t *nat, pptp_pxy_t *pptp, int rev)
    244 {
    245 	static const char *funcname = "ipf_p_pptp_nextmessage";
    246 	pptp_side_t *pptps;
    247 	u_32_t start, end;
    248 	pptp_hdr_t *hdr;
    249 	tcphdr_t *tcp;
    250 	int dlen, off;
    251 	u_short len;
    252 	char *msg;
    253 
    254 	tcp = fin->fin_dp;
    255 	dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2);
    256 	start = ntohl(tcp->th_seq);
    257 	pptps = &pptp->pptp_side[rev];
    258 	off = (char *)tcp - (char *)fin->fin_ip + (TCP_OFF(tcp) << 2) +
    259 	      fin->fin_ipoff;
    260 
    261 	if (dlen <= 0)
    262 		return 0;
    263 	/*
    264 	 * If the complete data packet is before what we expect to see
    265 	 * "next", just ignore it as the chances are we've already seen it.
    266 	 * The next if statement following this one really just causes packets
    267 	 * ahead of what we've seen to be dropped, implying that something in
    268 	 * the middle went missing and we want to see that first.
    269 	 */
    270 	end = start + dlen;
    271 	if (pptps->pptps_next > end && pptps->pptps_next > start)
    272 		return 0;
    273 
    274 	if (pptps->pptps_next != start) {
    275 		if (ipf_p_pptp_debug > 5)
    276 			printf("%s: next (%x) != start (%x)\n", funcname,
    277 				pptps->pptps_next, start);
    278 		return -1;
    279 	}
    280 
    281 	msg = (char *)fin->fin_dp + (TCP_OFF(tcp) << 2);
    282 
    283 	while (dlen > 0) {
    284 		off += pptps->pptps_bytes;
    285 		if (pptps->pptps_gothdr == 0) {
    286 			/*
    287 			 * PPTP has an 8 byte header that inclues the cookie.
    288 			 * The start of every message should include one and
    289 			 * it should match 1a2b3c4d.  Byte order is ignored,
    290 			 * deliberately, when printing out the error.
    291 			 */
    292 			len = MIN(8 - pptps->pptps_bytes, dlen);
    293 			COPYDATA(fin->fin_m, off, len, pptps->pptps_wptr);
    294 			pptps->pptps_bytes += len;
    295 			pptps->pptps_wptr += len;
    296 			hdr = (pptp_hdr_t *)pptps->pptps_buffer;
    297 			if (pptps->pptps_bytes == 8) {
    298 				pptps->pptps_next += 8;
    299 				if (ntohl(hdr->pptph_cookie) != 0x1a2b3c4d) {
    300 					if (ipf_p_pptp_debug > 1)
    301 						printf("%s: bad cookie (%x)\n",
    302 						       funcname,
    303 						       hdr->pptph_cookie);
    304 					return -1;
    305 				}
    306 			}
    307 			dlen -= len;
    308 			msg += len;
    309 			off += len;
    310 
    311 			pptps->pptps_gothdr = 1;
    312 			len = ntohs(hdr->pptph_len);
    313 			pptps->pptps_len = len;
    314 			pptps->pptps_nexthdr += len;
    315 
    316 			/*
    317 			 * If a message is too big for the buffer, just set
    318 			 * the fields for the next message to come along.
    319 			 * The messages defined in RFC 2637 will not exceed
    320 			 * 512 bytes (in total length) so this is likely a
    321 			 * bad data packet, anyway.
    322 			 */
    323 			if (len > sizeof(pptps->pptps_buffer)) {
    324 				if (ipf_p_pptp_debug > 3)
    325 					printf("%s: message too big (%d)\n",
    326 					       funcname, len);
    327 				pptps->pptps_next = pptps->pptps_nexthdr;
    328 				pptps->pptps_wptr = pptps->pptps_buffer;
    329 				pptps->pptps_gothdr = 0;
    330 				pptps->pptps_bytes = 0;
    331 				pptps->pptps_len = 0;
    332 				break;
    333 			}
    334 		}
    335 
    336 		len = MIN(pptps->pptps_len - pptps->pptps_bytes, dlen);
    337 		COPYDATA(fin->fin_m, off, len, pptps->pptps_wptr);
    338 		pptps->pptps_bytes += len;
    339 		pptps->pptps_wptr += len;
    340 		pptps->pptps_next += len;
    341 
    342 		if (pptps->pptps_len > pptps->pptps_bytes)
    343 			break;
    344 
    345 		ipf_p_pptp_message(fin, nat, pptp, pptps);
    346 		pptps->pptps_wptr = pptps->pptps_buffer;
    347 		pptps->pptps_gothdr = 0;
    348 		pptps->pptps_bytes = 0;
    349 		pptps->pptps_len = 0;
    350 
    351 		start += len;
    352 		msg += len;
    353 		dlen -= len;
    354 	}
    355 
    356 	return 0;
    357 }
    358 
    359 
    360 /*
    361  * handle a complete PPTP message
    362  */
    363 int
    364 ipf_p_pptp_message(fr_info_t *fin, nat_t *nat, pptp_pxy_t *pptp,
    365     pptp_side_t *pptps)
    366 {
    367 	pptp_hdr_t *hdr = (pptp_hdr_t *)pptps->pptps_buffer;
    368 
    369 	switch (ntohs(hdr->pptph_type))
    370 	{
    371 	case PPTP_MSGTYPE_CTL :
    372 		ipf_p_pptp_mctl(fin, nat, pptp, pptps);
    373 		break;
    374 
    375 	default :
    376 		break;
    377 	}
    378 	return 0;
    379 }
    380 
    381 
    382 /*
    383  * handle a complete PPTP control message
    384  */
    385 int
    386 ipf_p_pptp_mctl(fr_info_t *fin, nat_t *nat, pptp_pxy_t *pptp,
    387     pptp_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 			ipf_p_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 			ipf_p_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  */
    471 int
    472 ipf_p_pptp_inout(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
    473 {
    474 	pptp_pxy_t *pptp;
    475 	tcphdr_t *tcp;
    476 	int rev;
    477 
    478 	if ((fin->fin_out == 1) && (nat->nat_dir == NAT_INBOUND))
    479 		rev = 1;
    480 	else if ((fin->fin_out == 0) && (nat->nat_dir == NAT_OUTBOUND))
    481 		rev = 1;
    482 	else
    483 		rev = 0;
    484 
    485 	tcp = (tcphdr_t *)fin->fin_dp;
    486 	if ((tcp->th_flags & TH_OPENING) == TH_OPENING) {
    487 		pptp = (pptp_pxy_t *)aps->aps_data;
    488 		pptp->pptp_side[1 - rev].pptps_next = ntohl(tcp->th_ack);
    489 		pptp->pptp_side[1 - rev].pptps_nexthdr = ntohl(tcp->th_ack);
    490 		pptp->pptp_side[rev].pptps_next = ntohl(tcp->th_seq) + 1;
    491 		pptp->pptp_side[rev].pptps_nexthdr = ntohl(tcp->th_seq) + 1;
    492 	}
    493 	return ipf_p_pptp_nextmessage(fin, nat, (pptp_pxy_t *)aps->aps_data,
    494 				     rev);
    495 }
    496 
    497 
    498 /*
    499  * clean up after ourselves.
    500  */
    501 void
    502 ipf_p_pptp_del(ipf_main_softc_t *softc, ap_session_t *aps)
    503 {
    504 	pptp_pxy_t *pptp;
    505 
    506 	pptp = aps->aps_data;
    507 
    508 	if (pptp != NULL) {
    509 		/*
    510 		 * Don't bother changing any of the NAT structure details,
    511 		 * *_del() is on a callback from aps_free(), from nat_delete()
    512 		 */
    513 
    514 		READ_ENTER(&softc->ipf_state);
    515 		if (pptp->pptp_state != NULL) {
    516 			ipf_state_setpending(softc, pptp->pptp_state);
    517 		}
    518 		RWLOCK_EXIT(&softc->ipf_state);
    519 
    520 		if (pptp->pptp_nat != NULL)
    521 			ipf_nat_setpending(softc, pptp->pptp_nat);
    522 		MUTEX_DESTROY(&pptp->pptp_rule.in_lock);
    523 	}
    524 }
    525