#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if_dl.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <netdb.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <syslog.h>
#include <sys/time.h>
#include <time.h>
#include <event.h>
#include "npppd.h"
#include "time_utils.h"
#include "ppp.h"
#include "psm-opt.h"
#ifdef USE_NPPPD_RADIUS
#include <radius.h>
#include "npppd_radius.h"
#endif
#include "debugutil.h"
#ifdef PPP_DEBUG
#define PPP_DBG(x) ppp_log x
#define PPP_ASSERT(cond) \
if (!(cond)) { \
fprintf(stderr, \
"\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\
, __func__, __FILE__, __LINE__); \
abort(); \
}
#else
#define PPP_ASSERT(cond)
#define PPP_DBG(x)
#endif
static u_int ppp_seq = 0;
static void ppp_stop0 (npppd_ppp *);
static int ppp_recv_packet (npppd_ppp *, unsigned char *, int, int);
static const char *ppp_peer_auth_string (npppd_ppp *);
static void ppp_idle_timeout (int, short, void *);
#ifdef USE_NPPPD_PIPEX
static void ppp_on_network_pipex(npppd_ppp *);
#endif
static uint32_t ppp_proto_bit(int);
#define AUTH_IS_PAP(ppp) ((ppp)->peer_auth == PPP_AUTH_PAP)
#define AUTH_IS_CHAP(ppp) ((ppp)->peer_auth == PPP_AUTH_CHAP_MD5 ||\
(ppp)->peer_auth == PPP_AUTH_CHAP_MS || \
(ppp)->peer_auth == PPP_AUTH_CHAP_MS_V2)
#define AUTH_IS_EAP(ppp) ((ppp)->peer_auth == PPP_AUTH_EAP)
npppd_ppp *
ppp_create()
{
npppd_ppp *_this;
if ((_this = calloc(1, sizeof(npppd_ppp))) == NULL) {
log_printf(LOG_ERR, "calloc() failed in %s(): %m", __func__ );
return NULL;
}
_this->snp.snp_family = AF_INET;
_this->snp.snp_len = sizeof(_this->snp);
_this->snp.snp_type = SNP_PPP;
_this->snp.snp_data_ptr = _this;
return _this;
}
int
ppp_init(npppd *pppd, npppd_ppp *_this)
{
struct tunnconf *conf;
PPP_ASSERT(_this != NULL);
PPP_ASSERT(strlen(_this->phy_label) > 0);
_this->id = -1;
_this->ifidx = -1;
_this->has_acf = 1;
_this->recv_packet = ppp_recv_packet;
_this->id = ppp_seq++;
_this->pppd = pppd;
lcp_init(&_this->lcp, _this);
conf = ppp_get_tunnconf(_this);
_this->mru = conf->mru;
if (_this->outpacket_buf == NULL) {
_this->outpacket_buf = malloc(_this->mru + 64);
if (_this->outpacket_buf == NULL){
log_printf(LOG_ERR, "malloc() failed in %s(): %m",
__func__);
return -1;
}
}
_this->adjust_mss = (conf->tcp_mss_adjust)? 1 : 0;
#ifdef USE_NPPPD_PIPEX
_this->use_pipex = (conf->pipex)? 1 : 0;
#endif
_this->ingress_filter = (conf->ingress_filter)? 1 : 0;
#ifdef USE_NPPPD_MPPE
mppe_init(&_this->mppe, _this);
#endif
ccp_init(&_this->ccp, _this);
ipcp_init(&_this->ipcp, _this);
pap_init(&_this->pap, _this);
chap_init(&_this->chap, _this);
_this->timeout_sec = conf->idle_timeout;
if (!evtimer_initialized(&_this->idle_event))
evtimer_set(&_this->idle_event, ppp_idle_timeout, _this);
if (conf->lcp_keepalive) {
_this->lcp.echo_interval = conf->lcp_keepalive_interval;
_this->lcp.echo_retry_interval =
conf->lcp_keepalive_retry_interval;
_this->lcp.echo_max_retries = conf->lcp_keepalive_max_retries;
} else {
_this->lcp.echo_interval = 0;
_this->lcp.echo_retry_interval = 0;
_this->lcp.echo_max_retries = 0;
}
_this->log_dump_in = (conf->debug_dump_pktin == 0)? 0 : 1;
_this->log_dump_out = (conf->debug_dump_pktout == 0)? 0 : 1;
return 0;
}
static void
ppp_set_tunnel_label(npppd_ppp *_this, char *buf, int lbuf)
{
int flag, af;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
u_char *ea;
hbuf[0] = 0;
sbuf[0] = 0;
af = ((struct sockaddr *)&_this->phy_info)->sa_family;
if (af == AF_LINK) {
ea = LLADDR((struct sockaddr_dl *)&_this->phy_info);
snprintf(buf, lbuf, "%02x:%02x:%02x:%02x:%02x:%02x", *ea,
*(ea + 1), *(ea + 2), *(ea + 3), *(ea + 4), *(ea + 5));
} else if (af < AF_MAX) {
flag = NI_NUMERICHOST;
if (af == AF_INET || af == AF_INET6)
flag |= NI_NUMERICSERV;
if (getnameinfo((struct sockaddr *)&_this->phy_info,
((struct sockaddr *)&_this->phy_info)->sa_len, hbuf,
sizeof(hbuf), sbuf, sizeof(sbuf), flag) != 0) {
ppp_log(_this, LOG_ERR, "getnameinfo() failed at %s",
__func__);
strlcpy(hbuf, "0.0.0.0", sizeof(hbuf));
strlcpy(sbuf, "0", sizeof(sbuf));
}
if (af == AF_INET || af == AF_INET6)
snprintf(buf, lbuf, "%s:%s", hbuf, sbuf);
else
snprintf(buf, lbuf, "%s", hbuf);
} else if (af == NPPPD_AF_PHONE_NUMBER) {
strlcpy(buf,
((npppd_phone_number *)&_this->phy_info)->pn_number, lbuf);
}
}
void
ppp_start(npppd_ppp *_this)
{
char label[512];
PPP_ASSERT(_this != NULL);
PPP_ASSERT(_this->recv_packet != NULL);
PPP_ASSERT(_this->send_packet != NULL);
PPP_ASSERT(_this->phy_close != NULL);
_this->start_time = time(NULL);
_this->start_monotime = get_monosec();
ppp_set_tunnel_label(_this, label, sizeof(label));
ppp_log(_this, LOG_INFO, "logtype=Started tunnel=%s(%s)",
_this->phy_label, label);
lcp_lowerup(&_this->lcp);
}
int
ppp_dialin_proxy_prepare(npppd_ppp *_this, dialin_proxy_info *dpi)
{
int renego_force, renego;
struct tunnconf *conf;
conf = ppp_get_tunnconf(_this);
renego = conf->proto.l2tp.lcp_renegotiation;
renego_force = conf->proto.l2tp.force_lcp_renegotiation;
if (renego_force)
renego = 1;
if (lcp_dialin_proxy(&_this->lcp, dpi, renego, renego_force) != 0) {
ppp_log(_this, LOG_ERR,
"Failed to dialin-proxy, proxied lcp is broken.");
return 1;
}
return 0;
}
static void
ppp_down_others(npppd_ppp *_this)
{
fsm_lowerdown(&_this->ccp.fsm);
fsm_lowerdown(&_this->ipcp.fsm);
npppd_release_ip(_this->pppd, _this);
if (AUTH_IS_PAP(_this))
pap_stop(&_this->pap);
if (AUTH_IS_CHAP(_this))
chap_stop(&_this->chap);
#ifdef USE_NPPPD_EAP_RADIUS
if (AUTH_IS_EAP(_this))
eap_stop(&_this->eap);
#endif
evtimer_del(&_this->idle_event);
}
void
ppp_stop(npppd_ppp *_this, const char *reason)
{
PPP_ASSERT(_this != NULL);
#ifdef USE_NPPPD_RADIUS
ppp_set_radius_terminate_cause(_this,
RADIUS_TERMNATE_CAUSE_ADMIN_RESET);
#endif
ppp_set_disconnect_cause(_this, PPP_DISCON_NORMAL, 0, 2 ,
NULL);
ppp_down_others(_this);
fsm_close(&_this->lcp.fsm, reason);
}
void
ppp_set_disconnect_cause(npppd_ppp *_this, npppd_ppp_disconnect_code code,
int proto, int direction, const char *message)
{
if (_this->disconnect_code == PPP_DISCON_NO_INFORMATION) {
_this->disconnect_code = code;
_this->disconnect_proto = proto;
_this->disconnect_direction = direction;
_this->disconnect_message = message;
}
}
void
ppp_set_radius_terminate_cause(npppd_ppp *_this, int cause)
{
if (_this->terminate_cause == 0)
_this->terminate_cause = cause;
}
static void
ppp_stop0(npppd_ppp *_this)
{
char mppe_str[BUFSIZ];
char label[512];
#ifdef USE_NPPPD_RADIUS
ppp_set_radius_terminate_cause(_this, RADIUS_TERMNATE_CAUSE_NAS_ERROR);
#endif
ppp_set_disconnect_cause(_this, PPP_DISCON_NORMAL, 0, 1 ,
NULL);
_this->end_monotime = get_monosec();
if (_this->phy_close != NULL)
_this->phy_close(_this);
_this->phy_close = NULL;
if (_this->lcp.dialin_proxy != 0 &&
_this->lcp.dialin_proxy_lcp_renegotiation == 0) {
} else if (_this->lcp.recv_ress == 0) {
if (_this->lcp.recv_reqs == 0)
ppp_log(_this, LOG_WARNING, "no PPP frames from the "
"peer. router/NAT issue? (may have filtered out)");
else
ppp_log(_this, LOG_WARNING, "my PPP frames may not "
"have arrived at the peer. router/NAT issue? (may "
"be the only-first-person problem)");
}
#ifdef USE_NPPPD_PIPEX
if (npppd_ppp_pipex_disable(_this->pppd, _this) != 0)
ppp_log(_this, LOG_ERR,
"npppd_ppp_pipex_disable() failed: %m");
#endif
ppp_set_tunnel_label(_this, label, sizeof(label));
#ifdef USE_NPPPD_MPPE
if (_this->mppe_started) {
snprintf(mppe_str, sizeof(mppe_str),
"mppe=yes mppe_in=%dbits,%s mppe_out=%dbits,%s",
_this->mppe.recv.keybits,
(_this->mppe.recv.stateless)? "stateless" : "stateful",
_this->mppe.send.keybits,
(_this->mppe.send.stateless)? "stateless" : "stateful");
} else
#endif
snprintf(mppe_str, sizeof(mppe_str), "mppe=no");
ppp_log(_this, LOG_NOTICE,
"logtype=TUNNELUSAGE user=\"%s\" duration=%ldsec layer2=%s "
"layer2from=%s auth=%s data_in=%llubytes,%upackets "
"data_out=%llubytes,%upackets error_in=%u error_out=%u %s "
"iface=%s",
_this->username[0]? _this->username : "<unknown>",
(long)(_this->end_monotime - _this->start_monotime),
_this->phy_label, label,
_this->username[0]? ppp_peer_auth_string(_this) : "none",
(unsigned long long)_this->ibytes, _this->ipackets,
(unsigned long long)_this->obytes, _this->opackets,
_this->ierrors, _this->oerrors, mppe_str,
npppd_ppp_get_iface_name(_this->pppd, _this));
#ifdef USE_NPPPD_RADIUS
npppd_ppp_radius_acct_stop(_this->pppd, _this);
#endif
npppd_on_ppp_stop(_this->pppd, _this);
npppd_ppp_unbind_iface(_this->pppd, _this);
#ifdef USE_NPPPD_MPPE
mppe_fini(&_this->mppe);
#endif
evtimer_del(&_this->idle_event);
npppd_release_ip(_this->pppd, _this);
ppp_destroy(_this);
}
void
ppp_destroy(void *ctx)
{
npppd_ppp *_this = ctx;
free(_this->proxy_authen_resp);
fsm_lowerdown(&_this->ccp.fsm);
fsm_lowerdown(&_this->ipcp.fsm);
pap_stop(&_this->pap);
chap_stop(&_this->chap);
free(_this->outpacket_buf);
free(_this);
}
static const char *
ppp_peer_auth_string(npppd_ppp *_this)
{
switch(_this->peer_auth) {
case PPP_AUTH_PAP: return "PAP";
case PPP_AUTH_CHAP_MD5: return "MD5-CHAP";
case PPP_AUTH_CHAP_MS: return "MS-CHAP";
case PPP_AUTH_CHAP_MS_V2: return "MS-CHAP-V2";
case PPP_AUTH_EAP: return "EAP";
default: return "ERROR";
}
}
void
ppp_lcp_up(npppd_ppp *_this)
{
#ifdef USE_NPPPD_MPPE
if (MPPE_IS_REQUIRED(_this) && !MPPE_MUST_NEGO(_this)) {
ppp_log(_this, LOG_ERR, "MPPE is required, auth protocol must "
"be MS-CHAP-V2 or EAP");
ppp_stop(_this, "Encryption required");
return;
}
#endif
if (_this->peer_mru > _this->mru)
_this->peer_mru = _this->mru;
if (_this->peer_auth != 0 && _this->auth_runonce == 0) {
if (AUTH_IS_PAP(_this)) {
pap_start(&_this->pap);
_this->auth_runonce = 1;
return;
}
if (AUTH_IS_CHAP(_this)) {
chap_start(&_this->chap);
_this->auth_runonce = 1;
return;
}
#ifdef USE_NPPPD_EAP_RADIUS
if (AUTH_IS_EAP(_this)) {
eap_init(&_this->eap, _this);
eap_start(&_this->eap);
return;
}
#endif
}
if (_this->peer_auth == 0)
ppp_auth_ok(_this);
}
void
ppp_lcp_finished(npppd_ppp *_this)
{
PPP_ASSERT(_this != NULL);
ppp_down_others(_this);
fsm_lowerdown(&_this->lcp.fsm);
ppp_stop0(_this);
}
void
ppp_phy_downed(npppd_ppp *_this)
{
PPP_ASSERT(_this != NULL);
ppp_down_others(_this);
fsm_lowerdown(&_this->lcp.fsm);
fsm_close(&_this->lcp.fsm, NULL);
#ifdef USE_NPPPD_RADIUS
ppp_set_radius_terminate_cause(_this,
RADIUS_TERMNATE_CAUSE_LOST_CARRIER);
#endif
ppp_stop0(_this);
}
static const char *
proto_name(uint16_t proto)
{
switch (proto) {
case PPP_PROTO_IP: return "ip";
case PPP_PROTO_LCP: return "lcp";
case PPP_PROTO_PAP: return "pap";
case PPP_PROTO_CHAP: return "chap";
case PPP_PROTO_EAP: return "eap";
case PPP_PROTO_MPPE: return "mppe";
case PPP_PROTO_NCP | NCP_CCP: return "ccp";
case PPP_PROTO_NCP | NCP_IPCP: return "ipcp";
case PPP_PROTO_NCP | NCP_IPV6CP: return "ipv6cp";
case PPP_PROTO_ACSP: return "acsp";
}
return "unknown";
}
void
ppp_auth_ok(npppd_ppp *_this)
{
if (npppd_ppp_bind_iface(_this->pppd, _this) != 0) {
ppp_log(_this, LOG_WARNING, "No interface binding.");
ppp_stop(_this, NULL);
return;
}
if (_this->realm != NULL) {
npppd_ppp_get_username_for_auth(_this->pppd, _this,
_this->username, _this->username);
if (!npppd_check_calling_number(_this->pppd, _this)) {
ppp_log(_this, LOG_ALERT,
"logtype=TUNNELDENY user=\"%s\" "
"reason=\"Calling number check is failed\"",
_this->username);
ppp_stop(_this, NULL);
return;
}
}
if (_this->peer_auth != 0) {
if (!npppd_check_user_max_session(_this->pppd, _this)) {
ppp_stop(_this, NULL);
return;
}
PPP_ASSERT(_this->realm != NULL);
}
if (!npppd_ppp_iface_is_ready(_this->pppd, _this)) {
ppp_log(_this, LOG_WARNING,
"interface '%s' is not ready.",
npppd_ppp_get_iface_name(_this->pppd, _this));
ppp_stop(_this, NULL);
return;
}
free(_this->proxy_authen_resp);
_this->proxy_authen_resp = NULL;
fsm_lowerup(&_this->ipcp.fsm);
fsm_open(&_this->ipcp.fsm);
#ifdef USE_NPPPD_MPPE
if (MPPE_MUST_NEGO(_this)) {
fsm_lowerup(&_this->ccp.fsm);
fsm_open(&_this->ccp.fsm);
}
#endif
return;
}
static void
ppp_idle_timeout(int fd, short evtype, void *context)
{
npppd_ppp *_this;
_this = context;
ppp_log(_this, LOG_NOTICE, "Idle timeout(%d sec)", _this->timeout_sec);
#ifdef USE_NPPPD_RADIUS
ppp_set_radius_terminate_cause(_this,
RADIUS_TERMNATE_CAUSE_IDLE_TIMEOUT);
#endif
ppp_stop(_this, NULL);
}
void
ppp_reset_idle_timeout(npppd_ppp *_this)
{
struct timeval tv;
evtimer_del(&_this->idle_event);
if (_this->timeout_sec > 0) {
tv.tv_usec = 0;
tv.tv_sec = _this->timeout_sec;
evtimer_add(&_this->idle_event, &tv);
}
}
void
ppp_ipcp_opened(npppd_ppp *_this)
{
time_t curr_time;
curr_time = get_monosec();
npppd_set_ip_enabled(_this->pppd, _this, 1);
if (_this->logged_acct_start == 0) {
char label[512], ipstr[64];
ppp_set_tunnel_label(_this, label, sizeof(label));
strlcpy(ipstr, " ip=", sizeof(ipstr));
strlcat(ipstr, inet_ntoa(_this->ppp_framed_ip_address),
sizeof(ipstr));
if (_this->ppp_framed_ip_netmask.s_addr != 0xffffffffL) {
strlcat(ipstr, ":", sizeof(ipstr));
strlcat(ipstr, inet_ntoa(_this->ppp_framed_ip_netmask),
sizeof(ipstr));
}
ppp_log(_this, LOG_NOTICE,
"logtype=TUNNELSTART user=\"%s\" duration=%lusec layer2=%s "
"layer2from=%s auth=%s %s iface=%s%s",
_this->username[0]? _this->username : "<unknown>",
(long)(curr_time - _this->start_monotime),
_this->phy_label, label,
_this->username[0]? ppp_peer_auth_string(_this) : "none",
ipstr, npppd_ppp_get_iface_name(_this->pppd, _this),
(_this->lcp.dialin_proxy != 0)? " dialin_proxy=yes" : ""
);
#ifdef USE_NPPPD_RADIUS
npppd_ppp_radius_acct_start(_this->pppd, _this);
#endif
npppd_on_ppp_start(_this->pppd, _this);
_this->logged_acct_start = 1;
ppp_reset_idle_timeout(_this);
}
#ifdef USE_NPPPD_PIPEX
ppp_on_network_pipex(_this);
#endif
}
void
ppp_ccp_opened(npppd_ppp *_this)
{
#ifdef USE_NPPPD_MPPE
if (_this->ccp.mppe_rej == 0) {
if (_this->mppe_started == 0) {
mppe_start(&_this->mppe);
}
} else {
ppp_log(_this, LOG_INFO, "mppe is rejected by peer");
if (_this->mppe.required)
ppp_stop(_this, "MPPE is required");
}
#endif
#ifdef USE_NPPPD_PIPEX
ppp_on_network_pipex(_this);
#endif
}
void
ppp_ccp_stopped(npppd_ppp *_this)
{
#ifdef USE_NPPPD_MPPE
if (_this->mppe.required) {
ppp_stop(_this, NULL);
return;
}
#endif
#ifdef USE_NPPPD_PIPEX
ppp_on_network_pipex(_this);
#endif
}
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) {
} else if (inp[0] == PPP_ALLSTATIONS && inp[1] == PPP_UI) {
inp += 2;
} else {
if (!psm_opt_is_accepted(&_this->lcp, acfc) &&
_this->logged_no_address == 0) {
ppp_log(_this, LOG_INFO,
"%s: Rcvd broken frame. ACFC is not accepted, "
"but received ppp frame that has no address.",
__func__);
_this->logged_no_address = 1;
}
}
inp_proto = inp;
if ((inp[0] & 0x01) != 0) {
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 (flags & PPP_IO_FLAGS_DELAYED && proto != PPP_PROTO_IP)
return 1;
if (_this->log_dump_in != 0 && debug_get_debugfp() != NULL) {
struct tunnconf *conf = ppp_get_tunnconf(_this);
if ((ppp_proto_bit(proto) & conf->debug_dump_pktin) != 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 == NPPPD_TUNNEL_PPPOE) {
switch (proto) {
case PPP_PROTO_IP:
return 2;
case PPP_PROTO_NCP | NCP_CCP:
if (lpkt - (inp - pkt) < 4)
break;
if (*inp == 0x0e ||
*inp == 0x0f ) {
return 2;
}
default:
break;
}
}
#endif
switch (proto) {
#ifdef USE_NPPPD_MPPE
case PPP_PROTO_IP:
if ((flags & PPP_IO_FLAGS_MPPE_ENCRYPTED) == 0) {
if (MPPE_IS_REQUIRED(_this)) {
if (_this->logged_naked_ip == 0) {
ppp_log(_this, LOG_INFO,
"mppe is required but received "
"naked IP.");
_this->logged_naked_ip = 1;
}
return 1;
}
if (MPPE_RECV_READY(_this)) {
ppp_log(_this, LOG_WARNING,
"mppe is available but received naked IP.");
}
}
break;
case PPP_PROTO_MPPE:
#ifdef USE_NPPPD_MPPE
if (!MPPE_RECV_READY(_this)) {
#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;
#endif
mppe_input(&_this->mppe, inp, lpkt - (inp - pkt));
goto handled;
#endif
default:
if ((proto & 0xff00) == PPP_PROTO_NCP) {
switch (proto & 0xff) {
case NCP_CCP:
#ifdef USE_NPPPD_MPPE
if (MPPE_MUST_NEGO(_this)) {
fsm_input(&_this->ccp.fsm, inp,
lpkt - (inp - pkt));
goto handled;
}
#endif
break;
case NCP_IPCP:
fsm_input(&_this->ipcp.fsm, inp,
lpkt - (inp - pkt));
goto handled;
}
}
}
ppp_log(_this, LOG_INFO, "unhandled protocol %s, %d(%04x)",
proto_name(proto), proto, proto);
if ((flags & PPP_IO_FLAGS_MPPE_ENCRYPTED) != 0) {
} else {
lcp_send_protrej(&_this->lcp, inp_proto,
MINIMUM(lpkt - (inp_proto - pkt), NPPPD_MIN_MRU - 32));
}
return 1;
handled:
return 0;
}
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;
is_lcp = (proto == PPP_PROTO_LCP)? 1 : 0;
if (_this->has_acf == 0 ||
(!is_lcp && psm_peer_opt_is_accepted(&_this->lcp, acfc))) {
} else {
PUTCHAR(PPP_ALLSTATIONS, outp);
PUTCHAR(PPP_UI, outp);
}
if (!is_lcp && proto <= 0xff &&
psm_peer_opt_is_accepted(&_this->lcp, pfc)) {
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) {
struct tunnconf *conf = ppp_get_tunnconf(_this);
if ((ppp_proto_bit(proto) & conf->debug_dump_pktout) != 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);
}
u_char *
ppp_packetbuf(npppd_ppp *_this, int proto)
{
int save;
save = 0;
if (proto != PPP_PROTO_LCP) {
if (psm_peer_opt_is_accepted(&_this->lcp, acfc))
save += 2;
if (proto <= 0xff && psm_peer_opt_is_accepted(&_this->lcp, pfc))
save += 1;
}
return _this->outpacket_buf + (PPP_HDRLEN - save);
}
int
ppp_log(npppd_ppp *_this, int prio, const char *fmt, ...)
{
int status;
char logbuf[BUFSIZ];
va_list ap;
PPP_ASSERT(_this != NULL);
va_start(ap, fmt);
snprintf(logbuf, sizeof(logbuf), "ppp id=%u layer=base %s",
_this->id, fmt);
status = vlog_printf(prio, logbuf, ap);
va_end(ap);
return status;
}
#ifdef USE_NPPPD_PIPEX
static void
ppp_on_network_pipex(npppd_ppp *_this)
{
if (_this->use_pipex == 0)
return;
if (_this->tunnel_type != NPPPD_TUNNEL_PPTP &&
_this->tunnel_type != NPPPD_TUNNEL_PPPOE &&
_this->tunnel_type != NPPPD_TUNNEL_L2TP)
return;
if (_this->pipex_started != 0)
return;
if (_this->assigned_ip4_enabled != 0 &&
(!MPPE_MUST_NEGO(_this) || _this->ccp.fsm.state == OPENED ||
_this->ccp.fsm.state == STOPPED)) {
if (npppd_ppp_pipex_enable(_this->pppd, _this) != 0) {
ppp_log(_this, LOG_WARNING, "failed enable pipex: %m");
ppp_phy_downed(_this);
return;
}
ppp_log(_this, LOG_NOTICE, "Using pipex=%s",
(_this->pipex_enabled != 0)? "yes" : "no");
_this->pipex_started = 1;
}
}
#endif
static uint32_t
ppp_proto_bit(int proto)
{
switch (proto) {
case PPP_PROTO_IP: return NPPPD_PROTO_BIT_IP;
case PPP_PROTO_LCP: return NPPPD_PROTO_BIT_LCP;
case PPP_PROTO_PAP: return NPPPD_PROTO_BIT_PAP;
case PPP_PROTO_CHAP: return NPPPD_PROTO_BIT_CHAP;
case PPP_PROTO_EAP: return NPPPD_PROTO_BIT_EAP;
case PPP_PROTO_MPPE: return NPPPD_PROTO_BIT_MPPE;
case PPP_PROTO_NCP | NCP_CCP: return NPPPD_PROTO_BIT_CCP;
case PPP_PROTO_NCP | NCP_IPCP: return NPPPD_PROTO_BIT_IPCP;
}
return 0;
}
struct tunnconf tunnconf_default_l2tp = {
.mru = 1360,
.tcp_mss_adjust = false,
.pipex = true,
.ingress_filter = false,
.lcp_keepalive = false,
.lcp_keepalive_interval = DEFAULT_LCP_ECHO_INTERVAL,
.lcp_keepalive_retry_interval = DEFAULT_LCP_ECHO_RETRY_INTERVAL,
.lcp_keepalive_max_retries = DEFAULT_LCP_ECHO_MAX_RETRIES,
.auth_methods = NPPPD_AUTH_METHODS_CHAP | NPPPD_AUTH_METHODS_MSCHAPV2,
.mppe_yesno = true,
.mppe_required = false,
.mppe_keylen = NPPPD_MPPE_40BIT | NPPPD_MPPE_56BIT | NPPPD_MPPE_128BIT,
.mppe_keystate = NPPPD_MPPE_STATELESS | NPPPD_MPPE_STATEFUL,
.callnum_check = 0,
.proto = {
.l2tp = {
.hostname = NULL,
.vendor_name = NULL,
.listen = TAILQ_HEAD_INITIALIZER(
tunnconf_default_l2tp.proto.l2tp.listen),
.data_use_seq = true,
.require_ipsec = false,
.lcp_renegotiation = true,
.force_lcp_renegotiation = false,
}
}
};
struct tunnconf tunnconf_default_pptp = {
.mru = 1400,
.tcp_mss_adjust = false,
.pipex = true,
.ingress_filter = false,
.lcp_keepalive = true,
.lcp_keepalive_interval = DEFAULT_LCP_ECHO_INTERVAL,
.lcp_keepalive_retry_interval = DEFAULT_LCP_ECHO_RETRY_INTERVAL,
.lcp_keepalive_max_retries = DEFAULT_LCP_ECHO_MAX_RETRIES,
.auth_methods = NPPPD_AUTH_METHODS_CHAP | NPPPD_AUTH_METHODS_MSCHAPV2,
.mppe_yesno = true,
.mppe_required = true,
.mppe_keylen = NPPPD_MPPE_40BIT | NPPPD_MPPE_56BIT | NPPPD_MPPE_128BIT,
.mppe_keystate = NPPPD_MPPE_STATELESS | NPPPD_MPPE_STATEFUL,
.callnum_check = 0,
.proto = {
.pptp = {
.hostname = NULL,
.vendor_name = NULL,
.listen = TAILQ_HEAD_INITIALIZER(
tunnconf_default_pptp.proto.pptp.listen),
}
}
};
struct tunnconf tunnconf_default_pppoe = {
.mru = 1492,
.tcp_mss_adjust = false,
.pipex = true,
.ingress_filter = false,
.lcp_keepalive = true,
.lcp_keepalive_interval = DEFAULT_LCP_ECHO_INTERVAL,
.lcp_keepalive_retry_interval = DEFAULT_LCP_ECHO_RETRY_INTERVAL,
.lcp_keepalive_max_retries = DEFAULT_LCP_ECHO_MAX_RETRIES,
.auth_methods = NPPPD_AUTH_METHODS_CHAP | NPPPD_AUTH_METHODS_MSCHAPV2,
.mppe_yesno = true,
.mppe_required = false,
.mppe_keylen = NPPPD_MPPE_40BIT | NPPPD_MPPE_56BIT | NPPPD_MPPE_128BIT,
.mppe_keystate = NPPPD_MPPE_STATELESS | NPPPD_MPPE_STATEFUL,
.callnum_check = 0,
.proto = {
.pppoe = {
.accept_any_service = true,
}
}
};
struct tunnconf *
ppp_get_tunnconf(npppd_ppp *_this)
{
struct tunnconf *conf;
conf = npppd_get_tunnconf(_this->pppd, _this->phy_label);
if (conf != NULL)
return conf;
switch (_this->tunnel_type) {
case NPPPD_TUNNEL_L2TP:
return &tunnconf_default_l2tp;
break;
case NPPPD_TUNNEL_PPTP:
return &tunnconf_default_pptp;
break;
case NPPPD_TUNNEL_PPPOE:
return &tunnconf_default_pppoe;
break;
}
return NULL;
}