Beispiel #1
0
/* resend una packets */
static int
l2tp_ctrl_resend_una_packets(l2tp_ctrl *_this)
{
	uint16_t seq;
	bytebuffer *bytebuf;
	struct l2tp_header *header;
	int nsend;

	nsend = 0;
	for (seq = _this->snd_una; SEQ_LT(seq, _this->snd_nxt); seq++) {
		bytebuf = _this->snd_buffers[seq % _this->winsz];
		header = bytebuffer_pointer(bytebuf);
		header->nr = htons(_this->rcv_nxt);
#ifdef L2TP_CTRL_DEBUG
		if (debuglevel >= 3) {
			l2tp_ctrl_log(_this, DEBUG_LEVEL_3, "RESEND seq=%u",
			    ntohs(header->ns));
			show_hd(debug_get_debugfp(),
			    bytebuffer_pointer(bytebuf),
			    bytebuffer_remaining(bytebuf));
		}
#endif
		if (l2tp_ctrl_send(_this, bytebuffer_pointer(bytebuf),
		    bytebuffer_remaining(bytebuf)) < 0) {
			l2tp_ctrl_log(_this, LOG_ERR,
			    "sendto() failed in %s: %m", __func__);
			return -1;
		}
		nsend++;
	}
	return nsend;
}
Beispiel #2
0
/* send control packet */
int
l2tp_ctrl_send_packet(l2tp_ctrl *_this, int call_id, bytebuffer *bytebuf)
{
	struct l2tp_header *hdr;
	int rval;
	time_t curr_time;

	curr_time = get_monosec();

	bytebuffer_flip(bytebuf);
	hdr = (struct l2tp_header *)bytebuffer_pointer(bytebuf);
	memset(hdr, 0, sizeof(*hdr));

	hdr->t = 1;
	hdr->ver = L2TP_HEADER_VERSION_RFC2661;
	hdr->l = 1;
	hdr->length = htons(bytebuffer_remaining(bytebuf));
	hdr->tunnel_id = htons(_this->peer_tunnel_id);
	hdr->session_id = htons(call_id);

	hdr->s = 1;
	hdr->ns = htons(_this->snd_nxt);
	hdr->nr = htons(_this->rcv_nxt);

	if (bytebuffer_remaining(bytebuf) > sizeof(struct l2tp_header))
		/* Not ZLB */
		_this->snd_nxt++;

	L2TP_CTRL_DBG((_this, DEBUG_LEVEL_2,
	    "SEND C ns=%u nr=%u snd_nxt=%u snd_una=%u rcv_nxt=%u ",
	    ntohs(hdr->ns), htons(hdr->nr),
	    _this->snd_nxt, _this->snd_una, _this->rcv_nxt));

	if (L2TP_CTRL_CONF(_this)->ctrl_out_pktdump  != 0) {
		l2tpd_log(_this->l2tpd, LOG_DEBUG,
		    "L2TP Control output packet dump");
		show_hd(debug_get_debugfp(), bytebuffer_pointer(bytebuf),
		    bytebuffer_remaining(bytebuf));
	}

	if ((rval = l2tp_ctrl_send(_this, bytebuffer_pointer(bytebuf),
	    bytebuffer_remaining(bytebuf))) < 0) {
		L2TP_CTRL_DBG((_this, LOG_DEBUG, "sendto() failed: %m"));
	}

	_this->last_snd_ctrl = curr_time;

	return (rval == bytebuffer_remaining(bytebuf))? 0 : 1;
}
Beispiel #3
0
/* send L2TP data message */
static int
l2tp_call_send_data_packet(l2tp_call *_this, bytebuffer *buffer)
{
	int rval;
	struct l2tp_header *hdr;

	bytebuffer_flip(buffer);
	hdr = (struct l2tp_header *)bytebuffer_pointer(buffer);
	memset(hdr, 0, sizeof(*hdr) - 4);	/* Nr, NS are option */

	hdr->t = 0;
	hdr->ver = L2TP_HEADER_VERSION_RFC2661;
	hdr->l = 1;
	hdr->length = htons(bytebuffer_remaining(buffer));
	hdr->tunnel_id = htons(_this->ctrl->peer_tunnel_id);
	hdr->session_id = htons(_this->peer_session_id);
	if (_this->use_seq) {
		hdr->s = 1;
		hdr->ns = htons(_this->snd_nxt++);
		hdr->nr = htons(_this->rcv_nxt);
	}

	if (L2TP_CTRL_CONF(_this->ctrl)->data_out_pktdump != 0) {
		l2tpd_log(_this->ctrl->l2tpd, LOG_DEBUG,
		    "ctrl=%u call=%u L2TP Data output packet dump",
		    _this->ctrl->id, _this->id);
		show_hd(debug_get_debugfp(), bytebuffer_pointer(buffer),
		    bytebuffer_remaining(buffer));
	}
	if ((rval = l2tp_ctrl_send(_this->ctrl, bytebuffer_pointer(buffer),
	    bytebuffer_remaining(buffer))) < 0) {
		L2TP_CALL_DBG((_this, LOG_DEBUG, "sendto() failed: %m"));
	}

	return (rval == bytebuffer_remaining(buffer))? 0 : 1;
}
Beispiel #4
0
/**
 * receiving ConfRej.
 */
