/* 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; }
/* 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; }
/* 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; }
/** * 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 }
/** 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 }
/** 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 }
/* 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; }
/* 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; }
/** * 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); }