summaryrefslogtreecommitdiff
path: root/usr.sbin/npppd/pptp/pptp_call.c
diff options
context:
space:
mode:
authorYASUOKA Masahiko <yasuoka@cvs.openbsd.org>2010-01-11 04:20:58 +0000
committerYASUOKA Masahiko <yasuoka@cvs.openbsd.org>2010-01-11 04:20:58 +0000
commitf24f75f44d582e005fed41d187261a034bb7628a (patch)
treec2b790f0da2c27916ed9341222df263ee87ec950 /usr.sbin/npppd/pptp/pptp_call.c
parent1087250c10f476d6aedd9c44ca4ba96ce45792f6 (diff)
Initial import npppd(8). npppd is a new PPP daemon that handles many
ppp sessions as a server. It supports L2TP, PPTP and PPPoE as tunneling. ok mcbride@ dlg@ deraadt@ reyk@.
Diffstat (limited to 'usr.sbin/npppd/pptp/pptp_call.c')
-rw-r--r--usr.sbin/npppd/pptp/pptp_call.c852
1 files changed, 852 insertions, 0 deletions
diff --git a/usr.sbin/npppd/pptp/pptp_call.c b/usr.sbin/npppd/pptp/pptp_call.c
new file mode 100644
index 00000000000..0bdc6835638
--- /dev/null
+++ b/usr.sbin/npppd/pptp/pptp_call.c
@@ -0,0 +1,852 @@
+/*-
+ * Copyright (c) 2009 Internet Initiative Japan Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/* $Id: pptp_call.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */
+/**@file
+ * PPTPコールの実装。PACを仮定しています。
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <event.h>
+
+#ifdef USE_LIBSOCKUTIL
+#include <seil/sockfromto.h>
+#endif
+
+#include "bytebuf.h"
+#include "slist.h"
+#include "hash.h"
+#include "debugutil.h"
+#include "time_utils.h"
+
+#include "pptp.h"
+#include "pptp_local.h"
+#include "pptp_subr.h"
+
+#ifdef PPTP_CALL_DEBUG
+#define PPTP_CALL_DBG(x) pptp_call_log x
+#define PPTP_CALL_ASSERT(x) ASSERT(x)
+#else
+#define PPTP_CALL_DBG(x)
+#define PPTP_CALL_ASSERT(x)
+#endif
+
+static void pptp_call_log (pptp_call *, int, const char *, ...) __printflike(3,4);
+
+
+static void pptp_call_notify_down (pptp_call *);
+static int pptp_call_recv_SLI (pptp_call *, u_char *, int);
+static int pptp_call_recv_CCR (pptp_call *, u_char *, int);
+static int pptp_call_recv_OCRQ (pptp_call *, u_char *, int);
+static void pptp_call_send_CDN (pptp_call *, int, int, int, const char *);
+static int pptp_call_send_OCRP (pptp_call *, int, int, int);
+static int pptp_call_gre_output (pptp_call *, int, int, u_char *, int);
+static int pptp_call_bind_ppp (pptp_call *);
+static void pptp_call_log (pptp_call *, int, const char *, ...);
+static void pptp_call_OCRQ_string (struct pptp_ocrq *, char *, int);
+static void pptp_call_OCRP_string (struct pptp_ocrp *, char *, int);
+static void pptp_call_ppp_input (pptp_call *, unsigned char *, int);
+static char * pptp_call_state_string(int);
+
+/* not used
+static int pptp_call_send_SLI (pptp_call *);
+ */
+
+#define SEQ_LT(a,b) ((int)((a) - (b)) < 0)
+#define SEQ_LE(a,b) ((int)((a) - (b)) <= 0)
+#define SEQ_GT(a,b) ((int)((a) - (b)) > 0)
+#define SEQ_GE(a,b) ((int)((a) - (b)) >= 0)
+#define SEQ_SUB(a,b) ((int32_t)((a) - (b)))
+
+/** 切り上げ割算 */
+#define RUPDIV(n,d) (((n) + ((d) - ((n) % (d)))) / (d))
+
+/***********************************************************************
+ * インスタンス操作関連
+ ***********************************************************************/
+/** pptp_call インスタンスを生成します */
+pptp_call *
+pptp_call_create(void)
+{
+ pptp_call *_this;
+
+ if ((_this = malloc(sizeof(pptp_call))) == NULL)
+ return NULL;
+
+ return _this;
+}
+
+/** pptp_call インスタンスを初期化します */
+int
+pptp_call_init(pptp_call *_this, pptp_ctrl *ctrl)
+{
+ memset(_this, 0, sizeof(pptp_call));
+ _this->ctrl = ctrl;
+
+ _this->maxwinsz = PPTP_CALL_DEFAULT_MAXWINSZ;
+ _this->winsz = RUPDIV(_this->maxwinsz, 2);
+ _this->last_io = get_monosec();
+ _this->snd_nxt = 1;
+
+ return 0;
+ return 1;
+}
+
+/** pptp_call インスタンスを開始します */
+int
+pptp_call_start(pptp_call *_this)
+{
+ if (pptp_call_bind_ppp(_this) != 0)
+ return 1;
+
+ return 0;
+}
+/** pptp_call インスタンスを終了します */
+int
+pptp_call_stop(pptp_call *_this)
+{
+ if (_this->state != PPTP_CALL_STATE_CLEANUP_WAIT)
+ pptp_call_disconnect(_this, 0, 0, NULL);
+
+ pptp_call_log(_this, LOG_NOTICE, "logtype=Terminated");
+ pptpd_release_call(_this->ctrl->pptpd, _this);
+
+ return 0;
+}
+
+/** pptp_call インスタンスを解放します */
+void
+pptp_call_destroy(pptp_call *_this)
+{
+ PPTP_CALL_ASSERT(_this != NULL);
+ free(_this);
+}
+
+/** PPTPコールを切断します。*/
+void
+pptp_call_disconnect(pptp_call *_this, int result, int error, const char *
+ statistics)
+{
+ if (_this->state == PPTP_CALL_STATE_CLEANUP_WAIT) {
+ pptp_call_notify_down(_this);
+ return;
+ }
+ if (result > 0)
+ pptp_call_send_CDN(_this, result, error, 0, statistics);
+ _this->state = PPTP_CALL_STATE_CLEANUP_WAIT;
+ pptp_call_notify_down(_this);
+}
+
+/***********************************************************************
+ * コントロールパケットの入出力
+ ***********************************************************************/
+/** コントロールパケットの入力 */
+void
+pptp_call_input(pptp_call *_this, int mes_type, u_char *pkt, int lpkt)
+{
+
+ PPTP_CALL_ASSERT(_this != NULL);
+ PPTP_CALL_ASSERT(pkt != NULL);
+ PPTP_CALL_ASSERT(lpkt >= 4);
+
+ _this->last_io = get_monosec();
+ switch (mes_type) {
+ case PPTP_CTRL_MES_CODE_OCRQ:
+ if (_this->state != PPTP_CALL_STATE_IDLE) {
+ pptp_call_send_OCRP(_this,
+ PPTP_OCRP_RESULT_GENERIC_ERROR,
+ PPTP_ERROR_BAD_CALL, 0);
+ goto bad_state;
+ }
+ if (pptp_call_recv_OCRQ(_this, pkt, lpkt) != 0) {
+ pptp_call_send_OCRP(_this,
+ PPTP_OCRP_RESULT_GENERIC_ERROR,
+ PPTP_ERROR_BAD_CALL, 0);
+ return;
+ }
+ if (pptpd_assign_call(_this->ctrl->pptpd, _this) != 0) {
+ pptp_call_send_OCRP(_this, PPTP_OCRP_RESULT_BUSY,
+ PPTP_ERROR_NONE, 0);
+ return;
+ }
+ if (pptp_call_send_OCRP(_this, PPTP_OCRP_RESULT_CONNECTED,
+ PPTP_ERROR_NONE, 0) != 0) {
+ pptp_call_disconnect(_this,
+ PPTP_CDN_RESULT_GENRIC_ERROR,
+ PPTP_ERROR_PAC_ERROR, NULL);
+ return;
+ }
+ if (pptp_call_start(_this) != 0) {
+ pptp_call_disconnect(_this,
+ PPTP_CDN_RESULT_GENRIC_ERROR,
+ PPTP_ERROR_PAC_ERROR, NULL);
+ return;
+ }
+ _this->state = PPTP_CALL_STATE_ESTABLISHED;
+ break;
+ case PPTP_CTRL_MES_CODE_SLI:
+ if (pptp_call_recv_SLI(_this, pkt, lpkt) != 0) {
+ return;
+ }
+ return;
+ case PPTP_CTRL_MES_CODE_CCR:
+ pptp_call_recv_CCR(_this, pkt, lpkt);
+ if (_this->state == PPTP_CALL_STATE_ESTABLISHED) {
+ pptp_call_disconnect(_this, PPTP_CDN_RESULT_REQUEST,
+ PPTP_ERROR_NONE, NULL);
+ } else {
+ pptp_call_disconnect(_this, 0, 0, NULL);
+ if (_this->state != PPTP_CALL_STATE_CLEANUP_WAIT)
+ goto bad_state;
+ }
+ return;
+ default:
+ pptp_call_log(_this, LOG_WARNING,
+ "Unhandled control message type=%s(%d)",
+ pptp_ctrl_mes_type_string(mes_type), mes_type);
+ }
+ return;
+bad_state:
+ pptp_call_log(_this, LOG_WARNING,
+ "Received control message %s(%d) in bad state=%s",
+ pptp_ctrl_mes_type_string(mes_type), mes_type,
+ pptp_call_state_string(_this->state));
+}
+
+/**
+ * Set-Link-Info の受信
+ * <p>
+ * この実装では『同期フレーム』しかサポートしないので、ACCM
+ * (Asynchronous Control Character Map) は、保持しない。</p>
+ */
+static int
+pptp_call_recv_SLI(pptp_call *_this, u_char *pkt, int lpkt)
+{
+ struct pptp_sli *sli;
+
+ if (lpkt < sizeof(struct pptp_sli)) {
+ pptp_call_log(_this, LOG_ERR, "Received bad SLI: packet too "
+ "short: %d < %d", lpkt, (int)sizeof(struct pptp_sli));
+ return 1;
+ }
+ sli = (struct pptp_sli *)pkt;
+ sli->send_accm = ntohl(sli->send_accm);
+ sli->recv_accm = ntohl(sli->recv_accm);
+ if (sli->send_accm != 0xffffffffL ||
+ sli->recv_accm != 0xffffffffL) {
+ pptp_call_log(_this, LOG_WARNING,
+ "RecvSLI Received bad ACCM %08x:%08x: asynchronous framing "
+ "is not supported.\n", sli->send_accm, sli->recv_accm);
+ return 1;
+ }
+ pptp_call_log(_this, LOG_INFO, "RecvSLI accm=%08x:%08x",
+ sli->send_accm, sli->recv_accm);
+
+ return 0;
+}
+
+#if 0
+/*
+ * 『PPTPパススルー』機能をサポートする一部のルータは、PAC から SLI を
+ * を受信すると、以後 PAC => PNS の 1723/tcp パケットを落すモノがある
+ * ようです。(今のところ富士通製 FLASHWAVE がそんな感じ)
+ *
+ * このため、使い途のわからない SLI は npppd からは送信しないことにしました。
+ * See idgw-develop 5916
+ */
+/** SLI の送信 */
+static int
+pptp_call_send_SLI(pptp_call *_this)
+{
+ int lpkt;
+ struct pptp_sli *sli;
+
+ sli = bytebuffer_pointer(_this->ctrl->send_buf);
+ lpkt = bytebuffer_remaining(_this->ctrl->send_buf);
+ if (lpkt < sizeof(struct pptp_sli)) {
+ pptp_call_log(_this, LOG_ERR,
+ "SendOCRP failed: No buffer space available");
+ return -1;
+ }
+ memset(sli, 0, sizeof(struct pptp_sli));
+
+ pptp_init_header(&sli->header, sizeof(struct pptp_sli),
+ PPTP_CTRL_MES_CODE_SLI);
+
+ sli->peers_call_id = _this->id;
+ sli->send_accm = 0xffffffff;
+ sli->recv_accm = 0xffffffff;
+
+ _this->last_io = get_monosec();
+ pptp_call_log(_this, LOG_INFO, "SendSLI accm=%08x:%08x",
+ sli->send_accm, sli->recv_accm);
+ sli->peers_call_id = htons(sli->peers_call_id);
+ sli->send_accm = htonl(sli->send_accm);
+ sli->recv_accm = htonl(sli->recv_accm);
+ pptp_ctrl_output(_this->ctrl, NULL, sizeof(struct pptp_sli));
+
+ return 0;
+}
+#endif
+
+/** Call-Clear-Request の受信 */
+static int
+pptp_call_recv_CCR(pptp_call *_this, u_char *pkt, int lpkt)
+{
+ struct pptp_ccr *ccr;
+
+ // サイズ検査
+ PPTP_CALL_ASSERT(lpkt >= sizeof(struct pptp_ccr));
+ if (lpkt < sizeof(struct pptp_ccr)) {
+ // call_id チェック済みでここに呼ばれるので、起こり得ない。
+ return 1;
+ }
+ ccr = (struct pptp_ccr *)pkt;
+
+ // バイトオーダー
+ ccr->call_id = ntohs(ccr->call_id);
+ pptp_call_log(_this, LOG_INFO, "RecvCCR call_id=%u", ccr->call_id);
+
+ return 0;
+}
+
+/** Call-Disconnect-Notify の送信 */
+static void
+pptp_call_send_CDN(pptp_call *_this, int result, int error, int cause,
+ const char *statistics)
+{
+ int lpkt;
+ struct pptp_cdn *cdn;
+
+ cdn = bytebuffer_pointer(_this->ctrl->send_buf);
+ lpkt = bytebuffer_remaining(_this->ctrl->send_buf);
+ if (lpkt < sizeof(struct pptp_cdn)) {
+ pptp_call_log(_this, LOG_ERR,
+ "SendCCR failed: No buffer space available");
+ return;
+ }
+ memset(cdn, 0, sizeof(struct pptp_cdn));
+
+ pptp_init_header(&cdn->header, sizeof(struct pptp_cdn),
+ PPTP_CTRL_MES_CODE_CDN);
+
+ cdn->call_id = _this->id;
+ cdn->result_code = result;
+ cdn->error_code = error;
+ cdn->cause_code = cause;
+ if (statistics != NULL)
+ strlcpy(cdn->statistics, statistics, sizeof(cdn->statistics));
+
+ pptp_call_log(_this, LOG_INFO, "SendCDN "
+ "call_id=%u result=%s(%d) error=%s(%d) cause=%d statistics=%s",
+ cdn->call_id,
+ pptp_CDN_result_string(cdn->result_code), cdn->result_code,
+ pptp_general_error_string(cdn->error_code), cdn->error_code,
+ cdn->cause_code,
+ (statistics == NULL)? "(none)" : (char *)cdn->statistics);
+
+ cdn->call_id = htons(cdn->call_id);
+ cdn->cause_code = htons(cdn->cause_code);
+
+ _this->last_io = get_monosec();
+ pptp_ctrl_output(_this->ctrl, NULL, sizeof(struct pptp_cdn));
+}
+
+/** Outgoing-Call-Reply の送信 */
+static int
+pptp_call_send_OCRP(pptp_call *_this, int result, int error, int cause)
+{
+ int lpkt;
+ struct pptp_ocrp *ocrp;
+ char logbuf[512];
+
+ ocrp = bytebuffer_pointer(_this->ctrl->send_buf);
+ lpkt = bytebuffer_remaining(_this->ctrl->send_buf);
+ if (lpkt < sizeof(struct pptp_ocrp)) {
+ pptp_call_log(_this, LOG_ERR,
+ "SendOCRP failed: No buffer space available");
+ return -1;
+ }
+ memset(ocrp, 0, sizeof(struct pptp_ocrp));
+
+ pptp_init_header(&ocrp->header, sizeof(struct pptp_ocrp),
+ PPTP_CTRL_MES_CODE_OCRP);
+
+ ocrp->call_id = _this->id;
+ ocrp->peers_call_id = _this->peers_call_id;
+ ocrp->result_code = result;
+ ocrp->error_code = error;
+ ocrp->cause_code = cause;
+ ocrp->connect_speed = PPTP_CALL_CONNECT_SPEED;
+ ocrp->recv_winsz = _this->maxwinsz;
+ ocrp->packet_proccessing_delay = PPTP_CALL_INITIAL_PPD;
+ ocrp->physical_channel_id = _this->id;
+
+ pptp_call_OCRP_string(ocrp, logbuf, sizeof(logbuf));
+ pptp_call_log(_this, LOG_INFO, "SendOCRP %s", logbuf);
+
+ ocrp->call_id = htons(ocrp->call_id);
+ ocrp->peers_call_id = htons(ocrp->peers_call_id);
+ ocrp->cause_code = htons(ocrp->cause_code);
+ ocrp->connect_speed = htons(ocrp->connect_speed);
+ ocrp->recv_winsz = htons(ocrp->recv_winsz);
+ ocrp->packet_proccessing_delay = htons(ocrp->packet_proccessing_delay);
+ ocrp->physical_channel_id = htonl(ocrp->physical_channel_id);
+
+ _this->last_io = get_monosec();
+ pptp_ctrl_output(_this->ctrl, NULL, sizeof(struct pptp_ocrp));
+
+ return 0;
+}
+
+/** Outgoing-Call-Request を受信 */
+static int
+pptp_call_recv_OCRQ(pptp_call *_this, u_char *pkt, int lpkt)
+{
+ char logbuf[512];
+ struct pptp_ocrq *ocrq;
+
+ // サイズ検査
+ if (lpkt < sizeof(struct pptp_ocrq)) {
+ pptp_call_log(_this, LOG_ERR, "Received bad OCRQ: packet too "
+ "short: %d < %d", lpkt, (int)sizeof(struct pptp_ocrq));
+ return 1;
+ }
+ ocrq = (struct pptp_ocrq *)pkt;
+
+ // バイトオーダー
+ ocrq->call_id = ntohs(ocrq->call_id);
+ ocrq->call_serial_number = ntohs(ocrq->call_serial_number);
+ ocrq->recv_winsz = ntohs(ocrq->recv_winsz);
+ ocrq->packet_proccessing_delay =
+ ntohs(ocrq->packet_proccessing_delay);
+ ocrq->phone_number_length = ntohs(ocrq->phone_number_length);
+ ocrq->reservied1 = ntohs(ocrq->reservied1);
+ ocrq->maximum_bps = ntohl(ocrq->maximum_bps);
+ ocrq->minimum_bps = ntohl(ocrq->minimum_bps);
+ ocrq->bearer_type = ntohl(ocrq->bearer_type);
+ ocrq->framing_type = ntohl(ocrq->framing_type);
+
+ _this->peers_call_id = ocrq->call_id;
+ _this->peers_maxwinsz = ocrq->recv_winsz;
+
+ pptp_call_OCRQ_string(ocrq, logbuf, sizeof(logbuf));
+ pptp_call_log(_this, LOG_INFO, "RecvOCRQ %s", logbuf);
+
+ return 0;
+}
+/***********************************************************************
+ * GRE の入出力
+ ***********************************************************************/
+
+/** GREからパケット受信 */
+void
+pptp_call_gre_input(pptp_call *_this, uint32_t seq, uint32_t ack,
+ int input_flags, u_char *pkt, int pktlen)
+{
+ int log_prio;
+ const char *reason;
+
+ PPTP_CALL_ASSERT(_this != NULL);
+
+ log_prio = LOG_INFO;
+
+#ifdef PPTP_CALL_DEBUG
+ if (debuglevel >= 2) {
+ pptp_call_log(_this, LOG_DEBUG,
+ "Received data packet seq=%u(%u-%u) ack=%u(%u-%u)",
+ seq, _this->rcv_nxt,
+ _this->rcv_nxt + _this->peers_maxwinsz - 1,
+ ack, _this->snd_una, _this->snd_nxt);
+ }
+#endif
+ if (_this->state != PPTP_CALL_STATE_ESTABLISHED) {
+ pptp_call_log(_this, LOG_INFO,
+ "Received data packet in illegal state=%s",
+ pptp_call_state_string(_this->state));
+ return;
+ }
+ PPTP_CALL_ASSERT(_this->state == PPTP_CALL_STATE_ESTABLISHED);
+
+ if (input_flags & PPTP_GRE_PKT_ACK_PRESENT) {
+ if (ack + 1 == _this->snd_una) {
+ /* 前回受信した ack から進んでいないだけ */
+ } else if (SEQ_LT(ack, _this->snd_una)) {
+ /* ack が戻った */
+ if (abs(ack - _this->snd_una) < PPTP_CALL_NMAX_INSEQ) {
+ /* ちょっと戻るのはパケット順入れ替わり。*/
+ log_prio = LOG_DEBUG;
+ }
+ reason = "ack out of sequence";
+ goto bad_pkt;
+ // FALL THROUGH
+ } else if (SEQ_GT(ack, _this->snd_nxt)) {
+ reason = "ack for unknown sequence.";
+ goto bad_pkt;
+ } else {
+ ack++;
+ _this->snd_una = ack;
+ }
+ }
+
+ if ((input_flags & PPTP_GRE_PKT_SEQ_PRESENT) == 0)
+ return; /* ack only packet */
+
+ // seq チェック
+ if (SEQ_LT(seq, _this->rcv_nxt)) {
+ /* 順番入れ替わり、届くのが遅れた? */
+ if (abs(seq - _this->rcv_nxt) < PPTP_CALL_NMAX_INSEQ)
+ log_prio = LOG_DEBUG;
+ reason = "out of sequence";
+ goto bad_pkt;
+ } else if (SEQ_GE(seq, _this->rcv_nxt + _this->maxwinsz)){
+ /*
+ * どんなにおかしくても受信せざるをえない。
+ *
+ * FIXME: パケットロスが 4096 発を超えると MPPE の状態が復元
+ * できないが。
+ */
+ pptp_call_log(_this, LOG_INFO,
+ "Received packet caused window overflow. seq=%u(%u-%u), "
+ "may lost %d packets.", seq, _this->rcv_nxt,
+ _this->rcv_nxt + _this->maxwinsz - 1,
+ SEQ_SUB(seq, _this->rcv_nxt));
+ }
+ seq++;
+ /* TODO: パケットロスは ppp->ierrors でカウントすべきか? */
+ _this->rcv_nxt = seq;
+
+ if (SEQ_SUB(seq, _this->rcv_acked) > RUPDIV(_this->winsz, 2)) {
+ /*
+ * Multi-packet acknowledgement.
+ * window size の半分を超えた場合のみ ack する
+ */
+ PPTP_CALL_DBG((_this, LOG_DEBUG, "rcv window size=%u %u %u\n",
+ SEQ_SUB(seq, _this->rcv_acked), seq, _this->rcv_acked));
+ pptp_call_gre_output(_this, 0, 1, NULL, 0);
+ }
+ pptp_call_ppp_input(_this, pkt, pktlen);
+
+ return;
+bad_pkt:
+ pptp_call_log(_this, log_prio,
+ "Received bad data packet: %s: seq=%u(%u-%u) ack=%u(%u-%u)",
+ reason, seq, _this->rcv_nxt, _this->rcv_nxt + _this->maxwinsz - 1,
+ ack, _this->snd_una, _this->snd_nxt);
+}
+
+/**
+ * GREに出力
+ * @param fseq SEQフィールドを含めるかどうか。
+ * @param fack ACKフィールドを含めるかどうか。
+ */
+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ヘッダ*/
+ 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 (_this->ctrl->pptpd->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;
+}
+
+/************************************************************************
+ * npppd の物理層として
+ ************************************************************************/
+#include <net/if_dl.h>
+#include "npppd.h"
+
+static int pptp_call_ppp_output (npppd_ppp *, unsigned char *, int, int);
+static void pptp_call_closed_by_ppp (npppd_ppp *);
+
+/** PPTP の物理層が終了したことを、PPP に確実に伝える。*/
+static void
+pptp_call_notify_down(pptp_call *_this)
+{
+ if (_this->ppp != NULL)
+ ppp_phy_downed(_this->ppp);
+}
+
+
+/** ppp にパケットを入力します。 */
+static void
+pptp_call_ppp_input(pptp_call *_this, u_char *pkt, int pktlen)
+{
+ int rval;
+ npppd_ppp *ppp;
+
+ ppp = _this->ppp;
+ if (ppp == NULL) {
+ pptp_call_log(_this, LOG_WARNING,
+ "Received ppp frame but ppp is not assigned yet");
+ return;
+ }
+ rval = ppp->recv_packet(ppp, pkt, pktlen, 0);
+ if (_this->ppp == NULL) /* ppp is freed */
+ return;
+
+ if (rval != 0) {
+ ppp->ierrors++;
+ } else {
+ ppp->ipackets++;
+ ppp->ibytes += pktlen;
+ }
+}
+
+/** ppp からパケットが出力される時に呼び出されます。 */
+static int
+pptp_call_ppp_output(npppd_ppp *ppp, unsigned char *bytes, int nbytes,
+ int flags)
+{
+ pptp_call *_this;
+
+ _this = ppp->phy_context;
+ PPTP_CALL_ASSERT(_this != NULL);
+
+ if (_this == NULL)
+ return 0;
+
+ if (pptp_call_gre_output(_this, 1, 1, bytes, nbytes) != 0) {
+ ppp->oerrors++;
+ return 1;
+ }
+ ppp->opackets++;
+ ppp->obytes += nbytes;
+
+ return 0;
+}
+
+/** ppp で切断された場合に呼び出されます。 */
+static void
+pptp_call_closed_by_ppp(npppd_ppp *ppp)
+{
+ pptp_call *_this;
+
+ PPTP_CALL_ASSERT(ppp != NULL);
+ PPTP_CALL_ASSERT(ppp->phy_context != NULL);
+
+ _this = ppp->phy_context;
+
+ _this->ppp = NULL; // pptp_call_disconnect より先に。
+
+ if (_this->state != PPTP_CALL_STATE_CLEANUP_WAIT) {
+ pptp_call_disconnect(_this, PPTP_CDN_RESULT_LOST_CARRIER, 0,
+ NULL);
+ }
+ pptp_call_log(_this, LOG_NOTICE, "logtype=PPPUnbind");
+}
+
+/** ppp の bind。*/
+static int
+pptp_call_bind_ppp(pptp_call *_this)
+{
+ npppd_ppp *ppp;
+
+ ppp = NULL;
+ if ((ppp = ppp_create()) == NULL)
+ goto reigai;
+
+ PPTP_CALL_ASSERT(_this->ppp == NULL);
+
+ if (_this->ppp != NULL)
+ return -1;
+
+ _this->ppp = ppp;
+
+ ppp->phy_context = _this;
+ ppp->tunnel_type = PPP_TUNNEL_PPTP;
+ ppp->send_packet = pptp_call_ppp_output;
+ ppp->phy_close = pptp_call_closed_by_ppp;
+
+ strlcpy(ppp->phy_label, _this->ctrl->phy_label, sizeof(ppp->phy_label));
+
+ memcpy(&ppp->phy_info.peer_in, &_this->ctrl->peer,
+ _this->ctrl->peer.ss_len);
+
+ if (ppp_init(npppd_get_npppd(), ppp) != 0)
+ goto reigai;
+
+ pptp_call_log(_this, LOG_NOTICE, "logtype=PPPBind ppp=%d", ppp->id);
+ ppp_start(ppp);
+
+ return 0;
+reigai:
+ pptp_call_log(_this, LOG_ERR, "failed binding ppp");
+
+ if (ppp != NULL)
+ ppp_destroy(ppp);
+ _this->ppp = NULL;
+
+ //pptp_call_disconnect(_this, PPTP_CDN_RCODE_BUSY, 0, NULL);
+ return 1;
+}
+
+/***********************************************************************
+ * その他ユーティリティ関数
+ ***********************************************************************/
+
+/** このインスタンスに基づいたラベルから始まるログを記録します。 */
+static void
+pptp_call_log(pptp_call *_this, int prio, const char *fmt, ...)
+{
+ char logbuf[BUFSIZ];
+ va_list ap;
+
+ va_start(ap, fmt);
+#ifdef PPTPD_MULITPLE
+ snprintf(logbuf, sizeof(logbuf), "pptpd id=%u ctrl=%u call=%u %s",
+ _this->ctrl->pptpd->id, _this->ctrl->id, _this->id, fmt);
+#else
+ snprintf(logbuf, sizeof(logbuf), "pptpd ctrl=%u call=%u %s",
+ _this->ctrl->id, _this->id, fmt);
+#endif
+ vlog_printf(prio, logbuf, ap);
+ va_end(ap);
+}
+
+/** Outgoing-Call-Request パケットを文字列で表現する */
+static void
+pptp_call_OCRQ_string(struct pptp_ocrq *ocrq, char *buf, int lbuf)
+{
+ snprintf(buf, lbuf,
+ "call_id=%u call_serial_number=%u max_bps=%u min_bps=%u bearer=%s "
+ "framing=%s recv_winsz=%u packet_proccessing_delay=%u "
+ "phone_nunmber=%s subaddress=%s",
+ ocrq->call_id, ocrq->call_serial_number, ocrq->maximum_bps,
+ ocrq->minimum_bps, pptp_bearer_string(ocrq->bearer_type),
+ pptp_framing_string(ocrq->framing_type), ocrq->recv_winsz,
+ ocrq->packet_proccessing_delay, ocrq->phone_number,
+ ocrq->subaddress);
+}
+
+/** Outgoing-Call-Reply パケットを文字列で表現する */
+void
+pptp_call_OCRP_string(struct pptp_ocrp *ocrp, char *buf, int lbuf)
+{
+ snprintf(buf, lbuf,
+ "call_id=%u peers_call_id=%u result=%u error=%u cause=%u "
+ "conn_speed=%u recv_winsz=%u packet_proccessing_delay=%u "
+ "physical_channel_id=%u",
+ ocrp->call_id, ocrp->peers_call_id, ocrp->result_code,
+ ocrp->error_code, ocrp->cause_code, ocrp->connect_speed,
+ ocrp->recv_winsz, ocrp->packet_proccessing_delay,
+ ocrp->physical_channel_id);
+}
+
+static char *
+pptp_call_state_string(int state)
+{
+ switch (state) {
+ case PPTP_CALL_STATE_IDLE:
+ return "idle";
+ case PPTP_CALL_STATE_WAIT_CONN:
+ return "wait-conn";
+ case PPTP_CALL_STATE_ESTABLISHED:
+ return "established";
+ case PPTP_CALL_STATE_CLEANUP_WAIT:
+ return "cleanup-wait";
+ }
+ return "unknown";
+}
+