static int
lcp_rejci(fsm *f, u_char *inp, int inlen)
{
	int chapalg, authproto, type, len, mru;
	u_char *inp0;
	lcp *_this;

#define	remlen()	(inlen - (inp - inp0))
#define	LCP_OPT_REJECTED(opt)				\
	if (!psm_opt_is_requested(&f->ppp->lcp, opt))	\
		goto fail;				\
	psm_opt_set_rejected(&f->ppp->lcp, opt, 1);

	f->ppp->lcp.recv_ress++;
	inp0 = inp;
	_this = &f->ppp->lcp;
	while (remlen() >= 2) {
		GETCHAR(type, inp);
		GETCHAR(len, inp);

		if (len <= 0 || remlen() + 2 < len)
			goto fail;

		switch (type) {
		case PPP_LCP_MAGICNUMBER:
			if (f->ppp->lcp.echo_interval > 0)
				goto fail;
			inp += 4;
			break;
		case PPP_LCP_MRU:
			LCP_OPT_REJECTED(mru);
			GETSHORT(mru, inp);
			break;
		case PPP_LCP_AUTH_PROTOCOL:
			if (len < 4)
				goto fail;
			GETSHORT(authproto, inp);
			switch (authproto) {
			case PPP_AUTH_PAP:
                                if (len != 4)
                                        goto fail;
				LCP_OPT_REJECTED(pap);
				break;
			case PPP_AUTH_CHAP:
				chapalg = 0;
				if (len == 5)
					GETCHAR(chapalg, inp);
				switch (chapalg) {
				case PPP_AUTH_CHAP_MD5:
					LCP_OPT_REJECTED(chap);
					break;
				case PPP_AUTH_CHAP_MS:
					LCP_OPT_REJECTED(chapms);
					break;
				case PPP_AUTH_CHAP_MS_V2:
					LCP_OPT_REJECTED(chapms_v2);
					break;
				default:
					fsm_log(f, LOG_INFO,
					    "Rejected chap algorithm is "
					    "unknown(%d).", chapalg);
					break;
				}
				break;
                         case PPP_AUTH_EAP:
                                if (len != 4)
                                        goto fail;
                                LCP_OPT_REJECTED(eap);
                                break;
			}
			if (NO_AUTH_AGREEABLE(_this)) {
				fsm_log(f, LOG_INFO, "No authentication "
				    "protocols are agreeable.");
				ppp_set_disconnect_cause(f->ppp,
				    PPP_DISCON_AUTH_PROTOCOL_UNACCEPTABLE,
				    authproto, 1 /* rejected by peer */, NULL);
				ppp_stop(f->ppp, "Authentication is required");
				return 1;
			}
			break;
		case PPP_LCP_PFC:
			if (len != 2)
				goto fail;
			LCP_OPT_REJECTED(pfc);
			break;
		case PPP_LCP_ACFC:
			if (len != 2)
				goto fail;
			LCP_OPT_REJECTED(acfc);
			break;
		default:
			goto fail;
		}
	}
	return 1;
fail:
	log_printf(LOG_ERR, "Received unexpected ConfRej.");
	if (debug_get_debugfp() != NULL)
		show_hd(debug_get_debugfp(), inp, inlen);
	return 0;
#undef remlen
}
Beispiel #5
0
/** receiving ConfNak. */
static int
lcp_nakci(fsm *f, u_char *inp, int inlen)
{
	int chapalg, authproto, type, len, mru;
	u_char *inp0;
	lcp *_this;
	const char *peer_auth = "unknown";

#define	remlen()	(inlen - (inp - inp0))
#define	LCP_OPT_REJECTED(opt)				\
	if (!psm_opt_is_requested(&f->ppp->lcp, opt))	\
		goto fail;				\
	psm_opt_set_rejected(&f->ppp->lcp, opt, 1);

	f->ppp->lcp.recv_ress++;
	inp0 = inp;
	_this = &f->ppp->lcp;
	while (remlen() >= 2) {
		GETCHAR(type, inp);
		GETCHAR(len, inp);

		if (len <= 0 || remlen() + 2 < len)
			goto fail;

		switch (type) {
		case PPP_LCP_MRU:
			if (len < 4)
				goto fail;
			GETSHORT(mru, inp);
			fsm_log(f, LOG_NOTICE,
			    "ignored ConfNak from the peer: mru=%d", mru);
			_this->xxxmru = mru;
			break;
		case PPP_LCP_AUTH_PROTOCOL:
			if (len < 4)
				goto fail;
			switch (_this->lastauth) {
			case PPP_AUTH_PAP:
				psm_opt_set_rejected(_this, pap, 1);
				break;
			case PPP_AUTH_CHAP_MD5:
				psm_opt_set_rejected(_this, chap, 1);
				break;
			case PPP_AUTH_CHAP_MS:
				psm_opt_set_rejected(_this, chapms, 1);
				break;
			case PPP_AUTH_CHAP_MS_V2:
				psm_opt_set_rejected(_this, chapms_v2, 1);
				break;
                        case PPP_AUTH_EAP:
                                psm_opt_set_rejected(_this, eap, 1);
                                break;
			}
			GETSHORT(authproto, inp);
			switch (authproto) {
			case PPP_AUTH_PAP:
				if (psm_opt_is_requested(_this, pap))
					psm_opt_set_accepted(_this, pap, 1);
				peer_auth = "pap";
				break;
			case PPP_AUTH_CHAP:
				chapalg = 0;
				if (len == 5)
					GETCHAR(chapalg, inp);
				switch (chapalg) {
				case PPP_AUTH_CHAP_MD5:
					if (psm_opt_is_requested(_this, chap))
						psm_opt_set_accepted(_this,
						    chap, 1);
					peer_auth = "chap";
					break;
				case PPP_AUTH_CHAP_MS:
					if (psm_opt_is_requested(_this, chapms))
						psm_opt_set_accepted(_this,
						    chapms, 1);
					peer_auth = "mschap";
					break;
				case PPP_AUTH_CHAP_MS_V2:
					if (psm_opt_is_requested(_this,
					    chapms_v2))
						psm_opt_set_accepted(_this,
						    chapms_v2, 1);
					peer_auth = "mschap_v2";
					break;
				default:
					fsm_log(f, LOG_INFO,
					    "Nacked chap algorithm is "
					    "unknown(%d).", chapalg);
					peer_auth = "unknown";
					break;
				}
				break;
                        case PPP_AUTH_EAP:
                                if (len != 4)
                                        goto fail;
                                peer_auth = "eap";
				if (psm_opt_is_requested(_this, eap))
					psm_opt_set_accepted(_this, eap, 1);
                                break;
			}
			if (NO_AUTH_AGREEABLE(_this)) {
				fsm_log(f, LOG_INFO, "No authentication "
				    "protocols are agreeable.  peer's "
				    "auth proto=%s",
				    peer_auth);
				ppp_set_disconnect_cause(f->ppp,
				    PPP_DISCON_AUTH_PROTOCOL_UNACCEPTABLE, 
				    authproto, 2 /* couldn't accept peer's */,
				    NULL);
				ppp_stop(f->ppp, "Authentication is required");
				return 1;
			}
			break;
		case PPP_LCP_PFC:
			if (len != 2)
				goto fail;
			LCP_OPT_REJECTED(pfc);
			break;
		case PPP_LCP_ACFC:
			if (len != 2)
				goto fail;
			LCP_OPT_REJECTED(acfc);
			break;
		default:
			goto fail;
		}
	}
	return 1;
fail:
	log_printf(LOG_ERR, "Received unexpected ConfNak.");
	if (debug_get_debugfp() != NULL)
		show_hd(debug_get_debugfp(), inp, inlen);
	return 0;
#undef remlen
#undef LCP_OPT_REJECTED
}
Beispiel #6
0
/** receiving ConfAck. */
static int
lcp_ackci(fsm *f, u_char *inp, int inlen)
{
	int chapalg, authproto, type, len, mru, magic;
	u_char *inp0;

#define	remlen()	(inlen - (inp - inp0))
#define	LCP_OPT_ACCEPTED(opt)				\
	if (!psm_opt_is_requested(&f->ppp->lcp, opt))	\
		goto fail;				\
	psm_opt_set_accepted(&f->ppp->lcp, opt, 1);

	f->ppp->lcp.recv_ress++;
	inp0 = inp;
	while (remlen() >= 2) {
		GETCHAR(type, inp);
		GETCHAR(len, inp);

		if (len <= 0 || remlen() + 2 < len)
			goto fail;

		switch (type) {
		case PPP_LCP_MAGICNUMBER:
			if (len != 6)
				goto fail;
			GETLONG(magic, inp);
			if (f->ppp->lcp.magic_number != magic)
				goto fail;
			break;
		case PPP_LCP_MRU:
			if (len != 4)
				goto fail;
			LCP_OPT_ACCEPTED(mru);
			GETSHORT(mru, inp);
			break;
		case PPP_LCP_AUTH_PROTOCOL:
			if (len < 4)
				goto fail;
			GETSHORT(authproto, inp);
			switch (authproto) {
			case PPP_AUTH_PAP:
				if (len != 4)
					goto fail;
				LCP_OPT_ACCEPTED(pap);
				break;
			case PPP_AUTH_CHAP:
				if (len != 5)
					goto fail;
				GETCHAR(chapalg, inp);
				switch (chapalg) {
				case PPP_AUTH_CHAP_MD5:
					LCP_OPT_ACCEPTED(chap);
					break;
				case PPP_AUTH_CHAP_MS:
					LCP_OPT_ACCEPTED(chapms);
					break;
				case PPP_AUTH_CHAP_MS_V2:
					LCP_OPT_ACCEPTED(chapms_v2);
					break;
				}
				break;
                        case PPP_AUTH_EAP:
                                if (len != 4)
                                     goto fail;
                                LCP_OPT_ACCEPTED(eap);
                                break;
			}
			break;

		/*
		 * As RFC1661, ConfRej must be used for boolean options, but
		 * at least RouterTester uses ConfNak for them.
		 */
		case PPP_LCP_PFC:
			if (len != 2)
				goto fail;
			LCP_OPT_ACCEPTED(pfc);
			break;
		case PPP_LCP_ACFC:
			if (len != 2)
				goto fail;
			LCP_OPT_ACCEPTED(acfc);
			break;

		default:
			goto fail;
		}
	}
	return 1;
fail:
	fsm_log(f, LOG_ERR, "Received unexpected ConfAck.");
	if (debug_get_debugfp() != NULL)
		show_hd(debug_get_debugfp(), inp, remlen());
	return 0;
#undef	LCP_OPT_ACCEPTED
}
Beispiel #7
0
/* receive GRE then route to pptp_call */
static void
pptpd_gre_input(pptpd_listener *listener, struct sockaddr *peer, u_char *pkt,
    int lpkt)
{
	int hlen, input_flags;
	uint32_t seq, ack, call_id;
	struct ip *iphdr;
	struct pptp_gre_header *grehdr;
	char hbuf0[NI_MAXHOST], logbuf[512];
	const char *reason;
	pptp_call *call;
	hash_link *hl;
	pptpd *_this;

	seq = 0;
	ack = 0;
	input_flags = 0;
	reason = "No error";
	_this = listener->self;

	PPTPD_ASSERT(peer->sa_family == AF_INET);

	strlcpy(hbuf0, "<unknown>", sizeof(hbuf0));
	if (getnameinfo(peer, peer->sa_len, hbuf0, sizeof(hbuf0), NULL, 0,
	    NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
		pptpd_log(_this, LOG_ERR,
		    "getnameinfo() failed at %s(): %m", __func__);
		goto fail;
	}
	if (_this->data_in_pktdump != 0) {
		pptpd_log(_this, LOG_DEBUG, "PPTP Data input packet dump");
		show_hd(debug_get_debugfp(), pkt, lpkt);
	}
	if (peer->sa_family != AF_INET) {
		pptpd_log(_this, LOG_ERR,
		    "Received malformed GRE packet: address family is not "
		    "supported: peer=%s af=%d", hbuf0, peer->sa_family);
		goto fail;
	}

	if (lpkt < sizeof(struct ip)) {
		pptpd_log(_this, LOG_ERR,
		    "Received a short length packet length=%d, from %s", lpkt,
			hbuf0);
		goto fail;
	}
	iphdr = (struct ip *)pkt;

	iphdr->ip_len = ntohs(iphdr->ip_len);
	hlen = iphdr->ip_hl * 4;

	if (iphdr->ip_len > lpkt ||
	    iphdr->ip_len < sizeof(struct pptp_gre_header)) {
		pptpd_log(_this, LOG_ERR,
		    "Received a broken packet: ip_hl=%d iplen=%d lpkt=%d", hlen,
			iphdr->ip_len, lpkt);
		show_hd(debug_get_debugfp(), pkt, lpkt);
		goto fail;
	}
	pkt += hlen;
	lpkt -= hlen;
	grehdr = (struct pptp_gre_header *)pkt;
	pkt += sizeof(struct pptp_gre_header);
	lpkt -= sizeof(struct pptp_gre_header);

	grehdr->protocol_type = htons(grehdr->protocol_type);
	grehdr->payload_length = htons(grehdr->payload_length);
	grehdr->call_id = htons(grehdr->call_id);

	if (!(grehdr->protocol_type == PPTP_GRE_PROTOCOL_TYPE &&
	    grehdr->C == 0 && grehdr->R == 0 && grehdr->K != 0 &&
	    grehdr->recur == 0 && grehdr->s == 0 && grehdr->flags == 0 &&
	    grehdr->ver == PPTP_GRE_VERSION)) {
		reason = "GRE header is broken";
		goto bad_gre;
	}
	if (grehdr->S != 0) {
		if (lpkt < 2) {
			reason = "No enough space for seq number";
			goto bad_gre;
		}
		input_flags |= PPTP_GRE_PKT_SEQ_PRESENT;
		seq = ntohl(*(uint32_t *)pkt);
		pkt += 4;
		lpkt -= 4;
	}

	if (grehdr->A != 0) {
		if (lpkt < 2) {
			reason = "No enough space for ack number";
			goto bad_gre;
		}
		input_flags |= PPTP_GRE_PKT_ACK_PRESENT;
		ack = ntohl(*(uint32_t *)pkt);
		pkt += 4;
		lpkt -= 4;
	}

	if (grehdr->payload_length > lpkt) {
		reason = "'Payload Length' is mismatch from actual length";
		goto bad_gre;
	}


	/* route to pptp_call */
	call_id = grehdr->call_id;

	hl = hash_lookup(_this->call_id_map,
	    (void *)(call_id | (listener->index << 16)));
	if (hl == NULL) {
		reason = "Received GRE packet has unknown call_id";
		goto bad_gre;
	}
	call = hl->item;
	pptp_call_gre_input(call, seq, ack, input_flags, pkt, lpkt);

	return;
bad_gre:
	pptp_gre_header_string(grehdr, logbuf, sizeof(logbuf));
	pptpd_log(_this, LOG_INFO,
	    "Received malformed GRE packet: %s: peer=%s sock=%s %s seq=%u: "
	    "ack=%u ifidx=%d", reason, hbuf0, inet_ntoa(iphdr->ip_dst), logbuf,
	    seq, ack, listener->index);
fail:
	return;
}
Beispiel #8
0
/* flags: fseq: contain SEQ field, fack: contain ACK field */
static int
pptp_call_gre_output(pptp_call *_this, int fseq, int fack, u_char *pkt,
    int lpkt)
{
	int sz;
	struct pptp_gre_header *grehdr;
	u_char buf[65535], *opkt;
	struct sockaddr_storage peer, sock;
#ifndef USE_LIBSOCKUTIL
	socklen_t peerlen;
#endif

	memset(buf, 0, sizeof(buf));

	opkt = buf;
	grehdr = (struct pptp_gre_header *)opkt;
	opkt += sizeof(struct pptp_gre_header);

	/* GRE header */
	grehdr->K = 1;
	grehdr->ver = PPTP_GRE_VERSION;
	grehdr->protocol_type = htons(PPTP_GRE_PROTOCOL_TYPE);
	grehdr->payload_length = htons(lpkt);
	grehdr->call_id = htons(_this->peers_call_id);

#ifdef	PPTP_CALL_DEBUG
	if (debuglevel >= 2 && (fseq || fack)) {
		pptp_call_log(_this, LOG_DEBUG,
		    "Sending data packet seq=%u ack=%u",
		    _this->snd_nxt, _this->rcv_nxt - 1);
	}
#endif
	PPTP_CALL_ASSERT(ALIGNED_POINTER(opkt, uint32_t));
	if (fseq) {
		grehdr->S = 1;
		*(uint32_t *)opkt = htonl(_this->snd_nxt++);
		opkt += 4;
	}
	if (fack) {
		grehdr->A = 1;
		_this->rcv_acked = _this->rcv_nxt;
		*(uint32_t *)opkt = htonl(_this->rcv_nxt - 1);
		opkt += 4;
	}
	if (lpkt > 0) {
		memcpy(opkt, pkt, lpkt);
		opkt += lpkt;
	}
	memcpy(&peer, &_this->ctrl->peer, sizeof(peer));
	memcpy(&sock, &_this->ctrl->our, sizeof(sock));
	switch (peer.ss_family) {
	case AF_INET:
		((struct sockaddr_in *)&peer)->sin_port = 0;
		((struct sockaddr_in *)&sock)->sin_port = 0;
#ifndef USE_LIBSOCKUTIL
		peerlen = sizeof(struct sockaddr_in);
#endif
		break;
	default:
		return 1;
	}

	if (PPTP_CTRL_CONF(_this->ctrl)->data_out_pktdump != 0) {
		pptp_call_log(_this, LOG_DEBUG, "PPTP Data output packet dump");
		show_hd(debug_get_debugfp(), buf, opkt - buf);
	}
#ifdef USE_LIBSOCKUTIL
	sz = sendfromto(pptp_ctrl_sock_gre(_this->ctrl), buf, opkt - buf,
	    0, (struct sockaddr *)&sock, (struct sockaddr *)&peer);
#else
	sz = sendto(pptp_ctrl_sock_gre(_this->ctrl), buf, opkt - buf, 0,
	    (struct sockaddr *)&peer, peerlen);
#endif

	if (sz <= 0)
		pptp_call_log(_this, LOG_WARNING, "sendto(%d) failed: %m",
		    pptp_ctrl_sock_gre(_this->ctrl));

	return (sz > 0)? 0 : 1;
}
Beispiel #9
0
/**
 * Receive the PPP packet.
 * @param	flags	Indicate information of received packet by bit flags.
 *			{@link ::PPP_IO_FLAGS_MPPE_ENCRYPTED} and
 *			{@link ::PPP_IO_FLAGS_DELAYED} may be used.
 * @return	return 0 on success.  return 1 on failure.
 */
static int
ppp_recv_packet(npppd_ppp *_this, unsigned char *pkt, int lpkt, int flags)
{
	u_char *inp, *inp_proto;
	uint16_t proto;

	PPP_ASSERT(_this != NULL);

	inp = pkt;

	if (lpkt < 4) {
		ppp_log(_this, LOG_DEBUG, "%s(): Rcvd short header.", __func__);
		return 0;
	}


	if (_this->has_acf == 0) {
		/* nothing to do */
	} else if (inp[0] == PPP_ALLSTATIONS && inp[1] == PPP_UI) {
		inp += 2;
	} else {
		/*
		 * Address and Control Field Compression
		 */
		if (!psm_opt_is_accepted(&_this->lcp, acfc) &&
		    _this->logged_no_address == 0) {
			/*
			 * On packet loss condition, we may receive ACFC'ed
			 * packets before our LCP is opened because the peer's
			 * LCP is opened already.
			 */
			ppp_log(_this, LOG_INFO,
			    "%s: Rcvd broken frame.  ACFC is not accepted, "
			    "but received ppp frame that has no address.",
			    __func__);
			/*
			 * Log this once because it may be noisy.
			 * For example, Yahama RTX-1000 refuses to use ACFC
			 * but it send PPP frames without the address field.
			 */
			_this->logged_no_address = 1;
		}
	}
	inp_proto = inp;
	if ((inp[0] & 0x01) != 0) {
		/*
		 * Protocol Field Compression
		 */
		if (!psm_opt_is_accepted(&_this->lcp, pfc)) {
			ppp_log(_this, LOG_INFO,
			    "%s: Rcvd broken frame.  No protocol field: "
			    "%02x %02x", __func__, inp[0], inp[1]);
			return 1;
		}
		GETCHAR(proto, inp);
	} else {
		GETSHORT(proto, inp);
	}

	/*
	 * if the PPP frame is reordered, drop it
	 * unless proto is reorder-tolerant
	 */
	if (flags & PPP_IO_FLAGS_DELAYED && proto != PPP_PROTO_IP)
		return 1;

	if (_this->log_dump_in != 0 && debug_get_debugfp() != NULL) {
		char buf[256];

		snprintf(buf, sizeof(buf), "log.%s.in.pktdump",
		    proto_name(proto));
		if (ppp_config_str_equal(_this, buf, "true", 0) != 0)  {
			ppp_log(_this, LOG_DEBUG,
			    "PPP input dump proto=%s(%d/%04x)",
			    proto_name(proto), proto, proto);
			show_hd(debug_get_debugfp(), pkt, lpkt);
		}
	}
#ifdef USE_NPPPD_PIPEX
	if (_this->pipex_enabled != 0 &&
	    _this->tunnel_type == PPP_TUNNEL_PPPOE) {
		switch (proto) {
		case PPP_PROTO_IP:
			return 2;		/* handled by PIPEX */
		case PPP_PROTO_NCP | NCP_CCP:
			if (lpkt - (inp - pkt) < 4)
				break;		/* error but do it on fsm.c */
			if (*inp == 0x0e ||	/* Reset-Request */
			    *inp == 0x0f	/* Reset-Ack */) {
				return 2;	/* handled by PIPEX */
			}
			/* FALLTHROUGH */
		default:
			break;
		}
	}
#endif /* USE_NPPPD_PIPEX */

	switch (proto) {
#ifdef	USE_NPPPD_MPPE
	case PPP_PROTO_IP:
		/* Checks for MPPE */
		if ((flags & PPP_IO_FLAGS_MPPE_ENCRYPTED) == 0) {
			if (MPPE_REQUIRED(_this)) {
				/* MPPE is required but naked ip */

				if (_this->logged_naked_ip == 0) {
					ppp_log(_this, LOG_INFO,
					    "mppe is required but received "
					    "naked IP.");
					/* log this once */
					_this->logged_naked_ip = 1;
				}
				/*
				 * Windows sends naked IP packets in condition
				 * such that MPPE is not opened and IPCP is not
				 * opened(*1).  This occurs at a high
				 * probability when the CCP establishment is
				 * delayed because of packet loss etc.  If we
				 * call ppp_stop() here, Windows on the packet
				 * loss condition etc cannot not connect us.
				 * So we don't call ppp_stop() here.
				 * (*1) At least Microsof Windows 2000
				 * Professional SP4 does.
				 */
				 /*ppp_stop(_this, "Encryption is required.");*/

				return 1;
			}
			if (MPPE_READY(_this)) {
				/* MPPE is opened but naked ip packet */
				ppp_log(_this, LOG_WARNING,
				    "mppe is available but received naked IP.");
			}
		}
		/* else input from MPPE */
		break;
	case PPP_PROTO_MPPE:
#ifdef USE_NPPPD_MPPE
		if (_this->mppe_started == 0)  {
#else
		{
#endif
			ppp_log(_this, LOG_ERR,
			    "mppe packet is received but mppe is stopped.");
			return 1;
		}
		break;
#endif
	}

	switch (proto) {
	case PPP_PROTO_IP:
		npppd_network_output(_this->pppd, _this, AF_INET, inp,
		    lpkt - (inp - pkt));
		goto handled;
	case PPP_PROTO_LCP:
		fsm_input(&_this->lcp.fsm, inp, lpkt - (inp - pkt));
		goto handled;
	case PPP_PROTO_PAP:
		pap_input(&_this->pap, inp, lpkt - (inp - pkt));
		goto handled;
	case PPP_PROTO_CHAP:
		chap_input(&_this->chap, inp, lpkt - (inp - pkt));
		goto handled;
#ifdef USE_NPPPD_EAP_RADIUS
	case PPP_PROTO_EAP:
		eap_input(&_this->eap, inp, lpkt - (inp - pkt));
		goto handled;
#endif
#ifdef	USE_NPPPD_MPPE
	case PPP_PROTO_MPPE:
#ifdef USE_NPPPD_PIPEX
		if (_this->pipex_enabled != 0)
			return -1; /* silent discard */
#endif /* USE_NPPPD_PIPEX */
		mppe_input(&_this->mppe, inp, lpkt - (inp - pkt));
		goto handled;
#endif
	default:
		if ((proto & 0xff00) == PPP_PROTO_NCP) {
			switch (proto & 0xff) {
			case NCP_CCP:	/* Compression */
#ifdef	USE_NPPPD_MPPE
				if (MPPE_MUST_NEGO(_this)) {
					fsm_input(&_this->ccp.fsm, inp,
					    lpkt - (inp - pkt));
					goto handled;
				}
				/* protocol-reject if MPPE is not necessary */
#endif
				break;
			case NCP_IPCP:	/* IPCP */
				fsm_input(&_this->ipcp.fsm, inp,
				    lpkt - (inp - pkt));
				goto handled;
			}
		}
	}
	/* Protocol reject.  Log it with protocol number */
	ppp_log(_this, LOG_INFO, "unhandled protocol %s, %d(%04x)",
	    proto_name(proto), proto, proto);

	if ((flags & PPP_IO_FLAGS_MPPE_ENCRYPTED) != 0) {
		/*
		 * Don't return a protocol-reject for the packet was encrypted,
		 * because lcp protocol-reject is not encrypted by mppe.
		 */
	} else {
		/*
		 * as RFC1661: Rejected-Information MUST be truncated to
		 * comply with the peer's established MRU.
		 */
		lcp_send_protrej(&_this->lcp, inp_proto,
		    MIN(lpkt - (inp_proto - pkt), NPPPD_MIN_MRU - 32));
	}

	return 1;
handled:

	return 0;
}

/** This function is called to output PPP packets */
inline void
ppp_output(npppd_ppp *_this, uint16_t proto, u_char code, u_char id,
    u_char *datap, int ldata)
{
	u_char *outp;
	int outlen, hlen, is_lcp = 0;

	outp = _this->outpacket_buf;

	/* No header compressions for LCP */
	is_lcp = (proto == PPP_PROTO_LCP)? 1 : 0;

	if (_this->has_acf == 0 ||
	    (!is_lcp && psm_peer_opt_is_accepted(&_this->lcp, acfc))) {
		/*
		 * Don't add ACF(Address and Control Field) if ACF is not
		 * needed on this link or ACFC is negotiated.
		 */
	} else {
		PUTCHAR(PPP_ALLSTATIONS, outp);
		PUTCHAR(PPP_UI, outp);
	}
	if (!is_lcp && proto <= 0xff &&
	    psm_peer_opt_is_accepted(&_this->lcp, pfc)) {
		/*
		 * Protocol Field Compression
		 */
		PUTCHAR(proto, outp);
	} else {
		PUTSHORT(proto, outp);
	}
	hlen = outp - _this->outpacket_buf;

	if (_this->mru > 0) {
		if (MRU_PKTLEN(_this->mru, proto) < ldata) {
			PPP_DBG((_this, LOG_ERR, "packet too large %d. mru=%d",
			    ldata , _this->mru));
			_this->oerrors++;
			PPP_ASSERT("NOT REACHED HERE" == NULL);
			return;
		}
	}

	if (code != 0) {
		outlen = ldata + HEADERLEN;

		PUTCHAR(code, outp);
		PUTCHAR(id, outp);
		PUTSHORT(outlen, outp);
	} else {
		outlen = ldata;
	}

	if (outp != datap && ldata > 0)
		memmove(outp, datap, ldata);

	if (_this->log_dump_out != 0 && debug_get_debugfp() != NULL) {
		char buf[256];

		snprintf(buf, sizeof(buf), "log.%s.out.pktdump",
		    proto_name(proto));
		if (ppp_config_str_equal(_this, buf, "true", 0) != 0)  {
			ppp_log(_this, LOG_DEBUG,
			    "PPP output dump proto=%s(%d/%04x)",
			    proto_name(proto), proto, proto);
			show_hd(debug_get_debugfp(),
			    _this->outpacket_buf, outlen + hlen);
		}
	}
	_this->send_packet(_this, _this->outpacket_buf, outlen + hlen, 0);
}