| /* | 
 |  * Copyright (C) 2002-2003 by Darren Reed | 
 |  * | 
 |  * Simple PPTP transparent proxy for in-kernel use.  For use with the NAT | 
 |  * code. | 
 |  * | 
 |  * $Id$ | 
 |  * | 
 |  */ | 
 | #define	IPF_PPTP_PROXY | 
 |  | 
 | typedef	struct pptp_hdr { | 
 | 	u_short	pptph_len; | 
 | 	u_short	pptph_type; | 
 | 	u_32_t	pptph_cookie; | 
 | } pptp_hdr_t; | 
 |  | 
 | #define	PPTP_MSGTYPE_CTL	1 | 
 | #define	PPTP_MTCTL_STARTREQ	1 | 
 | #define	PPTP_MTCTL_STARTREP	2 | 
 | #define	PPTP_MTCTL_STOPREQ	3 | 
 | #define	PPTP_MTCTL_STOPREP	4 | 
 | #define	PPTP_MTCTL_ECHOREQ	5 | 
 | #define	PPTP_MTCTL_ECHOREP	6 | 
 | #define	PPTP_MTCTL_OUTREQ	7 | 
 | #define	PPTP_MTCTL_OUTREP	8 | 
 | #define	PPTP_MTCTL_INREQ	9 | 
 | #define	PPTP_MTCTL_INREP	10 | 
 | #define	PPTP_MTCTL_INCONNECT	11 | 
 | #define	PPTP_MTCTL_CLEAR	12 | 
 | #define	PPTP_MTCTL_DISCONNECT	13 | 
 | #define	PPTP_MTCTL_WANERROR	14 | 
 | #define	PPTP_MTCTL_LINKINFO	15 | 
 |  | 
 |  | 
 | int ippr_pptp_init __P((void)); | 
 | void ippr_pptp_fini __P((void)); | 
 | int ippr_pptp_new __P((fr_info_t *, ap_session_t *, nat_t *)); | 
 | void ippr_pptp_del __P((ap_session_t *)); | 
 | int ippr_pptp_inout __P((fr_info_t *, ap_session_t *, nat_t *)); | 
 | void ippr_pptp_donatstate __P((fr_info_t *, nat_t *, pptp_pxy_t *)); | 
 | int ippr_pptp_message __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); | 
 | int ippr_pptp_nextmessage __P((fr_info_t *, nat_t *, pptp_pxy_t *, int)); | 
 | int ippr_pptp_mctl __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); | 
 |  | 
 | static	frentry_t	pptpfr; | 
 |  | 
 | int	pptp_proxy_init = 0; | 
 | int	ippr_pptp_debug = 0; | 
 | int	ippr_pptp_gretimeout = IPF_TTLVAL(120);	/* 2 minutes */ | 
 |  | 
 |  | 
 | /* | 
 |  * PPTP application proxy initialization. | 
 |  */ | 
 | int ippr_pptp_init() | 
 | { | 
 | 	bzero((char *)&pptpfr, sizeof(pptpfr)); | 
 | 	pptpfr.fr_ref = 1; | 
 | 	pptpfr.fr_age[0] = ippr_pptp_gretimeout; | 
 | 	pptpfr.fr_age[1] = ippr_pptp_gretimeout; | 
 | 	pptpfr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; | 
 | 	MUTEX_INIT(&pptpfr.fr_lock, "PPTP proxy rule lock"); | 
 | 	pptp_proxy_init = 1; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | void ippr_pptp_fini() | 
 | { | 
 | 	if (pptp_proxy_init == 1) { | 
 | 		MUTEX_DESTROY(&pptpfr.fr_lock); | 
 | 		pptp_proxy_init = 0; | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Setup for a new PPTP proxy. | 
 |  * | 
 |  * NOTE: The printf's are broken up with %s in them to prevent them being | 
 |  * optimised into puts statements on FreeBSD (this doesn't exist in the kernel) | 
 |  */ | 
 | int ippr_pptp_new(fin, aps, nat) | 
 | fr_info_t *fin; | 
 | ap_session_t *aps; | 
 | nat_t *nat; | 
 | { | 
 | 	pptp_pxy_t *pptp; | 
 | 	ipnat_t *ipn; | 
 | 	ip_t *ip; | 
 |  | 
 | 	ip = fin->fin_ip; | 
 |  | 
 | 	if (nat_outlookup(fin, 0, IPPROTO_GRE, nat->nat_inip, | 
 | 			  ip->ip_dst) != NULL) { | 
 | 		if (ippr_pptp_debug > 0) | 
 | 			printf("ippr_pptp_new: GRE session %s\n", | 
 | 			       "already exists"); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	aps->aps_psiz = sizeof(*pptp); | 
 | 	KMALLOCS(aps->aps_data, pptp_pxy_t *, sizeof(*pptp)); | 
 | 	if (aps->aps_data == NULL) { | 
 | 		if (ippr_pptp_debug > 0) | 
 | 			printf("ippr_pptp_new: malloc for aps_data %s\n", | 
 | 			       "failed"); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Create NAT rule against which the tunnel/transport mapping is | 
 | 	 * created.  This is required because the current NAT rule does not | 
 | 	 * describe GRE but TCP instead. | 
 | 	 */ | 
 | 	pptp = aps->aps_data; | 
 | 	bzero((char *)pptp, sizeof(*pptp)); | 
 | 	ipn = &pptp->pptp_rule; | 
 | 	ipn->in_ifps[0] = fin->fin_ifp; | 
 | 	ipn->in_apr = NULL; | 
 | 	ipn->in_use = 1; | 
 | 	ipn->in_hits = 1; | 
 | 	ipn->in_ippip = 1; | 
 | 	if (nat->nat_dir == NAT_OUTBOUND) { | 
 | 		ipn->in_nip = ntohl(nat->nat_outip.s_addr); | 
 | 		ipn->in_outip = fin->fin_saddr; | 
 | 		ipn->in_redir = NAT_MAP; | 
 | 	} else if (nat->nat_dir == NAT_INBOUND) { | 
 | 		ipn->in_nip = 0; | 
 | 		ipn->in_outip = nat->nat_outip.s_addr; | 
 | 		ipn->in_redir = NAT_REDIRECT; | 
 | 	} | 
 | 	ipn->in_inip = nat->nat_inip.s_addr; | 
 | 	ipn->in_inmsk = 0xffffffff; | 
 | 	ipn->in_outmsk = 0xffffffff; | 
 | 	ipn->in_srcip = fin->fin_saddr; | 
 | 	ipn->in_srcmsk = 0xffffffff; | 
 | 	bcopy(nat->nat_ptr->in_ifnames[0], ipn->in_ifnames[0], | 
 | 	      sizeof(ipn->in_ifnames[0])); | 
 | 	ipn->in_p = IPPROTO_GRE; | 
 |  | 
 | 	pptp->pptp_side[0].pptps_wptr = pptp->pptp_side[0].pptps_buffer; | 
 | 	pptp->pptp_side[1].pptps_wptr = pptp->pptp_side[1].pptps_buffer; | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | void ippr_pptp_donatstate(fin, nat, pptp) | 
 | fr_info_t *fin; | 
 | nat_t *nat; | 
 | pptp_pxy_t *pptp; | 
 | { | 
 | 	fr_info_t fi; | 
 | 	grehdr_t gre; | 
 | 	nat_t *nat2; | 
 | 	u_char p; | 
 | 	ip_t *ip; | 
 |  | 
 | 	ip = fin->fin_ip; | 
 | 	p = ip->ip_p; | 
 |  | 
 | 	nat2 = pptp->pptp_nat; | 
 | 	if ((nat2 == NULL) || (pptp->pptp_state == NULL)) { | 
 | 		bcopy((char *)fin, (char *)&fi, sizeof(fi)); | 
 | 		bzero((char *)&gre, sizeof(gre)); | 
 | 		fi.fin_state = NULL; | 
 | 		fi.fin_nat = NULL; | 
 | 		fi.fin_fi.fi_p = IPPROTO_GRE; | 
 | 		fi.fin_fr = &pptpfr; | 
 | 		if ((nat->nat_dir == NAT_OUTBOUND && fin->fin_out) || | 
 | 		    (nat->nat_dir == NAT_INBOUND && !fin->fin_out)) { | 
 | 			fi.fin_data[0] = pptp->pptp_call[0]; | 
 | 			fi.fin_data[1] = pptp->pptp_call[1]; | 
 | 		} else { | 
 | 			fi.fin_data[0] = pptp->pptp_call[1]; | 
 | 			fi.fin_data[1] = pptp->pptp_call[0]; | 
 | 		} | 
 | 		ip = fin->fin_ip; | 
 | 		ip->ip_p = IPPROTO_GRE; | 
 | 		fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); | 
 | 		fi.fin_flx |= FI_IGNORE; | 
 | 		fi.fin_dp = &gre; | 
 | 		gre.gr_flags = htons(1 << 13); | 
 | 		if (fin->fin_out && nat->nat_dir == NAT_INBOUND) { | 
 | 			fi.fin_fi.fi_saddr = fin->fin_fi.fi_daddr; | 
 | 			fi.fin_fi.fi_daddr = nat->nat_outip.s_addr; | 
 | 		} else if (!fin->fin_out && nat->nat_dir == NAT_OUTBOUND) { | 
 | 			fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; | 
 | 			fi.fin_fi.fi_daddr = fin->fin_fi.fi_saddr; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Update NAT timeout/create NAT if missing. | 
 | 	 */ | 
 | 	if (nat2 != NULL) | 
 | 		fr_queueback(&nat2->nat_tqe); | 
 | 	else { | 
 | 		nat2 = nat_new(&fi, &pptp->pptp_rule, &pptp->pptp_nat, | 
 | 			       NAT_SLAVE, nat->nat_dir); | 
 | 		pptp->pptp_nat = nat2; | 
 | 		if (nat2 != NULL) { | 
 | 			(void) nat_proto(&fi, nat2, 0); | 
 | 			nat_update(&fi, nat2, nat2->nat_ptr); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	READ_ENTER(&ipf_state); | 
 | 	if (pptp->pptp_state != NULL) { | 
 | 		fr_queueback(&pptp->pptp_state->is_sti); | 
 | 		RWLOCK_EXIT(&ipf_state); | 
 | 	} else { | 
 | 		RWLOCK_EXIT(&ipf_state); | 
 | 		if (nat2 != NULL) { | 
 | 			if (nat->nat_dir == NAT_INBOUND) | 
 | 				fi.fin_fi.fi_daddr = nat2->nat_inip.s_addr; | 
 | 			else | 
 | 				fi.fin_fi.fi_saddr = nat2->nat_inip.s_addr; | 
 | 		} | 
 | 		fi.fin_ifp = NULL; | 
 | 		pptp->pptp_state = fr_addstate(&fi, &pptp->pptp_state, | 
 | 					       0); | 
 | 		if (fi.fin_state != NULL) | 
 | 			fr_statederef((ipstate_t **)&fi.fin_state); | 
 | 	} | 
 | 	ip->ip_p = p; | 
 | 	return; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Try and build up the next PPTP message in the TCP stream and if we can | 
 |  * build it up completely (fits in our buffer) then pass it off to the message | 
 |  * parsing function. | 
 |  */ | 
 | int ippr_pptp_nextmessage(fin, nat, pptp, rev) | 
 | fr_info_t *fin; | 
 | nat_t *nat; | 
 | pptp_pxy_t *pptp; | 
 | int rev; | 
 | { | 
 | 	static const char *funcname = "ippr_pptp_nextmessage"; | 
 | 	pptp_side_t *pptps; | 
 | 	u_32_t start, end; | 
 | 	pptp_hdr_t *hdr; | 
 | 	tcphdr_t *tcp; | 
 | 	int dlen, off; | 
 | 	u_short len; | 
 | 	char *msg; | 
 |  | 
 | 	tcp = fin->fin_dp; | 
 | 	dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2); | 
 | 	start = ntohl(tcp->th_seq); | 
 | 	pptps = &pptp->pptp_side[rev]; | 
 | 	off = (char *)tcp - (char *)fin->fin_ip + (TCP_OFF(tcp) << 2) + | 
 | 	      fin->fin_ipoff; | 
 |  | 
 | 	if (dlen <= 0) | 
 | 		return 0; | 
 | 	/* | 
 | 	 * If the complete data packet is before what we expect to see | 
 | 	 * "next", just ignore it as the chances are we've already seen it. | 
 | 	 * The next if statement following this one really just causes packets | 
 | 	 * ahead of what we've seen to be dropped, implying that something in | 
 | 	 * the middle went missing and we want to see that first. | 
 | 	 */ | 
 | 	end = start + dlen; | 
 | 	if (pptps->pptps_next > end && pptps->pptps_next > start) | 
 | 		return 0; | 
 |  | 
 | 	if (pptps->pptps_next != start) { | 
 | 		if (ippr_pptp_debug > 5) | 
 | 			printf("%s: next (%x) != start (%x)\n", funcname, | 
 | 				pptps->pptps_next, start); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	msg = (char *)fin->fin_dp + (TCP_OFF(tcp) << 2); | 
 |  | 
 | 	while (dlen > 0) { | 
 | 		off += pptps->pptps_bytes; | 
 | 		if (pptps->pptps_gothdr == 0) { | 
 | 			/* | 
 | 			 * PPTP has an 8 byte header that inclues the cookie. | 
 | 			 * The start of every message should include one and | 
 | 			 * it should match 1a2b3c4d.  Byte order is ignored, | 
 | 			 * deliberately, when printing out the error. | 
 | 			 */ | 
 | 			len = MIN(8 - pptps->pptps_bytes, dlen); | 
 | 			COPYDATA(fin->fin_m, off, len, pptps->pptps_wptr); | 
 | 			pptps->pptps_bytes += len; | 
 | 			pptps->pptps_wptr += len; | 
 | 			hdr = (pptp_hdr_t *)pptps->pptps_buffer; | 
 | 			if (pptps->pptps_bytes == 8) { | 
 | 				pptps->pptps_next += 8; | 
 | 				if (ntohl(hdr->pptph_cookie) != 0x1a2b3c4d) { | 
 | 					if (ippr_pptp_debug > 1) | 
 | 						printf("%s: bad cookie (%x)\n", | 
 | 						       funcname, | 
 | 						       hdr->pptph_cookie); | 
 | 					return -1; | 
 | 				} | 
 | 			} | 
 | 			dlen -= len; | 
 | 			msg += len; | 
 | 			off += len; | 
 |  | 
 | 			pptps->pptps_gothdr = 1; | 
 | 			len = ntohs(hdr->pptph_len); | 
 | 			pptps->pptps_len = len; | 
 | 			pptps->pptps_nexthdr += len; | 
 |  | 
 | 			/* | 
 | 			 * If a message is too big for the buffer, just set | 
 | 			 * the fields for the next message to come along. | 
 | 			 * The messages defined in RFC 2637 will not exceed | 
 | 			 * 512 bytes (in total length) so this is likely a | 
 | 			 * bad data packet, anyway. | 
 | 			 */ | 
 | 			if (len > sizeof(pptps->pptps_buffer)) { | 
 | 				if (ippr_pptp_debug > 3) | 
 | 					printf("%s: message too big (%d)\n", | 
 | 					       funcname, len); | 
 | 				pptps->pptps_next = pptps->pptps_nexthdr; | 
 | 				pptps->pptps_wptr = pptps->pptps_buffer; | 
 | 				pptps->pptps_gothdr = 0; | 
 | 				pptps->pptps_bytes = 0; | 
 | 				pptps->pptps_len = 0; | 
 | 				break; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		len = MIN(pptps->pptps_len - pptps->pptps_bytes, dlen); | 
 | 		COPYDATA(fin->fin_m, off, len, pptps->pptps_wptr); | 
 | 		pptps->pptps_bytes += len; | 
 | 		pptps->pptps_wptr += len; | 
 | 		pptps->pptps_next += len; | 
 |  | 
 | 		if (pptps->pptps_len > pptps->pptps_bytes) | 
 | 			break; | 
 |  | 
 | 		ippr_pptp_message(fin, nat, pptp, pptps); | 
 | 		pptps->pptps_wptr = pptps->pptps_buffer; | 
 | 		pptps->pptps_gothdr = 0; | 
 | 		pptps->pptps_bytes = 0; | 
 | 		pptps->pptps_len = 0; | 
 |  | 
 | 		start += len; | 
 | 		msg += len; | 
 | 		dlen -= len; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * handle a complete PPTP message | 
 |  */ | 
 | int ippr_pptp_message(fin, nat, pptp, pptps) | 
 | fr_info_t *fin; | 
 | nat_t *nat; | 
 | pptp_pxy_t *pptp; | 
 | pptp_side_t *pptps; | 
 | { | 
 | 	pptp_hdr_t *hdr = (pptp_hdr_t *)pptps->pptps_buffer; | 
 |  | 
 | 	switch (ntohs(hdr->pptph_type)) | 
 | 	{ | 
 | 	case PPTP_MSGTYPE_CTL : | 
 | 		ippr_pptp_mctl(fin, nat, pptp, pptps); | 
 | 		break; | 
 |  | 
 | 	default : | 
 | 		break; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * handle a complete PPTP control message | 
 |  */ | 
 | int ippr_pptp_mctl(fin, nat, pptp, pptps) | 
 | fr_info_t *fin; | 
 | nat_t *nat; | 
 | pptp_pxy_t *pptp; | 
 | pptp_side_t *pptps; | 
 | { | 
 | 	u_short *buffer = (u_short *)(pptps->pptps_buffer); | 
 | 	pptp_side_t *pptpo; | 
 |  | 
 | 	if (pptps == &pptp->pptp_side[0]) | 
 | 		pptpo = &pptp->pptp_side[1]; | 
 | 	else | 
 | 		pptpo = &pptp->pptp_side[0]; | 
 |  | 
 | 	/* | 
 | 	 * Breakout to handle all the various messages.  Most are just state | 
 | 	 * transition. | 
 | 	 */ | 
 | 	switch (ntohs(buffer[4])) | 
 | 	{ | 
 | 	case PPTP_MTCTL_STARTREQ : | 
 | 		pptps->pptps_state = PPTP_MTCTL_STARTREQ; | 
 | 		break; | 
 | 	case PPTP_MTCTL_STARTREP : | 
 | 		if (pptpo->pptps_state == PPTP_MTCTL_STARTREQ) | 
 | 			pptps->pptps_state = PPTP_MTCTL_STARTREP; | 
 | 		break; | 
 | 	case PPTP_MTCTL_STOPREQ : | 
 | 		pptps->pptps_state = PPTP_MTCTL_STOPREQ; | 
 | 		break; | 
 | 	case PPTP_MTCTL_STOPREP : | 
 | 		if (pptpo->pptps_state == PPTP_MTCTL_STOPREQ) | 
 | 			pptps->pptps_state = PPTP_MTCTL_STOPREP; | 
 | 		break; | 
 | 	case PPTP_MTCTL_ECHOREQ : | 
 | 		pptps->pptps_state = PPTP_MTCTL_ECHOREQ; | 
 | 		break; | 
 | 	case PPTP_MTCTL_ECHOREP : | 
 | 		if (pptpo->pptps_state == PPTP_MTCTL_ECHOREQ) | 
 | 			pptps->pptps_state = PPTP_MTCTL_ECHOREP; | 
 | 		break; | 
 | 	case PPTP_MTCTL_OUTREQ : | 
 | 		pptps->pptps_state = PPTP_MTCTL_OUTREQ; | 
 | 		break; | 
 | 	case PPTP_MTCTL_OUTREP : | 
 | 		if (pptpo->pptps_state == PPTP_MTCTL_OUTREQ) { | 
 | 			pptps->pptps_state = PPTP_MTCTL_OUTREP; | 
 | 			pptp->pptp_call[0] = buffer[7]; | 
 | 			pptp->pptp_call[1] = buffer[6]; | 
 | 			ippr_pptp_donatstate(fin, nat, pptp); | 
 | 		} | 
 | 		break; | 
 | 	case PPTP_MTCTL_INREQ : | 
 | 		pptps->pptps_state = PPTP_MTCTL_INREQ; | 
 | 		break; | 
 | 	case PPTP_MTCTL_INREP : | 
 | 		if (pptpo->pptps_state == PPTP_MTCTL_INREQ) { | 
 | 			pptps->pptps_state = PPTP_MTCTL_INREP; | 
 | 			pptp->pptp_call[0] = buffer[7]; | 
 | 			pptp->pptp_call[1] = buffer[6]; | 
 | 			ippr_pptp_donatstate(fin, nat, pptp); | 
 | 		} | 
 | 		break; | 
 | 	case PPTP_MTCTL_INCONNECT : | 
 | 		pptps->pptps_state = PPTP_MTCTL_INCONNECT; | 
 | 		break; | 
 | 	case PPTP_MTCTL_CLEAR : | 
 | 		pptps->pptps_state = PPTP_MTCTL_CLEAR; | 
 | 		break; | 
 | 	case PPTP_MTCTL_DISCONNECT : | 
 | 		pptps->pptps_state = PPTP_MTCTL_DISCONNECT; | 
 | 		break; | 
 | 	case PPTP_MTCTL_WANERROR : | 
 | 		pptps->pptps_state = PPTP_MTCTL_WANERROR; | 
 | 		break; | 
 | 	case PPTP_MTCTL_LINKINFO : | 
 | 		pptps->pptps_state = PPTP_MTCTL_LINKINFO; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * For outgoing PPTP packets.  refresh timeouts for NAT & state entries, if | 
 |  * we can.  If they have disappeared, recreate them. | 
 |  */ | 
 | int ippr_pptp_inout(fin, aps, nat) | 
 | fr_info_t *fin; | 
 | ap_session_t *aps; | 
 | nat_t *nat; | 
 | { | 
 | 	pptp_pxy_t *pptp; | 
 | 	tcphdr_t *tcp; | 
 | 	int rev; | 
 |  | 
 | 	if ((fin->fin_out == 1) && (nat->nat_dir == NAT_INBOUND)) | 
 | 		rev = 1; | 
 | 	else if ((fin->fin_out == 0) && (nat->nat_dir == NAT_OUTBOUND)) | 
 | 		rev = 1; | 
 | 	else | 
 | 		rev = 0; | 
 |  | 
 | 	tcp = (tcphdr_t *)fin->fin_dp; | 
 | 	if ((tcp->th_flags & TH_OPENING) == TH_OPENING) { | 
 | 		pptp = (pptp_pxy_t *)aps->aps_data; | 
 | 		pptp->pptp_side[1 - rev].pptps_next = ntohl(tcp->th_ack); | 
 | 		pptp->pptp_side[1 - rev].pptps_nexthdr = ntohl(tcp->th_ack); | 
 | 		pptp->pptp_side[rev].pptps_next = ntohl(tcp->th_seq) + 1; | 
 | 		pptp->pptp_side[rev].pptps_nexthdr = ntohl(tcp->th_seq) + 1; | 
 | 	} | 
 | 	return ippr_pptp_nextmessage(fin, nat, (pptp_pxy_t *)aps->aps_data, | 
 | 				     rev); | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * clean up after ourselves. | 
 |  */ | 
 | void ippr_pptp_del(aps) | 
 | ap_session_t *aps; | 
 | { | 
 | 	pptp_pxy_t *pptp; | 
 |  | 
 | 	pptp = aps->aps_data; | 
 |  | 
 | 	if (pptp != NULL) { | 
 | 		/* | 
 | 		 * Don't bother changing any of the NAT structure details, | 
 | 		 * *_del() is on a callback from aps_free(), from nat_delete() | 
 | 		 */ | 
 |  | 
 | 		READ_ENTER(&ipf_state); | 
 | 		if (pptp->pptp_state != NULL) { | 
 | 			pptp->pptp_state->is_die = fr_ticks + 1; | 
 | 			pptp->pptp_state->is_me = NULL; | 
 | 			fr_queuefront(&pptp->pptp_state->is_sti); | 
 | 		} | 
 | 		RWLOCK_EXIT(&ipf_state); | 
 |  | 
 | 		pptp->pptp_state = NULL; | 
 | 		pptp->pptp_nat = NULL; | 
 | 	} | 
 | } |