summaryrefslogtreecommitdiff
path: root/usr.sbin/npppd/pptp
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
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')
-rw-r--r--usr.sbin/npppd/pptp/pptp.h423
-rw-r--r--usr.sbin/npppd/pptp/pptp_call.c852
-rw-r--r--usr.sbin/npppd/pptp/pptp_ctrl.c1168
-rw-r--r--usr.sbin/npppd/pptp/pptp_local.h177
-rw-r--r--usr.sbin/npppd/pptp/pptp_subr.c144
-rw-r--r--usr.sbin/npppd/pptp/pptp_subr.h50
-rw-r--r--usr.sbin/npppd/pptp/pptpd.c1151
7 files changed, 3965 insertions, 0 deletions
diff --git a/usr.sbin/npppd/pptp/pptp.h b/usr.sbin/npppd/pptp/pptp.h
new file mode 100644
index 00000000000..d4c7f1e70ac
--- /dev/null
+++ b/usr.sbin/npppd/pptp/pptp.h
@@ -0,0 +1,423 @@
+/*-
+ * 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.
+ */
+#ifndef PPTP_H
+#define PPTP_H 1
+
+/************************************************************************
+ * プロトコル上の定数
+ ************************************************************************/
+#define PPTP_MES_TYPE_CTRL 1
+#define PPTP_MAGIC_COOKIE 0x1a2b3c4d
+#define PPTP_RFC_2637_VERSION 0x0100
+
+#ifndef PPTP_MAX_CALL
+#define PPTP_MAX_CALL 8192
+#endif
+
+
+/** Start-Control-Connection-Request */
+#define PPTP_CTRL_MES_CODE_SCCRQ 1
+
+/** Start-Control-Connection-Reply */
+#define PPTP_CTRL_MES_CODE_SCCRP 2
+
+/** Stop-Control-Connection-Request */
+#define PPTP_CTRL_MES_CODE_StopCCRQ 3
+
+/** Stop-Control-Connection-Reply */
+#define PPTP_CTRL_MES_CODE_StopCCRP 4
+
+/** Echo-Request */
+#define PPTP_CTRL_MES_CODE_ECHO_RQ 5
+
+/** Echo-Reply */
+#define PPTP_CTRL_MES_CODE_ECHO_RP 6
+
+/** Outgoing-Call-Request */
+#define PPTP_CTRL_MES_CODE_OCRQ 7
+
+/** Outgoing-Call-Reply */
+#define PPTP_CTRL_MES_CODE_OCRP 8
+
+/** Incoming-Call-Request */
+#define PPTP_CTRL_MES_CODE_ICRQ 9
+
+/** Incoming-Call-Reply */
+#define PPTP_CTRL_MES_CODE_ICRP 10
+
+/** Incoming-Call-Connected */
+#define PPTP_CTRL_MES_CODE_ICCN 11
+
+/** Call-Clear-Request */
+#define PPTP_CTRL_MES_CODE_CCR 12
+
+/** Call-Disconnect-Notify */
+#define PPTP_CTRL_MES_CODE_CDN 13
+
+/** Set-Link-Info */
+#define PPTP_CTRL_MES_CODE_SLI 15
+
+
+#define PPTP_CTRL_FRAMING_ASYNC 1
+#define PPTP_CTRL_FRAMING_SYNC 2
+
+#define PPTP_CTRL_BEARER_ANALOG 1
+#define PPTP_CTRL_BEARER_DIGITAL 2
+
+/* Start-Control-Connection-Reply の Result Code */
+#define PPTP_SCCRP_RESULT_SUCCESS 1
+#define PPTP_SCCRP_RESULT_GENERIC_ERROR 2
+#define PPTP_SCCRP_RESULT_CHANNEL_EXISTS 3
+#define PPTP_SCCRP_RESULT_NOT_AUTHORIZIZED 4
+#define PPTP_SCCRP_RESULT_BAD_PROTOCOL_VERSION 5
+
+/* General Error Code (RFC 2637 2.16 pp.36) */
+#define PPTP_ERROR_NONE 0
+#define PPTP_ERROR_NOT_CONNECTED 1
+#define PPTP_ERROR_BAD_FORMAT 2
+#define PPTP_ERROR_NO_RESOURCE 3
+#define PPTP_ERROR_BAD_CALL 4
+#define PPTP_ERROR_PAC_ERROR 5
+
+/* Outgoing-Call-Reply の Result Code */
+#define PPTP_OCRP_RESULT_CONNECTED 1
+#define PPTP_OCRP_RESULT_GENERIC_ERROR 2
+#define PPTP_OCRP_RESULT_NO_CARRIER 3
+#define PPTP_OCRP_RESULT_BUSY 4
+#define PPTP_OCRP_RESULT_NO_DIALTONE 5
+#define PPTP_OCRP_RESULT_TIMEOUT 6
+#define PPTP_OCRP_RESULT_DO_NOT_ACCEPT 7
+
+/* Echo-Reply の Result Code */
+#define PPTP_ECHO_RP_RESULT_OK 1
+#define PPTP_ECHO_RP_RESULT_GENERIC_ERROR 2
+
+/* Stop-Control-Connection-Request の Reason */
+#define PPTP_StopCCRQ_REASON_NONE 1
+#define PPTP_StopCCRQ_REASON_STOP_PROTOCOL 2
+#define PPTP_StopCCRQ_REASON_STOP_LOCAL_SHUTDOWN 3
+
+/* Stop-Control-Connection-Response の Result */
+#define PPTP_StopCCRP_RESULT_OK 1
+#define PPTP_StopCCRP_RESULT_GENERIC_ERROR 2
+
+#define PPTP_CDN_RESULT_LOST_CARRIER 1
+#define PPTP_CDN_RESULT_GENRIC_ERROR 2
+#define PPTP_CDN_RESULT_ADMIN_SHUTDOWN 3
+#define PPTP_CDN_RESULT_REQUEST 4
+
+/** デフォルトの待ち受け TCP ポート番号 */
+#define PPTPD_DEFAULT_TCP_PORT 1723
+
+
+#define PPTP_GRE_PROTOCOL_TYPE 0x880b
+#define PPTP_GRE_VERSION 1
+
+/************************************************************************
+ * この実装の定数
+ ************************************************************************/
+/* pptpd ステータス */
+#define PPTPD_STATE_INIT 0
+#define PPTPD_STATE_RUNNING 1
+#define PPTPD_STATE_SHUTTING_DOWN 2
+#define PPTPD_STATE_STOPPED 3
+
+#define PPTPD_CONFIG_BUFSIZ 65535
+
+#define PPTP_BACKLOG 32
+#define PPTP_BUFSIZ 1024
+
+#define PPTPD_DEFAULT_LAYER2_LABEL "PPTP"
+
+/** ステートマシン */
+#define PPTP_CTRL_STATE_IDLE 0
+#define PPTP_CTRL_STATE_WAIT_CTRL_REPLY 1
+#define PPTP_CTRL_STATE_ESTABLISHED 2
+#define PPTP_CTRL_STATE_WAIT_STOP_REPLY 3
+#define PPTP_CTRL_STATE_DISPOSING 4
+
+#ifndef PPTPD_DEFAULT_VENDOR_NAME
+#define PPTPD_DEFAULT_VENDOR_NAME "IIJ"
+#endif
+
+#ifndef PPTP_CALL_DEFAULT_MAXWINSZ
+#define PPTP_CALL_DEFAULT_MAXWINSZ 64
+#endif
+
+#ifndef PPTP_CALL_CONNECT_SPEED
+/** 接続スピード。OCRP で通知する値で、現在は 10Mbps で固定 */
+#define PPTP_CALL_CONNECT_SPEED 10000000
+#endif
+
+#ifndef PPTP_CALL_INITIAL_PPD
+/** OCRP で通知する。初期 Packet Processing Dealy */
+#define PPTP_CALL_INITIAL_PPD 0
+#endif
+
+#ifndef PPTP_CALL_NMAX_INSEQ
+/** シーケンスが戻った場合に何パケットまでを戻ったとみなすか。*/
+#define PPTP_CALL_NMAX_INSEQ 64
+#endif
+
+/** call のステートマシン */
+#define PPTP_CALL_STATE_IDLE 0
+#define PPTP_CALL_STATE_WAIT_CONN 1
+#define PPTP_CALL_STATE_ESTABLISHED 2
+#define PPTP_CALL_STATE_CLEANUP_WAIT 3
+
+/* タイムアウト関連 */
+#define PPTPD_SHUTDOWN_TIMEOUT 5
+
+#define PPTPD_IDLE_TIMEOUT 60
+
+#define PPTP_CALL_CLEANUP_WAIT_TIME 3
+
+#define PPTP_CTRL_DEFAULT_ECHO_INTERVAL 60
+#define PPTP_CTRL_DEFAULT_ECHO_TIMEOUT 60
+#define PPTP_CTRL_StopCCRP_WAIT_TIME 3
+
+/** アドレスは最大何個 bind 可能か。*/
+#ifndef PPTP_NLISTENER
+#define PPTP_NLISTENER 6
+#endif
+
+/** PPTPデーモンが停止したかどうかを返します。 */
+#define pptpd_is_stopped(pptpd) \
+ (((pptpd)->state != PPTPD_STATE_SHUTTING_DOWN && \
+ (pptpd)->state != PPTPD_STATE_RUNNING)? 1 : 0)
+
+/** PPTPデーモンが停止処理中かどうかを返します。 */
+#define pptpd_is_shutting_down(pptpd) \
+ (((pptpd)->state == PPTPD_STATE_SHUTTING_DOWN)? 1 : 0)
+
+/************************************************************************
+ * 型
+ ************************************************************************/
+/** PPTP デーモンの型*/
+struct _pptpd;
+/** PPTP デーモン待ち受け型 */
+typedef struct _pptpd_listener {
+ /** イベントコンテキスト */
+ struct event ev_sock;
+ /** GREイベントコンテキスト */
+ struct event ev_sock_gre;
+ /** PPTPD 自身 */
+ struct _pptpd *self;
+ /** インデックス番号 */
+ uint16_t index;
+ /** 待ち受けソケット */
+ int sock;
+ /** GREソケット */
+ int sock_gre;
+ /** 待ち受けアドレス TCP */
+ struct sockaddr_in bind_sin;
+ /** 待ち受けアドレス GRE */
+ struct sockaddr_in bind_sin_gre;
+ /** 物理層のラベル */
+ char phy_label[16];
+} pptpd_listener;
+
+/** PPTP デーモンの型*/
+typedef struct _pptpd {
+ /** インスタンスの Id */
+ unsigned id;
+ /** 待ち受けリスト */
+ slist listener;
+ /** ステート */
+ int state;
+ /** タイマーイベントコンテキスト */
+ struct event ev_timer;
+ /** PPTP コントロールのリスト */
+ slist ctrl_list;
+
+ /** 設定 */
+ struct properties *config;
+
+ /** 空きコールリスト */
+ slist call_free_list;
+ /** コールId => Call マップ */
+ hash_table *call_id_map;
+ /** 接続を許可するIPv4ネットワーク */
+ struct in_addr_range *ip4_allow;
+
+ /** フラグ */
+ uint32_t
+ initialized:1,
+ ctrl_in_pktdump:1,
+ ctrl_out_pktdump:1,
+ data_in_pktdump:1,
+ data_out_pktdump:1,
+ phy_label_with_ifname:1;
+} pptpd;
+
+#define pptp_ctrl_sock_gre(ctrl) \
+ ((pptpd_listener *)slist_get(&(ctrl)->pptpd->listener,\
+ (ctrl)->listener_index))->sock_gre
+
+/** pptp_ctrl から、リスナーの物理層のラベルを取り出すマクロ */
+#define PPTP_CTRL_LISTENER_LABEL(ctrl) \
+ ((pptpd_listener *)slist_get(&(ctrl)->pptpd->listener,\
+ (ctrl)->listener_index))->phy_label
+
+/** PPTP コントロールの型*/
+typedef struct _pptp_ctrl {
+ /** 親 pptpd */
+ pptpd *pptpd;
+ /** リスナー インデックス番号*/
+ uint16_t listener_index;
+ /** インスタンスの Id */
+ unsigned id;
+ /** ステート */
+ int state;
+ /** 物理層のラベル */
+ char phy_label[16];
+
+ /** ソケット */
+ int sock;
+ /** 先方のアドレス */
+ struct sockaddr_storage peer;
+ /** 当方のアドレス */
+ struct sockaddr_storage our;
+ /** イベントコンテキスト */
+ struct event ev_sock;
+ /** タイマーイベントコンテキスト */
+ struct event ev_timer;
+
+ /** アイドル状態から ECHO 送信までの秒数。0以下は無効。*/
+ int echo_interval;
+ /** ECHO のタイムアウト */
+ int echo_timeout;
+
+ /** 送信可能かどうか。送信バッファが埋まっていないか */
+ int send_ready;
+ /** 受信バッファ */
+ bytebuffer *recv_buf;
+ /** 送信バッファ */
+ bytebuffer *send_buf;
+
+ /** コールのリスト */
+ slist call_list;
+
+ /** 最後にコントロールメッセージを送信した時間 */
+ time_t last_snd_ctrl;
+ /** 最後にコントロールメッセージを受信を送信した時間 */
+ time_t last_rcv_ctrl;
+ /** Echo Request の identifier */
+ uint32_t echo_seq;
+
+ int16_t /** I/O イベント処理中。*/
+ on_io_event:1,
+ reserved:15;
+
+} pptp_ctrl;
+
+/** PPTP コールの型 */
+typedef struct _pptp_call {
+ /** 親コントロール */
+ pptp_ctrl *ctrl;
+ /** コールID*/
+ unsigned id;
+
+ /** 受信インタフェース番号 */
+ int ifidx;
+
+ /** ステート */
+ int state;
+
+ /** 先方のコールID*/
+ unsigned peers_call_id;
+ void *ppp;
+
+ /** 次の確認応答 */
+ uint32_t snd_una;
+ /** 次の送信シーケンス番号 */
+ uint32_t snd_nxt;
+
+ /** 受信シーケンス番号 */
+ uint32_t rcv_nxt;
+ /** 応答確認した受信シーケンス番号 */
+ uint32_t rcv_acked;
+
+ /** カレントウィンドウサイズ */
+ int winsz;
+ /** 最大ウィンドウサイズ */
+ int maxwinsz;
+ /** 先方の最大ウィンドウサイズ*/
+ int peers_maxwinsz;
+
+ time_t last_io;
+
+} pptp_call;
+
+
+/************************************************************************
+ * 関数プロトタイプ
+ ************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int pptpd_init (pptpd *);
+void pptpd_uninit (pptpd *);
+int pptpd_assign_call (pptpd *, pptp_call *);
+void pptpd_release_call (pptpd *, pptp_call *);
+int pptpd_start (pptpd *);
+void pptpd_stop (pptpd *);
+void pptpd_stop_immediatly (pptpd *);
+void pptpd_ctrl_finished_notify(pptpd *, pptp_ctrl *);
+int pptpd_add_listener(pptpd *, int, const char *, struct sockaddr *);
+
+pptp_ctrl *pptp_ctrl_create (void);
+int pptp_ctrl_init (pptp_ctrl *);
+int pptp_ctrl_start (pptp_ctrl *);
+void pptp_ctrl_stop (pptp_ctrl *, int);
+void pptp_ctrl_destroy (pptp_ctrl *);
+void pptp_ctrl_output (pptp_ctrl *, u_char *, int);
+
+pptp_call *pptp_call_create (void);
+int pptp_call_init (pptp_call *, pptp_ctrl *);
+int pptp_call_start (pptp_call *);
+int pptp_call_stop (pptp_call *);
+void pptp_call_destroy (pptp_call *);
+void pptp_call_input (pptp_call *, int, u_char *, int);
+void pptp_call_gre_input (pptp_call *, uint32_t, uint32_t, int, u_char *, int);
+void pptp_call_disconnect(pptp_call *, int, int, const char *);
+int pptpd_reload(pptpd *, struct properties *, const char *, int);
+
+/* config_helper */
+const char *pptpd_config_str (pptpd *, const char *);
+int pptpd_config_int (pptpd *, const char *, int);
+int pptpd_config_str_equal (pptpd *, const char *, const char *, int);
+int pptpd_config_str_equali (pptpd *, const char *, const char *, int);
+const char *pptp_ctrl_config_str (pptp_ctrl *, const char *);
+int pptp_ctrl_config_int (pptp_ctrl *, const char *, int);
+int pptp_ctrl_config_str_equal (pptp_ctrl *, const char *, const char *, int);
+int pptp_ctrl_config_str_equali (pptp_ctrl *, const char *, const char *, int);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
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";
+}
+
diff --git a/usr.sbin/npppd/pptp/pptp_ctrl.c b/usr.sbin/npppd/pptp/pptp_ctrl.c
new file mode 100644
index 00000000000..7d8941bbc11
--- /dev/null
+++ b/usr.sbin/npppd/pptp/pptp_ctrl.c
@@ -0,0 +1,1168 @@
+/*-
+ * 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.
+ */
+/**@file
+ * PPTP(RFC 2637) コントロール接続部の実装。PACのみ。
+ */
+/* $Id: pptp_ctrl.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <event.h>
+
+#include "bytebuf.h"
+#include "debugutil.h"
+#include "hash.h"
+#include "slist.h"
+#include "time_utils.h"
+
+#include "version.h"
+
+#include "pptp.h"
+#include "pptp_local.h"
+#include "pptp_subr.h"
+
+/** 2 秒毎に pptp_ctrl_timeout */
+#define PPTP_CTRL_TIMEOUT_IVAL_SEC 2
+
+#ifdef PPTP_CTRL_DEBUG
+#define PPTP_CTRL_ASSERT(x) ASSERT(x)
+#define PPTP_CTRL_DBG(x) pptp_ctrl_log x
+#else
+#define PPTP_CTRL_ASSERT(x)
+#define PPTP_CTRL_DBG(x)
+#endif
+
+static unsigned pptp_ctrl_seqno = 0;
+
+static void pptp_ctrl_log (pptp_ctrl *, int, const char *, ...) __printflike(3,4);
+static void pptp_ctrl_timeout (int, short, void *);
+static void pptp_ctrl_reset_timeout (pptp_ctrl *);
+static void pptp_ctrl_io_event (int, short, void *);
+static void pptp_ctrl_set_io_event (pptp_ctrl *);
+static int pptp_ctrl_output_flush (pptp_ctrl *);
+static void pptp_ctrl_SCCRx_string (struct pptp_scc *, u_char *, int);
+static int pptp_ctrl_recv_SCCRQ (pptp_ctrl *, u_char *, int);
+static int pptp_ctrl_recv_StopCCRP (pptp_ctrl *, u_char *, int);
+static int pptp_ctrl_send_StopCCRQ (pptp_ctrl *, int);
+static int pptp_ctrl_recv_StopCCRQ (pptp_ctrl *, u_char *, int);
+static int pptp_ctrl_send_StopCCRP (pptp_ctrl *, int, int);
+static int pptp_ctrl_send_SCCRP (pptp_ctrl *, int, int);
+static void pptp_ctrl_send_CDN (pptp_ctrl *, int, int, int, const char *);
+static void pptp_ctrl_process_echo_req (pptp_ctrl *, u_char *, int);
+static int pptp_ctrl_recv_echo_rep (pptp_ctrl *, u_char *, int);
+static void pptp_ctrl_send_echo_req (pptp_ctrl *);
+static int pptp_ctrl_input (pptp_ctrl *, u_char *, int);
+static int pptp_ctrl_call_input (pptp_ctrl *, int, u_char *, int);
+static const char *pptp_ctrl_state_string (int);
+static void pptp_ctrl_fini(pptp_ctrl *);
+
+/************************************************************************
+ * pptp_ctrl インスタンス操作
+ ************************************************************************/
+pptp_ctrl *
+pptp_ctrl_create(void)
+{
+ pptp_ctrl *_this;
+
+ if ((_this = malloc(sizeof(pptp_ctrl))) == NULL)
+ return NULL;
+
+ return _this;
+}
+
+int
+pptp_ctrl_init(pptp_ctrl *_this)
+{
+ time_t curr_time;
+
+ PPTP_CTRL_ASSERT(_this != NULL);
+ curr_time = get_monosec();
+ memset(_this, 0, sizeof(pptp_ctrl));
+ _this->id = pptp_ctrl_seqno++;
+ _this->sock = -1;
+
+ if ((_this->recv_buf = bytebuffer_create(PPTP_BUFSIZ)) == NULL) {
+ pptp_ctrl_log(_this, LOG_ERR, "bytebuffer_create() failed at "
+ "%s(): %m", __func__);
+ goto reigai;
+ }
+ if ((_this->send_buf = bytebuffer_create(PPTP_BUFSIZ)) == NULL) {
+ pptp_ctrl_log(_this, LOG_ERR, "bytebuffer_create() failed at "
+ "%s(): %m", __func__);
+ goto reigai;
+ }
+ _this->last_rcv_ctrl = curr_time;
+ _this->last_snd_ctrl = curr_time;
+ _this->echo_seq = (random() << 16 )| (random() & 0xffff);
+ _this->echo_interval = PPTP_CTRL_DEFAULT_ECHO_INTERVAL;
+ _this->echo_timeout = PPTP_CTRL_DEFAULT_ECHO_TIMEOUT;
+ slist_init(&_this->call_list);
+ evtimer_set(&_this->ev_timer, pptp_ctrl_timeout, _this);
+
+ return 0;
+reigai:
+ return 1;
+}
+
+int
+pptp_ctrl_start(pptp_ctrl *_this)
+{
+ int ival;
+ char hbuf0[NI_MAXHOST], sbuf0[NI_MAXSERV];
+ char hbuf1[NI_MAXHOST], sbuf1[NI_MAXSERV];
+ struct sockaddr_storage sock;
+ socklen_t socklen;
+
+ PPTP_CTRL_ASSERT(_this != NULL);
+ PPTP_CTRL_ASSERT(_this->sock >= 0);
+
+ /* ログ用にアドレス=>文字列変換 */
+
+ strlcpy(hbuf0, "<unknown>", sizeof(hbuf0));
+ strlcpy(sbuf0, "<unknown>", sizeof(sbuf0));
+ strlcpy(hbuf1, "<unknown>", sizeof(hbuf1));
+ strlcpy(sbuf1, "<unknown>", sizeof(sbuf1));
+ if (getnameinfo((struct sockaddr *)&_this->peer, _this->peer.ss_len,
+ hbuf0, sizeof(hbuf0), sbuf0, sizeof(sbuf0),
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+ pptp_ctrl_log(_this, LOG_ERR,
+ "getnameinfo() failed at %s(): %m", __func__);
+ }
+ socklen = sizeof(sock);
+ if (getsockname(_this->sock, (struct sockaddr *)&sock, &socklen) != 0) {
+ pptp_ctrl_log(_this, LOG_ERR,
+ "getsockname() failed at %s(): %m", __func__);
+ goto reigai;
+ }
+ if (getnameinfo((struct sockaddr *)&sock, sock.ss_len, hbuf1,
+ sizeof(hbuf1), sbuf1, sizeof(sbuf1),
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+ pptp_ctrl_log(_this, LOG_ERR,
+ "getnameinfo() failed at %s(): %m", __func__);
+ }
+ pptp_ctrl_log(_this, LOG_INFO, "Starting peer=%s:%s/tcp "
+ "sock=%s:%s/tcp", hbuf0, sbuf0, hbuf1, sbuf1);
+
+ if ((ival = fcntl(_this->sock, F_GETFL, 0)) < 0) {
+ pptp_ctrl_log(_this, LOG_ERR,
+ "fcntl(F_GET_FL) failed at %s(): %m", __func__);
+ goto reigai;
+ } else if (fcntl(_this->sock, F_SETFL, ival | O_NONBLOCK) < 0) {
+ pptp_ctrl_log(_this, LOG_ERR,
+ "fcntl(F_SET_FL) failed at %s(): %m", __func__);
+ goto reigai;
+ }
+ pptp_ctrl_set_io_event(_this);
+ pptp_ctrl_reset_timeout(_this);
+
+ return 0;
+reigai:
+ return 1;
+}
+
+/** タイマー処理 */
+static void
+pptp_ctrl_timeout(int fd, short event, void *ctx)
+{
+ int i;
+ pptp_call *call;
+ pptp_ctrl *_this;
+ time_t last, curr_time;
+
+ _this = ctx;
+ curr_time = get_monosec();
+
+ PPTP_CTRL_DBG((_this, DEBUG_LEVEL_3, "enter %s()", __func__));
+ /* call のクリーンアップ */
+ i = 0;
+ while (i < slist_length(&_this->call_list)) {
+ call = slist_get(&_this->call_list, i);
+ if (call->state == PPTP_CALL_STATE_CLEANUP_WAIT &&
+ curr_time - call->last_io > PPTP_CALL_CLEANUP_WAIT_TIME) {
+ pptp_call_stop(call);
+ pptp_call_destroy(call);
+ slist_remove(&_this->call_list, i);
+ } else
+ i++;
+ }
+
+ /* ステートマシン、Timeout処理 */
+ switch (_this->state) {
+ default:
+ case PPTP_CTRL_STATE_WAIT_CTRL_REPLY:
+ case PPTP_CTRL_STATE_IDLE:
+ if (curr_time - _this->last_rcv_ctrl > PPTPD_IDLE_TIMEOUT) {
+ pptp_ctrl_log(_this, LOG_ERR,
+ "Timeout in state %s",
+ pptp_ctrl_state_string(_this->state));
+ pptp_ctrl_fini(_this);
+ return;
+ }
+ break;
+ case PPTP_CTRL_STATE_ESTABLISHED:
+ last = MAX(_this->last_rcv_ctrl, _this->last_snd_ctrl);
+
+ if (curr_time - _this->last_rcv_ctrl
+ >= _this->echo_interval + _this->echo_timeout) {
+ pptp_ctrl_log(_this, LOG_INFO,
+ "Timeout waiting for echo reply");
+ pptp_ctrl_fini(_this);
+ return;
+ }
+ if (curr_time - last >= _this->echo_interval) {
+ PPTP_CTRL_DBG((_this, LOG_DEBUG, "Echo"));
+ _this->echo_seq++;
+ pptp_ctrl_send_echo_req(_this);
+ }
+ break;
+ case PPTP_CTRL_STATE_WAIT_STOP_REPLY:
+ // お片付け
+ if (curr_time - _this->last_snd_ctrl >
+ PPTP_CTRL_StopCCRP_WAIT_TIME) {
+ pptp_ctrl_log(_this, LOG_WARNING,
+ "Timeout waiting for StopCCRP");
+ pptp_ctrl_fini(_this);
+ return;
+ }
+ break;
+ case PPTP_CTRL_STATE_DISPOSING:
+ pptp_ctrl_fini(_this);
+ return;
+ }
+ pptp_ctrl_reset_timeout(_this);
+}
+
+static void
+pptp_ctrl_reset_timeout(pptp_ctrl *_this)
+{
+ struct timeval tv;
+
+ switch (_this->state) {
+ case PPTP_CTRL_STATE_DISPOSING:
+ tv.tv_sec = 0; /* すぐに call back */
+ tv.tv_usec = 0;
+ break;
+ default:
+ tv.tv_sec = PPTP_CTRL_TIMEOUT_IVAL_SEC;
+ tv.tv_usec = 0;
+ break;
+ }
+ evtimer_add(&_this->ev_timer, &tv);
+}
+
+/**
+ * PPTPコントロールコネクションを終了します。
+ * @result Stop-Control-Connection-Request(StopCCRQ) の result に指定する
+ * 値。0 を指定すると、StopCCRQ を送信せずに、終了します。また、
+ * プロトコル上、StopCCRQ を送信する必要がない場合も送信しません。
+ * @see ::#PPTP_StopCCRQ_REASON_STOP_PROTOCOL
+ * @see ::#PPTP_StopCCRQ_REASON_STOP_LOCAL_SHUTDOWN
+ */
+void
+pptp_ctrl_stop(pptp_ctrl *_this, int result)
+{
+ int i;
+ pptp_call *call;
+
+ switch (_this->state) {
+ case PPTP_CTRL_STATE_WAIT_STOP_REPLY:
+ // 返事待ち。pptp_ctrl_timeout で処理されます。
+ break;
+ case PPTP_CTRL_STATE_ESTABLISHED:
+ if (result != 0) {
+ for (i = 0; i < slist_length(&_this->call_list); i++) {
+ call = slist_get(&_this->call_list, i);
+ pptp_call_disconnect(call,
+ PPTP_CDN_RESULT_ADMIN_SHUTDOWN, 0, NULL);
+ }
+ pptp_ctrl_send_StopCCRQ(_this, result);
+ _this->state = PPTP_CTRL_STATE_WAIT_STOP_REPLY;
+ break;
+ }
+ // FALL THROUGH
+ default:
+ pptp_ctrl_fini(_this);
+ }
+ return;
+}
+
+
+/** PPTP コントロールを終了化します。*/
+static void
+pptp_ctrl_fini(pptp_ctrl *_this)
+{
+ pptp_call *call;
+
+ PPTP_CTRL_ASSERT(_this != NULL);
+
+ if (_this->sock >= 0) {
+ event_del(&_this->ev_sock);
+ close(_this->sock);
+ _this->sock = -1;
+ }
+ for (slist_itr_first(&_this->call_list);
+ slist_itr_has_next(&_this->call_list);) {
+ call = slist_itr_next(&_this->call_list);
+ pptp_call_stop(call);
+ pptp_call_destroy(call);
+ slist_itr_remove(&_this->call_list);
+ }
+
+ if (_this->on_io_event != 0) {
+ /*
+ * I/O イベントハンドラ内での終了化処理は、最後まで行うと
+ * 例外処理が複雑になるので、途中までしか行わす、続きは、
+ * タイマイベントハンドラで行う。
+ */
+ PPTP_CTRL_DBG((_this, LOG_DEBUG, "Disposing"));
+ _this->state = PPTP_CTRL_STATE_DISPOSING;
+ pptp_ctrl_reset_timeout(_this);
+ return;
+ }
+
+ evtimer_del(&_this->ev_timer);
+ slist_fini(&_this->call_list);
+
+ pptp_ctrl_log (_this, LOG_NOTICE, "logtype=Finished");
+
+ /* この後 _this は使用できない */
+ pptpd_ctrl_finished_notify(_this->pptpd, _this);
+}
+
+/* PPTP コントロールコンテキストを解放します。*/
+void
+pptp_ctrl_destroy(pptp_ctrl *_this)
+{
+ if (_this->send_buf != NULL) {
+ bytebuffer_destroy(_this->send_buf);
+ _this->send_buf = NULL;
+ }
+ if (_this->recv_buf != NULL) {
+ bytebuffer_destroy(_this->recv_buf);
+ _this->recv_buf = NULL;
+ }
+ free(_this);
+}
+
+/************************************************************************
+ * ネットワーク I/O
+ ************************************************************************/
+/** I/O イベントディスパッチャ */
+static void
+pptp_ctrl_io_event(int fd, short evmask, void *ctx)
+{
+ int sz, lpkt, hdrlen;
+ u_char *pkt;
+ pptp_ctrl *_this;
+
+ _this = ctx;
+ PPTP_CTRL_ASSERT(_this != NULL);
+
+ _this->on_io_event = 1;
+ if ((evmask & EV_WRITE) != 0) {
+ if (pptp_ctrl_output_flush(_this) != 0 ||
+ _this->state == PPTP_CTRL_STATE_DISPOSING)
+ goto reigai;
+ _this->send_ready = 1;
+ }
+ if ((evmask & EV_READ) != 0) {
+ sz = read(_this->sock, bytebuffer_pointer(_this->recv_buf),
+ bytebuffer_remaining(_this->recv_buf));
+ if (sz <= 0) {
+ if (errno == ECONNRESET || sz == 0) {
+ pptp_ctrl_log(_this, LOG_INFO,
+ "Connection closed by foreign host");
+ pptp_ctrl_fini(_this);
+ goto reigai;
+ } else if (errno != EAGAIN && errno != EINTR) {
+ pptp_ctrl_log(_this, LOG_INFO,
+ "read() failed at %s(): %m", __func__);
+ pptp_ctrl_fini(_this);
+ goto reigai;
+ }
+ }
+ bytebuffer_put(_this->recv_buf, BYTEBUFFER_PUT_DIRECT, sz);
+ bytebuffer_flip(_this->recv_buf);
+
+ for (;;) {
+ pkt = bytebuffer_pointer(_this->recv_buf);
+ lpkt = bytebuffer_remaining(_this->recv_buf);
+ if (pkt == NULL ||
+ lpkt < sizeof(struct pptp_ctrl_header))
+ break; /* read again */
+
+ hdrlen = pkt[0] << 8 | pkt[1];
+ if (lpkt < hdrlen)
+ break; /* read again */
+
+ bytebuffer_get(_this->recv_buf, NULL, hdrlen);
+ if (pptp_ctrl_input(_this, pkt, hdrlen) != 0 ||
+ _this->state == PPTP_CTRL_STATE_DISPOSING) {
+ bytebuffer_compact(_this->recv_buf);
+ goto reigai;
+ }
+ }
+ bytebuffer_compact(_this->recv_buf);
+ }
+ if (pptp_ctrl_output_flush(_this) != 0)
+ goto reigai;
+ pptp_ctrl_set_io_event(_this);
+reigai:
+ _this->on_io_event = 0;
+}
+
+
+/** イベントマスクを設定する */
+static void
+pptp_ctrl_set_io_event(pptp_ctrl *_this)
+{
+ int evmask;
+
+ PPTP_CTRL_ASSERT(_this != NULL);
+ PPTP_CTRL_ASSERT(_this->sock >= 0);
+
+ evmask = 0;
+ if (bytebuffer_remaining(_this->recv_buf) > 128)
+ evmask |= EV_READ;
+ if (_this->send_ready == 0)
+ evmask |= EV_WRITE;
+
+ event_del(&_this->ev_sock);
+ if (evmask != 0) {
+ event_set(&_this->ev_sock, _this->sock, evmask,
+ pptp_ctrl_io_event, _this);
+ event_add(&_this->ev_sock, NULL);
+ }
+}
+
+/**
+ * PPTPコントロールパケットを出力します。
+ * @param pkt パケットの領域へのポインタ。
+ * bytebuffer を使って、_this->send_buf に追記している場合には、
+ * NULL を指定します。
+ * @param lpkt パケットの長さ。
+ */
+void
+pptp_ctrl_output(pptp_ctrl *_this, u_char *pkt, int lpkt)
+{
+ PPTP_CTRL_ASSERT(_this != NULL);
+ PPTP_CTRL_ASSERT(lpkt > 0);
+
+ bytebuffer_put(_this->send_buf, pkt, lpkt);
+ /* 実際には書くのは後 */
+
+ if (_this->on_io_event != 0) {
+ /* pptp_ctrl_io_event で pptp_ctrl_output_flush を呼び出し */
+ } else {
+ /*
+ * I/O イベントハンドラからの呼出しではない場合は、
+ * pptp_ctrl_output_flush() を呼び出す必要があるが flush =>
+ * write 失敗 => finalize となると、この関数呼び出し側に例外
+ * 処理を実装し煩雑になるので、write ready イベントで発行し
+ * てもらって、そこで flush。
+ */
+ _this->send_ready = 0;
+ pptp_ctrl_set_io_event(_this);
+ }
+
+ return;
+}
+
+/** Stop-Control-Connection-Request の送信 */
+
+/** 実際にパケット送信 */
+static int
+pptp_ctrl_output_flush(pptp_ctrl *_this)
+{
+ int sz;
+ time_t curr_time;
+
+ curr_time = get_monosec();
+
+ if (bytebuffer_position(_this->send_buf) <= 0)
+ return 0; // nothing to write
+ if (_this->send_ready == 0) {
+ pptp_ctrl_set_io_event(_this);
+ return 0; // not ready to write
+ }
+
+ bytebuffer_flip(_this->send_buf);
+
+ if (_this->pptpd->ctrl_out_pktdump != 0) {
+ pptp_ctrl_log(_this, LOG_DEBUG, "PPTP Control output packet");
+ show_hd(debug_get_debugfp(),
+ bytebuffer_pointer(_this->send_buf),
+ bytebuffer_remaining(_this->send_buf));
+ }
+ if ((sz = write(_this->sock, bytebuffer_pointer(_this->send_buf),
+ bytebuffer_remaining(_this->send_buf))) < 0) {
+ pptp_ctrl_log(_this, LOG_ERR, "write to socket failed: %m");
+ pptp_ctrl_fini(_this);
+
+ return 1;
+ }
+ _this->last_snd_ctrl = curr_time;
+ bytebuffer_get(_this->send_buf, NULL, sz);
+ bytebuffer_compact(_this->send_buf);
+ _this->send_ready = 0;
+
+ return 0;
+}
+
+/** Start-Control-Connection-Request、-Reply パケットを文字列で表現する */
+static void
+pptp_ctrl_SCCRx_string(struct pptp_scc *scc, u_char *buf, int lbuf)
+{
+ char buf1[128], buf2[128], buf3[128];
+
+ // 64バイトギリギリまで入っている場合があるので
+ strlcpy(buf1, scc->host_name, sizeof(buf1));
+ strlcpy(buf2, scc->vendor_string, sizeof(buf2));
+
+ if (scc->result_code != 0)
+ snprintf(buf3, sizeof(buf3), "result=%d error=%d ",
+ scc->result_code, scc->error_code);
+ else
+ buf3[0] = '\0';
+
+ snprintf(buf, lbuf,
+ "protocol_version=%d.%d %sframing=%s bearer=%s max_channels=%d "
+ "firmware_revision=%d(0x%04x) host_name=\"%s\" "
+ "vendor_string=\"%s\"",
+ scc->protocol_version >> 8, scc->protocol_version & 0xff, buf3,
+ pptp_framing_string(scc->framing_caps),
+ pptp_bearer_string(scc->bearer_caps), scc->max_channels,
+ scc->firmware_revision, scc->firmware_revision, buf1, buf2);
+}
+
+/** Start-Control-Connection-Request を受信 */
+static int
+pptp_ctrl_recv_SCCRQ(pptp_ctrl *_this, u_char *pkt, int lpkt)
+{
+ char logbuf[512];
+ struct pptp_scc *scc;
+
+ // サイズ検査
+ if (lpkt < sizeof(struct pptp_scc)) {
+ pptp_ctrl_log(_this, LOG_ERR, "Received bad SCCRQ: packet too "
+ "short: %d < %d", lpkt, (int)sizeof(struct pptp_scc));
+ return 1;
+ }
+ scc = (struct pptp_scc *)pkt;
+
+ // バイトオーダー
+ scc->protocol_version = ntohs(scc->protocol_version);
+ scc->framing_caps = htonl(scc->framing_caps);
+ scc->bearer_caps = htonl(scc->bearer_caps);
+ scc->max_channels = htons(scc->max_channels);
+ scc->firmware_revision = htons(scc->firmware_revision);
+
+ // プロトコルバージョン
+ if (scc->protocol_version != PPTP_RFC_2637_VERSION) {
+ pptp_ctrl_log(_this, LOG_ERR, "Received bad SCCRQ: "
+ "unknown protocol version %d", scc->protocol_version);
+ return 1;
+ }
+
+ pptp_ctrl_SCCRx_string(scc, logbuf, sizeof(logbuf));
+ pptp_ctrl_log(_this, LOG_INFO, "RecvSCCRQ %s", logbuf);
+
+ return 0;
+}
+
+/** Stop-Control-Connection-Reply の受信 */
+static int
+pptp_ctrl_recv_StopCCRP(pptp_ctrl *_this, u_char *pkt, int lpkt)
+{
+ struct pptp_stop_ccrp *stop_ccrp;
+
+ if (lpkt < sizeof(struct pptp_stop_ccrp)) {
+ pptp_ctrl_log(_this, LOG_ERR, "Received bad StopCCRP: packet "
+ "too short: %d < %d", lpkt,
+ (int)sizeof(struct pptp_stop_ccrp));
+ return 1;
+ }
+ stop_ccrp = (struct pptp_stop_ccrp *)pkt;
+
+ pptp_ctrl_log(_this, LOG_INFO, "RecvStopCCRP reason=%s(%u)",
+ pptp_StopCCRP_result_string(stop_ccrp->result), stop_ccrp->result);
+
+ return 0;
+}
+
+static int
+pptp_ctrl_send_StopCCRQ(pptp_ctrl *_this, int reason)
+{
+ int lpkt;
+ struct pptp_stop_ccrq *stop_ccrq;
+
+ stop_ccrq = bytebuffer_pointer(_this->send_buf);
+ lpkt = bytebuffer_remaining(_this->send_buf);
+ if (lpkt < sizeof(struct pptp_stop_ccrq)) {
+ pptp_ctrl_log(_this, LOG_ERR,
+ "SendCCRP failed: No buffer space available");
+ return -1;
+ }
+ memset(stop_ccrq, 0, sizeof(struct pptp_stop_ccrq));
+
+ pptp_init_header(&stop_ccrq->header, sizeof(struct pptp_stop_ccrq),
+ PPTP_CTRL_MES_CODE_StopCCRQ);
+
+ stop_ccrq->reason = reason;
+
+ pptp_ctrl_log(_this, LOG_INFO, "SendStopCCRQ reason=%s(%u)",
+ pptp_StopCCRQ_reason_string(stop_ccrq->reason), stop_ccrq->reason);
+
+ pptp_ctrl_output(_this, NULL, sizeof(struct pptp_stop_ccrq));
+
+ return 0;
+}
+
+/** Stop-Control-Connection-Request を受信 */
+static int
+pptp_ctrl_recv_StopCCRQ(pptp_ctrl *_this, u_char *pkt, int lpkt)
+{
+ struct pptp_stop_ccrq *stop_ccrq;
+
+ if (lpkt < sizeof(struct pptp_stop_ccrq)) {
+ pptp_ctrl_log(_this, LOG_ERR, "Received bad StopCCRQ: packet "
+ "too short: %d < %d", lpkt,
+ (int)sizeof(struct pptp_stop_ccrq));
+ return 1;
+ }
+ stop_ccrq = (struct pptp_stop_ccrq *)pkt;
+
+ pptp_ctrl_log(_this, LOG_INFO, "RecvStopCCRQ reason=%s(%u)",
+ pptp_StopCCRQ_reason_string(stop_ccrq->reason), stop_ccrq->reason);
+
+ return 0;
+}
+
+
+
+/** Stop-Control-Connection-Reply を送信 */
+static int
+pptp_ctrl_send_StopCCRP(pptp_ctrl *_this, int result, int error)
+{
+ int lpkt;
+ struct pptp_stop_ccrp *stop_ccrp;
+
+ stop_ccrp = bytebuffer_pointer(_this->send_buf);
+
+ lpkt = bytebuffer_remaining(_this->send_buf);
+ if (lpkt < sizeof(struct pptp_stop_ccrp)) {
+ pptp_ctrl_log(_this, LOG_ERR,
+ "SendCCRQ failed: No buffer space available");
+ return -1;
+ }
+ memset(stop_ccrp, 0, sizeof(struct pptp_stop_ccrp));
+
+ pptp_init_header(&stop_ccrp->header, sizeof(struct pptp_stop_ccrp),
+ PPTP_CTRL_MES_CODE_StopCCRP);
+
+ stop_ccrp->result = result;
+ stop_ccrp->error = error;
+
+ pptp_ctrl_log(_this, LOG_INFO,
+ "SendStopCCRP result=%s(%u) error=%s(%u)",
+ pptp_StopCCRP_result_string(stop_ccrp->result), stop_ccrp->result,
+ pptp_general_error_string(stop_ccrp->error), stop_ccrp->error);
+
+ pptp_ctrl_output(_this, NULL, sizeof(struct pptp_stop_ccrp));
+
+ return 0;
+}
+
+/** Start-Control-Connection-Reply を送信 */
+static int
+pptp_ctrl_send_SCCRP(pptp_ctrl *_this, int result, int error)
+{
+ int lpkt;
+ struct pptp_scc *scc;
+ char logbuf[512];
+ const char *val;
+
+ scc = bytebuffer_pointer(_this->send_buf);
+ lpkt = bytebuffer_remaining(_this->send_buf);
+ if (lpkt < sizeof(struct pptp_scc)) {
+ pptp_ctrl_log(_this, LOG_ERR,
+ "SendSCCRP failed: No buffer space available");
+ return -1;
+ }
+ memset(scc, 0, sizeof(struct pptp_scc));
+
+ pptp_init_header(&scc->header, sizeof(struct pptp_scc),
+ PPTP_CTRL_MES_CODE_SCCRP);
+
+ scc->protocol_version = PPTP_RFC_2637_VERSION;
+ scc->result_code = result;
+ scc->error_code = error;
+ // 同期フレームしかサポートせず。
+ //scc->framing_caps = PPTP_CTRL_FRAMING_ASYNC;
+ scc->framing_caps = PPTP_CTRL_FRAMING_SYNC;
+ scc->bearer_caps = PPTP_CTRL_BEARER_DIGITAL;
+
+ scc->max_channels = 4; // XX 設定?
+ scc->firmware_revision = MAJOR_VERSION << 8 | MINOR_VERSION;
+
+ // 63文字で切れても気にしない
+
+ // ホスト名
+ if ((val = pptp_ctrl_config_str(_this, "pptp.host_name")) == NULL)
+ val = "";
+ strlcpy(scc->host_name, val, sizeof(scc->host_name));
+
+ // ベンダ名
+ if ((val = pptp_ctrl_config_str(_this, "pptp.vendor_name")) == NULL)
+ val = PPTPD_DEFAULT_VENDOR_NAME;
+
+ strlcpy(scc->vendor_string, val, sizeof(scc->vendor_string));
+
+ pptp_ctrl_SCCRx_string(scc, logbuf, sizeof(logbuf));
+ pptp_ctrl_log(_this, LOG_INFO, "SendSCCRP %s", logbuf);
+
+ scc->protocol_version = htons(scc->protocol_version);
+ scc->framing_caps = htonl(scc->framing_caps);
+ scc->bearer_caps = htonl(scc->bearer_caps);
+ scc->max_channels = htons(scc->max_channels);
+ scc->firmware_revision = htons(scc->firmware_revision);
+
+ pptp_ctrl_output(_this, NULL, sizeof(struct pptp_scc));
+
+ return 0;
+}
+
+/** ECHO の受信 => 返信 */
+static void
+pptp_ctrl_process_echo_req(pptp_ctrl *_this, u_char *pkt, int lpkt)
+{
+ struct pptp_echo_rq *echo_rq;
+ struct pptp_echo_rp *echo_rp;
+
+ if (lpkt < sizeof(struct pptp_echo_rq)) {
+ pptp_ctrl_log(_this, LOG_ERR, "Received bad EchoReq: packet "
+ "too short: %d < %d", lpkt,
+ (int)sizeof(struct pptp_echo_rq));
+ return;
+ }
+ echo_rq = (struct pptp_echo_rq *)pkt;
+
+ PPTP_CTRL_DBG((_this, LOG_DEBUG, "RecvEchoReq"));
+
+ echo_rp = bytebuffer_pointer(_this->send_buf);
+ lpkt = bytebuffer_remaining(_this->send_buf);
+ if (echo_rp == NULL || lpkt < sizeof(struct pptp_echo_rp)) {
+ pptp_ctrl_log(_this, LOG_ERR,
+ "Failed to send EchoReq: No buffer space available");
+ return;
+ }
+ memset(echo_rp, 0, sizeof(struct pptp_echo_rp));
+
+ pptp_init_header(&echo_rp->header, sizeof(struct pptp_echo_rp),
+ PPTP_CTRL_MES_CODE_ECHO_RP);
+
+ echo_rp->identifier = echo_rq->identifier;
+ echo_rp->result_code = PPTP_ECHO_RP_RESULT_OK;
+ echo_rp->error_code = PPTP_ERROR_NONE;
+ echo_rp->reserved1 = htons(0);
+
+ pptp_ctrl_output(_this, NULL, sizeof(struct pptp_echo_rp));
+ PPTP_CTRL_DBG((_this, LOG_DEBUG, "SendEchoReply"));
+}
+
+/** Echo-Reply の受信 */
+static int
+pptp_ctrl_recv_echo_rep(pptp_ctrl *_this, u_char *pkt, int lpkt)
+{
+ struct pptp_echo_rp *echo_rp;
+
+ if (lpkt < sizeof(struct pptp_echo_rp)) {
+ pptp_ctrl_log(_this, LOG_ERR, "Received bad EchoReq: packet "
+ "too short: %d < %d", lpkt,
+ (int)sizeof(struct pptp_echo_rp));
+ return 1;
+ }
+ echo_rp = (struct pptp_echo_rp *)pkt;
+
+ if (echo_rp->result_code != PPTP_ECHO_RP_RESULT_OK) {
+ pptp_ctrl_log(_this, LOG_ERR, "Received negative EchoReply: %s",
+ pptp_general_error_string(echo_rp->error_code));
+ return 1;
+ }
+ if (_this->echo_seq != ntohl(echo_rp->identifier)) {
+ pptp_ctrl_log(_this, LOG_ERR, "Received bad EchoReply: "
+ "Identifier mismatch sent=%u recv=%u",
+ _this->echo_seq , ntohl(echo_rp->identifier));
+ return 1;
+ }
+ PPTP_CTRL_DBG((_this, LOG_DEBUG, "RecvEchoReply"));
+ return 0;
+}
+
+/** Echo-Request の送信 */
+static void
+pptp_ctrl_send_echo_req(pptp_ctrl *_this)
+{
+ int lpkt;
+ struct pptp_echo_rq *echo_rq;
+
+ echo_rq = (struct pptp_echo_rq *)bytebuffer_pointer(_this->send_buf);
+ lpkt = bytebuffer_remaining(_this->send_buf);
+ if (echo_rq == NULL || lpkt < sizeof(struct pptp_echo_rq)) {
+ pptp_ctrl_log(_this, LOG_ERR,
+ "SendEchoReq failed: No buffer space available");
+ return;
+ }
+ memset(echo_rq, 0, sizeof(struct pptp_echo_rq));
+
+ pptp_init_header(&echo_rq->header, sizeof(struct pptp_echo_rq),
+ PPTP_CTRL_MES_CODE_ECHO_RQ);
+
+ echo_rq->identifier = htonl(_this->echo_seq);
+
+ pptp_ctrl_output(_this, NULL, sizeof(struct pptp_echo_rq));
+ PPTP_CTRL_DBG((_this, LOG_DEBUG, "SendEchoReq"));
+}
+
+/* Call-Disconnect-Notify の送信 */
+static void
+pptp_ctrl_send_CDN(pptp_ctrl *_this, int result, int error, int cause,
+ const char *statistics)
+{
+ int lpkt;
+ struct pptp_cdn *cdn;
+
+ cdn = bytebuffer_pointer(_this->send_buf);
+ lpkt = bytebuffer_remaining(_this->send_buf);
+ if (lpkt < sizeof(struct pptp_cdn)) {
+ pptp_ctrl_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));
+
+ cdn->call_id = htons(cdn->call_id);
+ cdn->cause_code = htons(cdn->cause_code);
+
+ pptp_ctrl_output(_this, NULL, sizeof(struct pptp_cdn));
+}
+
+/** コントロールパケット受信 */
+static int
+pptp_ctrl_input(pptp_ctrl *_this, u_char *pkt, int lpkt)
+{
+ char errmes[256];
+ time_t curr_time;
+ struct pptp_ctrl_header *hdr;
+
+ PPTP_CTRL_ASSERT(lpkt >= sizeof(struct pptp_ctrl_header));
+
+ curr_time = get_monosec();
+ hdr = (struct pptp_ctrl_header *)pkt;
+
+ // バイトオーダー
+ hdr->length = ntohs(hdr->length);
+ hdr->pptp_message_type = ntohs(hdr->pptp_message_type);
+ hdr->magic_cookie = ntohl(hdr->magic_cookie);
+ hdr->control_message_type = ntohs(hdr->control_message_type);
+ hdr->reserved0 = ntohs(hdr->reserved0);
+
+ // 長さ検査
+ PPTP_CTRL_ASSERT(hdr->length <= lpkt);
+
+ _this->last_rcv_ctrl = curr_time;
+
+ if (_this->pptpd->ctrl_in_pktdump != 0) {
+ pptp_ctrl_log(_this, LOG_DEBUG,
+ "PPTP Control input packet dump: mestype=%s(%d)",
+ pptp_ctrl_mes_type_string(hdr->control_message_type),
+ hdr->control_message_type);
+ show_hd(debug_get_debugfp(), pkt, lpkt);
+ }
+
+ /* パケット検査 */
+ // メッセージタイプ
+ if (hdr->pptp_message_type != PPTP_MES_TYPE_CTRL) {
+ snprintf(errmes, sizeof(errmes), "unknown message type %d",
+ hdr->pptp_message_type);
+ goto bad_packet;
+ }
+ // マジッククッキー
+ if (hdr->magic_cookie != PPTP_MAGIC_COOKIE) {
+ snprintf(errmes, sizeof(errmes), "wrong magic %08x != %08x",
+ hdr->magic_cookie, PPTP_MAGIC_COOKIE);
+ goto bad_packet;
+ }
+
+ // ECHO Reply は別処理。ステートが交錯する可能性があるので。*/
+ switch (hdr->control_message_type) {
+ case PPTP_CTRL_MES_CODE_ECHO_RP:
+ if (pptp_ctrl_recv_echo_rep(_this, pkt, lpkt) != 0) {
+ pptp_ctrl_fini(_this);
+ return 1;
+ }
+ return 0;
+ }
+ /*
+ * ステートマシン
+ * - 正常に処理が終わったら、return する。
+ */
+ switch (_this->state) {
+ case PPTP_CTRL_STATE_IDLE:
+ switch (hdr->control_message_type) {
+ case PPTP_CTRL_MES_CODE_SCCRQ:
+ if (pptp_ctrl_recv_SCCRQ(_this, pkt, lpkt) != 0) {
+ return 0;
+ }
+ if (pptp_ctrl_send_SCCRP(_this,
+ PPTP_SCCRP_RESULT_SUCCESS, PPTP_ERROR_NONE) != 0) {
+ return 0;
+ }
+ _this->state = PPTP_CTRL_STATE_ESTABLISHED;
+ return 0;
+ default:
+ break;
+ }
+ break;
+ case PPTP_CTRL_STATE_ESTABLISHED:
+ switch (hdr->control_message_type) {
+ case PPTP_CTRL_MES_CODE_ECHO_RQ:
+ pptp_ctrl_process_echo_req(_this, pkt, lpkt);
+ return 0;
+ //コール関連パケットは、pptp_call_input にディスパッチ
+ case PPTP_CTRL_MES_CODE_SLI:
+ case PPTP_CTRL_MES_CODE_ICRQ:
+ case PPTP_CTRL_MES_CODE_ICRP:
+ case PPTP_CTRL_MES_CODE_OCRQ:
+ case PPTP_CTRL_MES_CODE_OCRP:
+ case PPTP_CTRL_MES_CODE_ICCN:
+ case PPTP_CTRL_MES_CODE_CDN:
+ case PPTP_CTRL_MES_CODE_CCR:
+ return pptp_ctrl_call_input(_this,
+ hdr->control_message_type, pkt, lpkt);
+ case PPTP_CTRL_MES_CODE_StopCCRQ:
+ if (pptp_ctrl_recv_StopCCRQ(_this, pkt, lpkt) != 0) {
+ pptp_ctrl_stop(_this,
+ PPTP_StopCCRQ_REASON_STOP_PROTOCOL);
+ return 0;
+ }
+ if (pptp_ctrl_send_StopCCRP(_this,
+ PPTP_StopCCRP_RESULT_OK, PPTP_ERROR_NONE)!= 0) {
+ return 0;
+ }
+ pptp_ctrl_fini(_this);
+ return 1;
+ default:
+ break;
+ }
+ case PPTP_CTRL_STATE_WAIT_STOP_REPLY:
+ switch (hdr->control_message_type) {
+ case PPTP_CTRL_MES_CODE_StopCCRP:
+ pptp_ctrl_recv_StopCCRP(_this, pkt, lpkt);
+ pptp_ctrl_fini(_this);
+ return 1;
+ }
+ break;
+ case PPTP_CTRL_STATE_WAIT_CTRL_REPLY:
+ // PAC の実装だけなので
+ break;
+ }
+ pptp_ctrl_log(_this, LOG_WARNING,
+ "Unhandled control message type=%s(%d)",
+ pptp_ctrl_mes_type_string(hdr->control_message_type),
+ hdr->control_message_type);
+ return 0;
+
+bad_packet:
+ pptp_ctrl_log(_this, LOG_ERR, "Received bad packet: %s", errmes);
+ pptp_ctrl_stop(_this, PPTP_StopCCRQ_REASON_STOP_PROTOCOL);
+
+ return 0;
+}
+
+/** PPTP Call 関連のメッセージを受信 */
+static int
+pptp_ctrl_call_input(pptp_ctrl *_this, int mes_type, u_char *pkt, int lpkt)
+{
+ int i, call_id, lpkt0;
+ pptp_call *call;
+ const char *reason;
+ u_char *pkt0;
+
+ pkt0 = pkt;
+ lpkt0 = lpkt;
+ call_id = -1;
+ pkt += sizeof(struct pptp_ctrl_header);
+ lpkt -= sizeof(struct pptp_ctrl_header);
+ reason = "(no reason)";
+
+ // callId
+ if (lpkt < 4) {
+ reason = "received packet is too short";
+ goto badpacket;
+ }
+ call = NULL;
+ call_id = ntohs(*(uint16_t *)pkt);
+
+ switch (mes_type) {
+ case PPTP_CTRL_MES_CODE_SLI: /* PNS <=> PAC */
+ /* SLI だけは、こちらの Call-ID が入っている */
+ for (i = 0; i < slist_length(&_this->call_list); i++) {
+ call = slist_get(&_this->call_list, i);
+ if (call->id == call_id)
+ break;
+ call = NULL;
+ }
+ if (call == NULL) {
+ reason = "Call Id is not associated by this control";
+ goto badpacket;
+ }
+ goto call_searched;
+ case PPTP_CTRL_MES_CODE_ICRP: /* PNS => PAC */
+ /*
+ * ICRQ は投げないのでこのメッセージは受信しないが、いちおう
+ * pptp_call.c 側で処理させる
+ */
+ // FALL THROUGH
+ case PPTP_CTRL_MES_CODE_OCRQ: /* PNS => PAC */
+ case PPTP_CTRL_MES_CODE_CCR: /* PNS => PAC */
+ // リニアサーチでよい。
+ for (i = 0; i < slist_length(&_this->call_list); i++) {
+ call = slist_get(&_this->call_list, i);
+ if (call->peers_call_id == call_id)
+ break;
+ call = NULL;
+ }
+ if (call == NULL && mes_type == PPTP_CTRL_MES_CODE_CCR) {
+ pptp_ctrl_send_CDN(_this, PPTP_CDN_RESULT_GENRIC_ERROR,
+ PPTP_ERROR_BAD_CALL, 0, NULL);
+ goto call_searched;
+ }
+ if (mes_type == PPTP_CTRL_MES_CODE_OCRQ) {
+ // 新しい Call を作成
+ if (call != NULL) {
+ pptp_call_input(call, mes_type, pkt0, lpkt0);
+ return 0;
+ }
+ if ((call = pptp_call_create()) == NULL) {
+ pptp_ctrl_log(_this, LOG_ERR,
+ "pptp_call_create() failed: %m");
+ goto reigai;
+ }
+ if (pptp_call_init(call, _this) != 0) {
+ pptp_ctrl_log(_this, LOG_ERR,
+ "pptp_call_init() failed: %m");
+ pptp_call_destroy(call);
+ goto reigai;
+ }
+ slist_add(&_this->call_list, call);
+ }
+call_searched:
+ if (call == NULL) {
+ reason = "Call Id is not associated by this control";
+ goto badpacket;
+ }
+ pptp_call_input(call, mes_type, pkt0, lpkt0);
+ return 0;
+ case PPTP_CTRL_MES_CODE_OCRP: /* PAC => PNS */
+ case PPTP_CTRL_MES_CODE_ICRQ: /* PAC => PNS */
+ case PPTP_CTRL_MES_CODE_ICCN: /* PAC => PNS */
+ case PPTP_CTRL_MES_CODE_CDN: /* PAC => PNS */
+ /* 以上 PNS 用なので、受信しない */
+ default:
+ break;
+ }
+ reason = "Message type is unexpected.";
+ // FALL THROUGH
+badpacket:
+ pptp_ctrl_log(_this, LOG_INFO,
+ "Received a bad %s(%d) call_id=%d: %s",
+ pptp_ctrl_mes_type_string(mes_type), mes_type, call_id, reason);
+ return 0;
+reigai:
+ pptp_ctrl_stop(_this, PPTP_StopCCRQ_REASON_STOP_PROTOCOL);
+ return 0;
+}
+
+
+/************************************************************************
+ * 雑多な関数
+ ************************************************************************/
+
+/** このインスタンスに基づいたラベルから始まるログを記録します。 */
+static void
+pptp_ctrl_log(pptp_ctrl *_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 %s",
+ _this->pptpd->id, _this->id, fmt);
+#else
+ snprintf(logbuf, sizeof(logbuf), "pptpd ctrl=%u %s", _this->id, fmt);
+#endif
+ vlog_printf(prio, logbuf, ap);
+ va_end(ap);
+}
+
+static const char *
+pptp_ctrl_state_string(int state)
+{
+ switch (state) {
+ case PPTP_CTRL_STATE_IDLE:
+ return "idle";
+ case PPTP_CTRL_STATE_WAIT_CTRL_REPLY:
+ return "wait-ctrl-reply";
+ case PPTP_CTRL_STATE_ESTABLISHED:
+ return "established";
+ case PPTP_CTRL_STATE_WAIT_STOP_REPLY:
+ return "wait-stop-reply";
+ }
+ return "unknown";
+}
diff --git a/usr.sbin/npppd/pptp/pptp_local.h b/usr.sbin/npppd/pptp/pptp_local.h
new file mode 100644
index 00000000000..1cd6e3755dd
--- /dev/null
+++ b/usr.sbin/npppd/pptp/pptp_local.h
@@ -0,0 +1,177 @@
+/*-
+ * 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.
+ */
+#ifndef PPTP_LOCAL_H
+#define PPTP_LOCAL_H 1
+
+#ifndef countof
+#define countof(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+struct pptp_gre_header {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t recur:3,
+ s:1,
+ S:1,
+ K:1,
+ R:1,
+ C:1;
+ uint8_t ver:3,
+ flags:4,
+ A:1;
+#else
+ uint8_t C:1,
+ R:1,
+ K:1,
+ S:1,
+ s:1,
+ recur:3;
+ uint8_t A:1,
+ flags:4,
+ ver:3;
+#endif
+ uint16_t protocol_type;
+ uint16_t payload_length;
+ uint16_t call_id;
+} __attribute__((__packed__));
+
+
+/** PPTPコントロールパケットの(ヘッダ)共通部分 */
+struct pptp_ctrl_header {
+ uint16_t length;
+ uint16_t pptp_message_type;
+ uint32_t magic_cookie;
+ uint16_t control_message_type;
+ uint16_t reserved0;
+} __attribute__((__packed__));
+
+/** Start-Control-Connection-{Request,Reply} パケット */
+struct pptp_scc {
+ struct pptp_ctrl_header header;
+ uint16_t protocol_version;
+ uint8_t result_code;
+ uint8_t error_code;
+ uint32_t framing_caps;
+ uint32_t bearer_caps;
+ uint16_t max_channels;
+ uint16_t firmware_revision;
+ u_char host_name[64];
+ u_char vendor_string[64];
+} __attribute__((__packed__));
+
+/** Outgoing-Call-Request パケット */
+struct pptp_ocrq {
+ struct pptp_ctrl_header header;
+ uint16_t call_id;
+ uint16_t call_serial_number;
+ uint32_t maximum_bps;
+ uint32_t minimum_bps;
+ uint32_t bearer_type;
+ uint32_t framing_type;
+ uint16_t recv_winsz;
+ uint16_t packet_proccessing_delay;
+ uint16_t phone_number_length;
+ uint16_t reservied1;
+ u_char phone_number[64];
+ u_char subaddress[64];
+} __attribute__((__packed__));
+
+/** Outgoing-Call-Reply パケット */
+struct pptp_ocrp {
+ struct pptp_ctrl_header header;
+ uint16_t call_id;
+ uint16_t peers_call_id;
+ uint8_t result_code;
+ uint8_t error_code;
+ uint16_t cause_code;
+ uint32_t connect_speed;
+ uint16_t recv_winsz;
+ uint16_t packet_proccessing_delay;
+ uint32_t physical_channel_id;
+} __attribute__((__packed__));
+
+/** Echo-Request パケット */
+struct pptp_echo_rq {
+ struct pptp_ctrl_header header;
+ uint32_t identifier;
+} __attribute__((__packed__));
+
+/** Echo-Reply パケット */
+struct pptp_echo_rp {
+ struct pptp_ctrl_header header;
+ uint32_t identifier;
+ uint8_t result_code;
+ uint8_t error_code;
+ uint16_t reserved1;
+} __attribute__((__packed__));
+
+/** Set-Link-Info パケット */
+struct pptp_sli {
+ struct pptp_ctrl_header header;
+ uint16_t peers_call_id;
+ uint16_t reserved;
+ uint32_t send_accm;
+ uint32_t recv_accm;
+} __attribute__((__packed__));
+
+struct pptp_stop_ccrq {
+ struct pptp_ctrl_header header;
+ uint8_t reason;
+ uint8_t reserved1;
+ uint16_t reserved2;
+} __attribute__((__packed__));
+
+struct pptp_stop_ccrp {
+ struct pptp_ctrl_header header;
+ uint8_t result;
+ uint8_t error;
+ uint16_t reserved2;
+} __attribute__((__packed__));
+
+
+/** Call-Clear-Request パケット */
+struct pptp_ccr {
+ struct pptp_ctrl_header header;
+ uint16_t call_id;
+ uint16_t reserved1;
+} __attribute__((__packed__));
+
+/** Call-Disconnect-Notify パケット */
+struct pptp_cdn {
+ struct pptp_ctrl_header header;
+ uint16_t call_id;
+ uint8_t result_code;
+ uint8_t error_code;
+ uint16_t cause_code;
+ uint16_t reserved1;
+ char statistics[128];
+} __attribute__((__packed__));
+
+
+#define PPTP_GRE_PKT_SEQ_PRESENT 0x0001
+#define PPTP_GRE_PKT_ACK_PRESENT 0x0002
+
+#endif
+
diff --git a/usr.sbin/npppd/pptp/pptp_subr.c b/usr.sbin/npppd/pptp/pptp_subr.c
new file mode 100644
index 00000000000..370fab840fd
--- /dev/null
+++ b/usr.sbin/npppd/pptp/pptp_subr.c
@@ -0,0 +1,144 @@
+/*-
+ * 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.
+ */
+/**@file
+ * PPTPの補助的な関数
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <event.h>
+
+#include "bytebuf.h"
+#include "hash.h"
+#include "slist.h"
+
+#include "pptp.h"
+#include "pptp_local.h"
+#include "pptp_subr.h"
+
+/** Faming Capability ビットをカンマ区切りの文字列で返す */
+const char *
+pptp_framing_string(uint32_t bits)
+{
+ static char framing_cap_buf[40];
+
+ snprintf(framing_cap_buf, sizeof(framing_cap_buf), "%s%s",
+ (bits & PPTP_CTRL_FRAMING_ASYNC)? ",async" : "",
+ (bits & PPTP_CTRL_FRAMING_SYNC)? ",sync" : "");
+
+ if (strlen(framing_cap_buf) > 0)
+ return &framing_cap_buf[1];
+
+ return "";
+}
+
+/** Bearer Capability ビットをカンマ区切りの文字列で返す */
+const char *
+pptp_bearer_string(uint32_t bits)
+{
+ static char bearer_cap_buf[40];
+
+ snprintf(bearer_cap_buf, sizeof(bearer_cap_buf), "%s%s",
+ (bits &PPTP_CTRL_BEARER_ANALOG)? ",analog" : "",
+ (bits &PPTP_CTRL_BEARER_DIGITAL)? ",digital" : "");
+
+ if (strlen(bearer_cap_buf) > 0)
+ return &bearer_cap_buf[1];
+
+ return "";
+}
+
+/** コントロールパケットのヘッダ(共通部分)を作成する */
+void
+pptp_init_header(struct pptp_ctrl_header *header, int length, int ctrl_mes_type)
+{
+ header->length = htons(length);
+ header->pptp_message_type = htons(PPTP_MES_TYPE_CTRL);
+ header->magic_cookie = htonl(PPTP_MAGIC_COOKIE);
+ header->control_message_type = htons(ctrl_mes_type);
+ header->reserved0 = 0;
+}
+
+#define NAME_VAL(x) { x, #x }
+static struct _label_name {
+ int label;
+ const char *name;
+} pptp_StopCCRQ_reason_labels[] = {
+ NAME_VAL(PPTP_StopCCRQ_REASON_NONE),
+ NAME_VAL(PPTP_StopCCRQ_REASON_STOP_PROTOCOL),
+ NAME_VAL(PPTP_StopCCRQ_REASON_STOP_LOCAL_SHUTDOWN),
+}, pptp_StopCCRP_result_labels[] = {
+ NAME_VAL(PPTP_StopCCRP_RESULT_OK),
+ NAME_VAL(PPTP_StopCCRP_RESULT_GENERIC_ERROR),
+}, pptp_general_error_labels[] = {
+ NAME_VAL(PPTP_ERROR_NONE),
+ NAME_VAL(PPTP_ERROR_NOT_CONNECTED),
+ NAME_VAL(PPTP_ERROR_BAD_FORMAT),
+ NAME_VAL(PPTP_ERROR_NO_RESOURCE),
+ NAME_VAL(PPTP_ERROR_BAD_CALL),
+}, pptp_ctrl_mes_type_labels[] = {
+ NAME_VAL(PPTP_CTRL_MES_CODE_SCCRQ),
+ NAME_VAL(PPTP_CTRL_MES_CODE_SCCRP),
+ NAME_VAL(PPTP_CTRL_MES_CODE_StopCCRQ),
+ NAME_VAL(PPTP_CTRL_MES_CODE_StopCCRP),
+ NAME_VAL(PPTP_CTRL_MES_CODE_ECHO_RQ),
+ NAME_VAL(PPTP_CTRL_MES_CODE_ECHO_RP),
+ NAME_VAL(PPTP_CTRL_MES_CODE_OCRQ),
+ NAME_VAL(PPTP_CTRL_MES_CODE_OCRP),
+ NAME_VAL(PPTP_CTRL_MES_CODE_ICRQ),
+ NAME_VAL(PPTP_CTRL_MES_CODE_ICRP),
+ NAME_VAL(PPTP_CTRL_MES_CODE_ICCN),
+ NAME_VAL(PPTP_CTRL_MES_CODE_CCR),
+ NAME_VAL(PPTP_CTRL_MES_CODE_CDN),
+ NAME_VAL(PPTP_CTRL_MES_CODE_SLI),
+}, pptp_CDN_result_labels[] = {
+ NAME_VAL(PPTP_CDN_RESULT_LOST_CARRIER),
+ NAME_VAL(PPTP_CDN_RESULT_GENRIC_ERROR),
+ NAME_VAL(PPTP_CDN_RESULT_ADMIN_SHUTDOWN),
+ NAME_VAL(PPTP_CDN_RESULT_REQUEST),
+};
+// _label_name を使ったマクロ。値を渡すと文字列で返す。
+#define LABEL_TO_STRING(func_name, label_names, prefix_len) \
+ const char * \
+ func_name(int code) \
+ { \
+ int i; \
+ \
+ for (i = 0; i < countof(label_names); i++) { \
+ if (label_names[i].label == code) \
+ return label_names[i].name + prefix_len;\
+ } \
+ \
+ return "UNKNOWN"; \
+ }
+LABEL_TO_STRING(pptp_StopCCRQ_reason_string, pptp_StopCCRQ_reason_labels, 21)
+LABEL_TO_STRING(pptp_StopCCRP_result_string, pptp_StopCCRP_result_labels, 21)
+LABEL_TO_STRING(pptp_general_error_string, pptp_general_error_labels, 11)
+LABEL_TO_STRING(pptp_ctrl_mes_type_string, pptp_ctrl_mes_type_labels, 19)
+LABEL_TO_STRING(pptp_CDN_result_string, pptp_CDN_result_labels, 16)
diff --git a/usr.sbin/npppd/pptp/pptp_subr.h b/usr.sbin/npppd/pptp/pptp_subr.h
new file mode 100644
index 00000000000..a09c82cc98f
--- /dev/null
+++ b/usr.sbin/npppd/pptp/pptp_subr.h
@@ -0,0 +1,50 @@
+/*-
+ * 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.
+ */
+#ifndef PPTP_SUBR_H
+#define PPTP_SUBR_H 1
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char *pptp_framing_string (uint32_t);
+const char *pptp_bearer_string (uint32_t);
+void pptp_init_header (struct pptp_ctrl_header *, int, int);
+const char *pptp_StopCCRQ_reason_string(int);
+const char *pptp_StopCCRP_result_string(int);
+const char *pptp_general_error_string(int);
+const char *pptp_ctrl_mes_type_string(int);
+const char *pptp_CDN_result_string(int);
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+
+
+
+
+
+
diff --git a/usr.sbin/npppd/pptp/pptpd.c b/usr.sbin/npppd/pptp/pptpd.c
new file mode 100644
index 00000000000..82cf429b644
--- /dev/null
+++ b/usr.sbin/npppd/pptp/pptpd.c
@@ -0,0 +1,1151 @@
+/*-
+ * 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: pptpd.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */
+/**@file
+ * PPTPデーモンの実装。現在は PAC(PPTP Access Concentrator) としての実装
+ * のみです。
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <string.h>
+#include <event.h>
+#include <ifaddrs.h>
+
+#ifdef USE_LIBSOCKUTIL
+#include <seil/sockfromto.h>
+#endif
+
+#include "net_utils.h"
+#include "bytebuf.h"
+#include "debugutil.h"
+#include "hash.h"
+#include "slist.h"
+#include "addr_range.h"
+#include "properties.h"
+#include "config_helper.h"
+#ifdef _SEIL_EXT_
+#include "rtev.h"
+#endif
+
+#include "pptp.h"
+#include "pptp_local.h"
+
+static int pptpd_seqno = 0;
+
+#ifdef PPTPD_DEBUG
+#define PPTPD_ASSERT(x) ASSERT(x)
+#define PPTPD_DBG(x) pptpd_log x
+#else
+#define PPTPD_ASSERT(x)
+#define PPTPD_DBG(x)
+#endif
+
+static void pptpd_log (pptpd *, int, const char *, ...) __printflike(3,4);
+static void pptpd_close_gre (pptpd *);
+static void pptpd_close_1723 (pptpd *);
+static void pptpd_io_event (int, short, void *);
+static void pptpd_gre_io_event (int, short, void *);
+static void pptpd_gre_input (pptpd_listener *, struct sockaddr *, u_char *, int);
+static void pptp_ctrl_start_by_pptpd (pptpd *, int, int, struct sockaddr *);
+static int pptp_call_cmp (const void *, const void *);
+static uint32_t pptp_call_hash (const void *, int);
+static void pptp_gre_header_string (struct pptp_gre_header *, char *, int);
+
+#define PPTPD_SHUFFLE_MARK -1
+
+/** PPTPデーモンを初期化します */
+int
+pptpd_init(pptpd *_this)
+{
+ int i, m;
+ struct sockaddr_in sin0;
+ uint16_t call0, call[UINT16_MAX - 1];
+
+ memset(_this, 0, sizeof(pptpd));
+ _this->id = pptpd_seqno++;
+
+ slist_init(&_this->listener);
+ memset(&sin0, 0, sizeof(sin0));
+ sin0.sin_len = sizeof(sin0);
+ sin0.sin_family = AF_INET;
+ if (pptpd_add_listener(_this, 0, PPTPD_DEFAULT_LAYER2_LABEL,
+ (struct sockaddr *)&sin0) != 0) {
+ return 1;
+ }
+
+ _this->ip4_allow = NULL;
+
+ slist_init(&_this->ctrl_list);
+ slist_init(&_this->call_free_list);
+
+ /* Call-ID シャッフル */
+ for (i = 0; i < countof(call) ; i++)
+ call[i] = i + 1;
+ for (i = countof(call); i > 1; i--) {
+ m = random() % i;
+ call0 = call[m];
+ call[m] = call[i - 1];
+ call[i - 1] = call0;
+ }
+ /* 必要個だけを slist に */
+ for (i = 0; i < MIN(PPTP_MAX_CALL, countof(call)); i++)
+ slist_add(&_this->call_free_list, (void *)(uintptr_t)call[i]);
+ /* 末尾に SHUFFLE_MARK。次回は slist_shuffle で shuflle される */
+ slist_add(&_this->call_free_list, (void *)PPTPD_SHUFFLE_MARK);
+
+ if (_this->call_id_map == NULL)
+ _this->call_id_map = hash_create(pptp_call_cmp, pptp_call_hash,
+ 0);
+
+ return 0;
+}
+
+/**
+ * {@link ::pptpd PPTPデーモン}に{@link ::pptpd_listener リスナ}を追加します。
+ * @param _this {@link ::pptpd PPTPデーモン}
+ * @param idx リスナのインデックス
+ * @param label 物理層としてのラベル。"PPTP" など
+ * @param bindaddr 待ち受けるアドレス
+ */
+int
+pptpd_add_listener(pptpd *_this, int idx, const char *label,
+ struct sockaddr *bindaddr)
+{
+ int inaddr_any;
+ pptpd_listener *plistener, *plstn;
+
+ plistener = NULL;
+ if (idx == 0 && slist_length(&_this->listener) > 0) {
+ slist_itr_first(&_this->listener);
+ while (slist_itr_has_next(&_this->listener)) {
+ slist_itr_next(&_this->listener);
+ plstn = slist_itr_remove(&_this->listener);
+ PPTPD_ASSERT(plstn != NULL);
+ PPTPD_ASSERT(plstn->sock == -1);
+ PPTPD_ASSERT(plstn->sock_gre == -1);
+ free(plstn);
+ }
+ }
+ PPTPD_ASSERT(slist_length(&_this->listener) == idx);
+ if (slist_length(&_this->listener) != idx) {
+ pptpd_log(_this, LOG_ERR,
+ "Invalid argument error on %s(): idx must be %d but %d",
+ __func__, slist_length(&_this->listener), idx);
+ goto reigai;
+ }
+ if ((plistener = malloc(sizeof(pptpd_listener))) == NULL) {
+ pptpd_log(_this, LOG_ERR, "malloc() failed in %s: %m",
+ __func__);
+ goto reigai;
+ }
+ memset(plistener, 0, sizeof(pptpd_listener));
+
+ PPTPD_ASSERT(sizeof(plistener->bind_sin) >= bindaddr->sa_len);
+ memcpy(&plistener->bind_sin, bindaddr, bindaddr->sa_len);
+ memcpy(&plistener->bind_sin_gre, bindaddr, bindaddr->sa_len);
+
+ /* ポート番号が省略された場合は、デフォルト (1723/tcp)を使う */
+ if (plistener->bind_sin.sin_port == 0)
+ plistener->bind_sin.sin_port = htons(PPTPD_DEFAULT_TCP_PORT);
+
+ /*
+ * raw ソケットで、INADDR_ANY と明示的な IP アドレス指定したソケット両
+ * 方を bind した場合、パケットは両方のソケットで受信される。この状態が
+ * 発生すると、パケットが重複して受信したように見えてしまうため、このよ
+ * うな設定は許さないこととした。
+ */
+ inaddr_any = 0;
+ slist_itr_first(&_this->listener);
+ while (slist_itr_has_next(&_this->listener)) {
+ plstn = slist_itr_next(&_this->listener);
+ if (plstn->bind_sin_gre.sin_addr.s_addr == INADDR_ANY)
+ inaddr_any++;
+ }
+ if (plistener->bind_sin_gre.sin_addr.s_addr == INADDR_ANY)
+ inaddr_any++;
+ if (inaddr_any > 0 && idx > 0) {
+ log_printf(LOG_ERR, "configuration error at pptpd.listener_in: "
+ "combination 0.0.0.0 and other address is not allowed.");
+ goto reigai;
+ }
+
+ plistener->bind_sin_gre.sin_port = 0;
+ plistener->sock = -1;
+ plistener->sock_gre = -1;
+ plistener->self = _this;
+ plistener->index = idx;
+ strlcpy(plistener->phy_label, label, sizeof(plistener->phy_label));
+
+ if (slist_add(&_this->listener, plistener) == NULL) {
+ pptpd_log(_this, LOG_ERR, "slist_add() failed in %s: %m",
+ __func__);
+ goto reigai;
+ }
+ return 0;
+reigai:
+ if (plistener != NULL)
+ free(plistener);
+ return 1;
+}
+
+void
+pptpd_uninit(pptpd *_this)
+{
+ pptpd_listener *plstn;
+
+ slist_fini(&_this->ctrl_list);
+ slist_fini(&_this->call_free_list);
+
+ slist_itr_first(&_this->listener);
+ while (slist_itr_has_next(&_this->listener)) {
+ plstn = slist_itr_next(&_this->listener);
+ PPTPD_ASSERT(plstn != NULL);
+ PPTPD_ASSERT(plstn->sock == -1);
+ PPTPD_ASSERT(plstn->sock_gre == -1);
+ free(plstn);
+ }
+ slist_fini(&_this->listener);
+ if (_this->call_id_map != NULL) {
+ // アイテムの削除?
+ hash_free(_this->call_id_map);
+ }
+ if (_this->ip4_allow != NULL)
+ in_addr_range_list_remove_all(&_this->ip4_allow);
+ _this->call_id_map = NULL;
+ _this->config = NULL;
+}
+
+#define CALL_MAP_KEY(call) \
+ (void *)(call->id | (call->ctrl->listener_index << 16))
+#define CALL_ID(item) ((uint32_t)item & 0xffff)
+
+/** PPTPを割り当てます */
+int
+pptpd_assign_call(pptpd *_this, pptp_call *call)
+{
+ int shuffle_cnt = 0, call_id;
+
+ shuffle_cnt = 0;
+ slist_itr_first(&_this->call_free_list);
+ while (slist_length(&_this->call_free_list) > 1 &&
+ slist_itr_has_next(&_this->call_free_list)) {
+ call_id = (int)slist_itr_next(&_this->call_free_list);
+ if (call_id == 0)
+ break;
+ slist_itr_remove(&_this->call_free_list);
+ if (call_id == PPTPD_SHUFFLE_MARK) {
+ if (shuffle_cnt++ > 0)
+ break;
+ slist_shuffle(&_this->call_free_list);
+ slist_add(&_this->call_free_list,
+ (void *)PPTPD_SHUFFLE_MARK);
+ slist_itr_first(&_this->call_free_list);
+ continue;
+ }
+ call->id = call_id;
+ hash_insert(_this->call_id_map, CALL_MAP_KEY(call), call);
+
+ return 0;
+ }
+ errno = EBUSY;
+ pptpd_log(_this, LOG_ERR, "call request reached limit=%d",
+ PPTP_MAX_CALL);
+ return -1;
+}
+
+/** PPTPを解放します。*/
+void
+pptpd_release_call(pptpd *_this, pptp_call *call)
+{
+ if (call->id != 0)
+ slist_add(&_this->call_free_list, (void *)call->id);
+ hash_delete(_this->call_id_map, CALL_MAP_KEY(call), 0);
+ call->id = 0;
+}
+
+static int
+pptpd_listener_start(pptpd_listener *_this)
+{
+ int sock, ival, sock_gre;
+ struct sockaddr_in bind_sin, bind_sin_gre;
+ int wildcardbinding;
+#ifdef NPPPD_FAKEBIND
+ extern void set_faith(int, int);
+#endif
+
+ wildcardbinding =
+ (_this->bind_sin.sin_addr.s_addr == INADDR_ANY)? 1 : 0;
+ sock = -1;
+ sock_gre = -1;
+ memcpy(&bind_sin, &_this->bind_sin, sizeof(bind_sin));
+ memcpy(&bind_sin_gre, &_this->bind_sin_gre, sizeof(bind_sin_gre));
+
+ if (_this->phy_label[0] == '\0')
+ strlcpy(_this->phy_label, PPTPD_DEFAULT_LAYER2_LABEL,
+ sizeof(_this->phy_label));
+ // 1723/tcp
+ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ pptpd_log(_this->self, LOG_ERR, "socket() failed at %s(): %m",
+ __func__);
+ goto reigai;
+ }
+ ival = 1;
+ if(setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &ival, sizeof(ival)) < 0){
+ pptpd_log(_this->self, LOG_WARNING,
+ "setsockopt(SO_REUSEPORT) failed at %s(): %m", __func__);
+ }
+#ifdef NPPPD_FAKEBIND
+ if (!wildcardbinding)
+ set_faith(sock, 1);
+#endif
+#if defined(IP_STRICT_RCVIF) && defined(USE_STRICT_RCVIF)
+ ival = 1;
+ if (setsockopt(sock, IPPROTO_IP, IP_STRICT_RCVIF, &ival, sizeof(ival))
+ != 0)
+ pptpd_log(_this->self, LOG_WARNING,
+ "%s(): setsockopt(IP_STRICT_RCVIF) failed: %m", __func__);
+#endif
+ if ((ival = fcntl(sock, F_GETFL, 0)) < 0) {
+ pptpd_log(_this->self, LOG_ERR,
+ "fcntl(F_GET_FL) failed at %s(): %m", __func__);
+ goto reigai;
+ } else if (fcntl(sock, F_SETFL, ival | O_NONBLOCK) < 0) {
+ pptpd_log(_this->self, LOG_ERR,
+ "fcntl(F_SET_FL) failed at %s(): %m", __func__);
+ goto reigai;
+ }
+ if (bind(sock, (struct sockaddr *)&_this->bind_sin,
+ _this->bind_sin.sin_len) != 0) {
+ pptpd_log(_this->self, LOG_ERR,
+ "bind(%s:%u) failed at %s(): %m",
+ inet_ntoa(_this->bind_sin.sin_addr),
+ ntohs(_this->bind_sin.sin_port), __func__);
+ goto reigai;
+ }
+ if (listen(sock, PPTP_BACKLOG) != 0) {
+ pptpd_log(_this->self, LOG_ERR,
+ "listen(%s:%u) failed at %s(): %m",
+ inet_ntoa(_this->bind_sin.sin_addr),
+ ntohs(_this->bind_sin.sin_port), __func__);
+ goto reigai;
+ }
+#ifdef NPPPD_FAKEBIND
+ if (!wildcardbinding)
+ set_faith(sock, 0);
+#endif
+ pptpd_log(_this->self, LOG_INFO, "Listening %s:%u/tcp (PPTP PAC) [%s]",
+ inet_ntoa(_this->bind_sin.sin_addr),
+ ntohs(_this->bind_sin.sin_port), _this->phy_label);
+
+ /* GRE */
+ bind_sin_gre.sin_port = 0;
+ if ((sock_gre = socket(AF_INET, SOCK_RAW, IPPROTO_GRE)) < 0) {
+ pptpd_log(_this->self, LOG_ERR, "socket() failed at %s(): %m",
+ __func__);
+ goto reigai;
+ }
+#ifdef NPPPD_FAKEBIND
+ if (!wildcardbinding)
+ set_faith(sock_gre, 1);
+#endif
+#if defined(IP_STRICT_RCVIF) && defined(USE_STRICT_RCVIF)
+ ival = 1;
+ if (setsockopt(sock_gre, IPPROTO_IP, IP_STRICT_RCVIF, &ival,
+ sizeof(ival)) != 0)
+ pptpd_log(_this->self, LOG_WARNING,
+ "%s(): setsockopt(IP_STRICT_RCVIF) failed: %m", __func__);
+#endif
+ if ((ival = fcntl(sock_gre, F_GETFL, 0)) < 0) {
+ pptpd_log(_this->self, LOG_ERR,
+ "fcntl(F_GET_FL) failed at %s(): %m", __func__);
+ goto reigai;
+ } else if (fcntl(sock_gre, F_SETFL, ival | O_NONBLOCK) < 0) {
+ pptpd_log(_this->self, LOG_ERR,
+ "fcntl(F_SET_FL) failed at %s(): %m", __func__);
+ goto reigai;
+ }
+ if (bind(sock_gre, (struct sockaddr *)&bind_sin_gre,
+ bind_sin_gre.sin_len) != 0) {
+ pptpd_log(_this->self, LOG_ERR,
+ "bind(%s:%u) failed at %s(): %m",
+ inet_ntoa(bind_sin_gre.sin_addr),
+ ntohs(bind_sin_gre.sin_port), __func__);
+ goto reigai;
+ }
+#ifdef NPPPD_FAKEBIND
+ if (!wildcardbinding)
+ set_faith(sock_gre, 0);
+#endif
+ if (wildcardbinding) {
+#ifdef USE_LIBSOCKUTIL
+ if (setsockoptfromto(sock) != 0) {
+ pptpd_log(_this->self, LOG_ERR,
+ "setsockoptfromto() failed in %s(): %m", __func__);
+ goto reigai;
+ }
+#else
+ /* nothing to do */
+#endif
+ }
+ pptpd_log(_this->self, LOG_INFO, "Listening %s:gre (PPTP PAC)",
+ inet_ntoa(bind_sin_gre.sin_addr));
+
+ _this->sock = sock;
+ _this->sock_gre = sock_gre;
+
+ event_set(&_this->ev_sock, _this->sock, EV_READ | EV_PERSIST,
+ pptpd_io_event, _this);
+ event_add(&_this->ev_sock, NULL);
+
+ event_set(&_this->ev_sock_gre, _this->sock_gre, EV_READ | EV_PERSIST,
+ pptpd_gre_io_event, _this);
+ event_add(&_this->ev_sock_gre, NULL);
+
+ return 0;
+reigai:
+ if (sock >= 0)
+ close(sock);
+ if (sock_gre >= 0)
+ close(sock_gre);
+
+ _this->sock = -1;
+ _this->sock_gre = -1;
+
+ return 1;
+}
+/** PPTPデーモンを開始します */
+int
+pptpd_start(pptpd *_this)
+{
+ int rval = 0;
+ pptpd_listener *plistener;
+
+ slist_itr_first(&_this->listener);
+ while (slist_itr_has_next(&_this->listener)) {
+ plistener = slist_itr_next(&_this->listener);
+ PPTPD_ASSERT(plistener != NULL);
+ rval |= pptpd_listener_start(plistener);
+ }
+ if (rval == 0)
+ _this->state = PPTPD_STATE_RUNNING;
+
+ return rval;
+}
+
+static void
+pptpd_listener_close_gre(pptpd_listener *_this)
+{
+ if (_this->sock_gre >= 0) {
+ event_del(&_this->ev_sock_gre);
+ close(_this->sock_gre);
+ pptpd_log(_this->self, LOG_INFO, "Shutdown %s/gre",
+ inet_ntoa(_this->bind_sin_gre.sin_addr));
+ }
+ _this->sock_gre = -1;
+}
+
+/** GREの待ち受けソケットを close します */
+static void
+pptpd_close_gre(pptpd *_this)
+{
+ pptpd_listener *plistener;
+
+ slist_itr_first(&_this->listener);
+ while (slist_itr_has_next(&_this->listener)) {
+ plistener = slist_itr_next(&_this->listener);
+ pptpd_listener_close_gre(plistener);
+ }
+}
+
+/** 1723/tcp の待ち受けソケットを close します */
+static void
+pptpd_listener_close_1723(pptpd_listener *_this)
+{
+ if (_this->sock >= 0) {
+ event_del(&_this->ev_sock);
+ close(_this->sock);
+ pptpd_log(_this->self, LOG_INFO, "Shutdown %s:%u/tcp",
+ inet_ntoa(_this->bind_sin.sin_addr),
+ ntohs(_this->bind_sin.sin_port));
+ }
+ _this->sock = -1;
+}
+/** 1723/tcp の待ち受けソケットを close します */
+static void
+pptpd_close_1723(pptpd *_this)
+{
+ pptpd_listener *plistener;
+
+ slist_itr_first(&_this->listener);
+ while (slist_itr_has_next(&_this->listener)) {
+ plistener = slist_itr_next(&_this->listener);
+ pptpd_listener_close_1723(plistener);
+ }
+}
+
+/** PPTPデーモンを本当に終了します。**/
+void
+pptpd_stop_immediatly(pptpd *_this)
+{
+ pptp_ctrl *ctrl;
+
+ if (event_initialized(&_this->ev_timer))
+ evtimer_del(&_this->ev_timer);
+ if (_this->state != PPTPD_STATE_STOPPED) {
+ /*
+ * pptp_ctrl_stop を呼び出すと、この関数が再度呼ばれる可能
+ * 性がある。このため、このstate 変更は重要。
+ */
+ _this->state = PPTPD_STATE_STOPPED;
+
+ pptpd_close_1723(_this);
+ for (slist_itr_first(&_this->ctrl_list);
+ (ctrl = slist_itr_next(&_this->ctrl_list)) != NULL;) {
+ pptp_ctrl_stop(ctrl, 0);
+ }
+ pptpd_close_gre(_this);
+ slist_fini(&_this->ctrl_list);
+ slist_fini(&_this->call_free_list);
+ pptpd_log(_this, LOG_NOTICE, "Stopped");
+ } else {
+ PPTPD_DBG((_this, LOG_DEBUG, "(Already) Stopped"));
+ }
+}
+
+static void
+pptpd_stop_timeout(int fd, short event, void *ctx)
+{
+ pptpd *_this;
+
+ _this = ctx;
+ pptpd_stop_immediatly(_this);
+}
+
+/** PPTPデーモンを終了します */
+void
+pptpd_stop(pptpd *_this)
+{
+ int nctrl;
+ pptp_ctrl *ctrl;
+ struct timeval tv;
+
+ if (event_initialized(&_this->ev_timer))
+ evtimer_del(&_this->ev_timer);
+ pptpd_close_1723(_this);
+ /* このあたりの動作は l2tpd_stop とあわせるべき */
+
+ if (pptpd_is_stopped(_this))
+ return;
+ if (pptpd_is_shutting_down(_this)) {
+ pptpd_stop_immediatly(_this);
+ return;
+ }
+ _this->state = PPTPD_STATE_SHUTTING_DOWN;
+ nctrl = 0;
+ for (slist_itr_first(&_this->ctrl_list);
+ (ctrl = slist_itr_next(&_this->ctrl_list)) != NULL;) {
+ pptp_ctrl_stop(ctrl, PPTP_CDN_RESULT_ADMIN_SHUTDOWN);
+ nctrl++;
+ }
+ if (nctrl > 0) {
+ // タイマーセット
+ tv.tv_sec = PPTPD_SHUTDOWN_TIMEOUT;
+ tv.tv_usec = 0;
+
+ evtimer_set(&_this->ev_timer, pptpd_stop_timeout, _this);
+ evtimer_add(&_this->ev_timer, &tv);
+
+ return;
+ }
+ pptpd_stop_immediatly(_this);
+}
+
+/***********************************************************************
+ * 設定関連
+ ***********************************************************************/
+#define CFG_KEY(p, s) config_key_prefix((p), (s))
+#define VAL_SEP " \t\r\n"
+
+CONFIG_FUNCTIONS(pptpd_config, pptpd, config);
+PREFIXED_CONFIG_FUNCTIONS(pptp_ctrl_config, pptp_ctrl, pptpd->config,
+ phy_label);
+int
+pptpd_reload(pptpd *_this, struct properties *config, const char *name,
+ int default_enabled)
+{
+ int i, do_start, aierr;
+ const char *val;
+ char *tok, *cp, buf[PPTPD_CONFIG_BUFSIZ], *label;
+ struct addrinfo *ai;
+
+ ASSERT(_this != NULL);
+ ASSERT(config != NULL);
+
+ _this->config = config; /* 現在は copy しなくて大丈夫 */
+ do_start = 0;
+ if (pptpd_config_str_equal(_this, CFG_KEY(name, "enabled"), "true",
+ default_enabled)) {
+ // false にした直後に true にされるかもしれない。
+ if (pptpd_is_shutting_down(_this))
+ pptpd_stop_immediatly(_this);
+ if (pptpd_is_stopped(_this))
+ do_start = 1;
+ } else {
+ if (!pptpd_is_stopped(_this))
+ pptpd_stop(_this);
+ return 0;
+ }
+ if (do_start && pptpd_init(_this) != 0)
+ return 1;
+ // pptpd_init でリセットされてしまうので。
+ _this->config = config;
+
+ _this->ctrl_in_pktdump = pptpd_config_str_equal(_this,
+ "log.pptp.ctrl.in.pktdump", "true", 0);
+ _this->data_in_pktdump = pptpd_config_str_equal(_this,
+ "log.pptp.data.in.pktdump", "true", 0);
+ _this->ctrl_out_pktdump = pptpd_config_str_equal(_this,
+ "log.pptp.ctrl.out.pktdump", "true", 0);
+ _this->data_out_pktdump = pptpd_config_str_equal(_this,
+ "log.pptp.data.out.pktdump", "true", 0);
+ _this->phy_label_with_ifname = pptpd_config_str_equal(_this,
+ CFG_KEY(name, "label_with_ifname"), "true", 0);
+
+ // ip4_allow をパース
+ in_addr_range_list_remove_all(&_this->ip4_allow);
+ val = pptpd_config_str(_this, CFG_KEY(name, "ip4_allow"));
+ if (val != NULL) {
+ if (strlen(val) >= sizeof(buf)) {
+ log_printf(LOG_ERR, "configuration error at "
+ "%s: too long", CFG_KEY(name, "ip4_allow"));
+ return 1;
+ }
+ strlcpy(buf, val, sizeof(buf));
+ for (cp = buf; (tok = strsep(&cp, VAL_SEP)) != NULL;) {
+ if (*tok == '\0')
+ continue;
+ if (in_addr_range_list_add(&_this->ip4_allow, tok)
+ != 0) {
+ pptpd_log(_this, LOG_ERR,
+ "configuration error at %s: %s",
+ CFG_KEY(name, "ip4_allow"), tok);
+ return 1;
+ }
+ }
+ }
+
+ if (do_start) {
+ /*
+ * 起動直後と、pptpd.enable が false -> true に変更された
+ * 場合に、do_start。すべてのリスナーが、初期化された状態を
+ * 仮定できる
+ */
+ // pptpd.listener_in の読み込む
+ val = pptpd_config_str(_this, CFG_KEY(name, "listener_in"));
+ if (val != NULL) {
+ if (strlen(val) >= sizeof(buf)) {
+ pptpd_log(_this, LOG_ERR,
+ "configuration error at "
+ "%s: too long", CFG_KEY(name, "listener"));
+ return 1;
+ }
+ strlcpy(buf, val, sizeof(buf));
+
+ label = NULL;
+ // タブ、スペース区切りで、複数指定可能
+ for (i = 0, cp = buf;
+ (tok = strsep(&cp, VAL_SEP)) != NULL;) {
+ if (*tok == '\0')
+ continue;
+ if (label == NULL) {
+ label = tok;
+ continue;
+ }
+ if ((aierr = addrport_parse(tok, IPPROTO_TCP,
+ &ai)) != 0) {
+ pptpd_log(_this, LOG_ERR,
+ "configuration error at "
+ "%s: %s: %s",
+ CFG_KEY(name, "listener_in"), tok,
+ gai_strerror(aierr));
+ return 1;
+ }
+ PPTPD_ASSERT(ai != NULL &&
+ ai->ai_family == AF_INET);
+ if (pptpd_add_listener(_this, i, label,
+ ai->ai_addr) != 0) {
+ freeaddrinfo(ai);
+ label = NULL;
+ break;
+ }
+ freeaddrinfo(ai);
+ label = NULL;
+ i++;
+ }
+ if (label != NULL) {
+ pptpd_log(_this, LOG_ERR,
+ "configuration error at %s: %s",
+ CFG_KEY(name, "listner_in"), label);
+ return 1;
+ }
+ }
+ if (pptpd_start(_this) != 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/***********************************************************************
+ * I/O関連
+ ***********************************************************************/
+static void
+pptpd_log_access_deny(pptpd *_this, const char *reason, struct sockaddr *peer)
+{
+ char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV];
+
+ if (getnameinfo(peer, peer->sa_len, hostbuf, sizeof(hostbuf),
+ servbuf, sizeof(servbuf), NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+ pptpd_log(_this, LOG_ERR, "getnameinfo() failed at %s(): %m",
+ __func__);
+ return;
+ }
+ pptpd_log(_this, LOG_ALERT, "denied a connection from %s:%s/tcp: %s",
+ hostbuf, servbuf, reason);
+}
+
+/** 1723/tcp の IOイベントハンドラ */
+static void
+pptpd_io_event(int fd, short evmask, void *ctx)
+{
+ int newsock;
+ const char *reason;
+ socklen_t peerlen;
+ struct sockaddr_storage peer;
+ pptpd *_this;
+ pptpd_listener *listener;
+
+ listener = ctx;
+ PPTPD_ASSERT(listener != NULL);
+ _this = listener->self;
+ PPTPD_ASSERT(_this != NULL);
+
+ if ((evmask & EV_READ) != 0) {
+ for (;;) { // EAGAIN まで 連続して accept
+ peerlen = sizeof(peer);
+ if ((newsock = accept(listener->sock,
+ (struct sockaddr *)&peer, &peerlen)) < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ break;
+ case ECONNABORTED:
+ pptpd_log(_this, LOG_WARNING,
+ "accept() failed at %s(): %m",
+ __func__);
+ break;
+ default:
+ pptpd_log(_this, LOG_ERR,
+ "accept() failed at %s(): %m",
+ __func__);
+ pptpd_listener_close_1723(listener);
+ pptpd_stop(_this);
+ }
+ break;
+ }
+ // 送信元チェック
+ switch (peer.ss_family) {
+ case AF_INET:
+ if (!in_addr_range_list_includes(
+ &_this->ip4_allow,
+ &((struct sockaddr_in *)&peer)->sin_addr)) {
+ reason = "not allowed by acl.";
+ break;
+ }
+ goto accept;
+ default:
+ reason = "address family is not supported.";
+ break;
+ }
+ // 許可されていない
+ pptpd_log_access_deny(_this, reason,
+ (struct sockaddr *)&peer);
+ close(newsock);
+ continue;
+ // NOTREACHED
+accept:
+ // 許可
+ pptp_ctrl_start_by_pptpd(_this, newsock,
+ listener->index, (struct sockaddr *)&peer);
+ }
+ }
+}
+
+/** GRE の IOイベントハンドラー */
+static void
+pptpd_gre_io_event(int fd, short evmask, void *ctx)
+{
+ int sz;
+ u_char pkt[65535];
+ socklen_t peerlen;
+ struct sockaddr_storage peer;
+ pptpd *_this;
+ pptpd_listener *listener;
+
+ listener = ctx;
+ PPTPD_ASSERT(listener != NULL);
+ _this = listener->self;
+ PPTPD_ASSERT(_this != NULL);
+
+ if (evmask & EV_READ) {
+ for (;;) {
+ // Block するまで読む
+ peerlen = sizeof(peer);
+ if ((sz = recvfrom(listener->sock_gre, pkt, sizeof(pkt),
+ 0, (struct sockaddr *)&peer, &peerlen)) <= 0) {
+ if (sz < 0 &&
+ (errno == EAGAIN || errno == EINTR))
+ break;
+ pptpd_log(_this, LOG_INFO,
+ "read(GRE) failed: %m");
+ pptpd_stop(_this);
+ return;
+ }
+ pptpd_gre_input(listener, (struct sockaddr *)&peer, pkt,
+ sz);
+ }
+ }
+}
+
+/** GREの受信 → 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 reigai;
+ }
+ 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 reigai;
+ }
+
+ if (lpkt < sizeof(struct ip)) {
+ pptpd_log(_this, LOG_ERR,
+ "Received a short length packet length=%d, from %s", lpkt,
+ hbuf0);
+ goto reigai;
+ }
+ iphdr = (struct ip *)pkt;
+
+ // IPヘッダは ntohs 済み NetBSD の場合
+#if !defined(__NetBSD__)
+ iphdr->ip_len = ntohs(iphdr->ip_len);
+#endif
+ 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 reigai;
+ }
+ 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;
+ }
+
+
+ // 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);
+reigai:
+ return;
+}
+
+/** PPTPコントロールを開始します。(新しい接続があれば呼び出される。) */
+static void
+pptp_ctrl_start_by_pptpd(pptpd *_this, int sock, int listener_index,
+ struct sockaddr *peer)
+{
+ int ival;
+ pptp_ctrl *ctrl;
+ socklen_t sslen;
+ char ifname[IF_NAMESIZE], msgbuf[128];
+
+ ctrl = NULL;
+ if ((ctrl = pptp_ctrl_create()) == NULL)
+ goto reigai;
+ if (pptp_ctrl_init(ctrl) != 0)
+ goto reigai;
+
+ memset(&ctrl->peer, 0, sizeof(ctrl->peer));
+ memcpy(&ctrl->peer, peer, peer->sa_len);
+ ctrl->pptpd = _this;
+ ctrl->sock = sock;
+ ctrl->listener_index = listener_index;
+
+ sslen = sizeof(ctrl->our);
+ if (getsockname(ctrl->sock, (struct sockaddr *)&ctrl->our,
+ &sslen) != 0) {
+ pptpd_log(_this, LOG_WARNING,
+ "getsockname() failed at %s(): %m", __func__);
+ goto reigai;
+ }
+ /* "L2TP%em0.mru" などと、インタフェースで設定を変更する場合 */
+ if (_this->phy_label_with_ifname != 0) {
+ if (get_ifname_by_sockaddr((struct sockaddr *)&ctrl->our,
+ ifname) == NULL) {
+ pptpd_log_access_deny(_this,
+ "could not get interface informations", peer);
+ goto reigai;
+ }
+ if (pptpd_config_str_equal(_this,
+ config_key_prefix("pptpd.interface", ifname), "accept", 0)){
+ snprintf(ctrl->phy_label, sizeof(ctrl->phy_label),
+ "%s%%%s", PPTP_CTRL_LISTENER_LABEL(ctrl), ifname);
+ } else if (pptpd_config_str_equal(_this,
+ config_key_prefix("pptpd.interface", "any"), "accept", 0)){
+ snprintf(ctrl->phy_label, sizeof(ctrl->phy_label),
+ "%s", PPTP_CTRL_LISTENER_LABEL(ctrl));
+ } else {
+ /* このインタフェースは許可されていない。*/
+ snprintf(msgbuf, sizeof(msgbuf),
+ "'%s' is not allowed by config.", ifname);
+ pptpd_log_access_deny(_this, msgbuf, peer);
+ goto reigai;
+ }
+#if defined(_SEIL_EXT_) && !defined(USE_LIBSOCKUTIL)
+ if (!rtev_ifa_is_primary(ifname,
+ (struct sockaddr *)&ctrl->our)) {
+ char hostbuf[NI_MAXHOST];
+
+ getnameinfo((struct sockaddr *)&ctrl->our,
+ ctrl->our.ss_len, hostbuf,
+ sizeof(hostbuf), NULL, 0, NI_NUMERICHOST);
+ snprintf(msgbuf, sizeof(msgbuf),
+ "connecting to %s (an alias address of %s)"
+ " is not allowed by this version.",
+ hostbuf, ifname);
+ pptpd_log_access_deny(_this, msgbuf, peer);
+
+ goto reigai;
+ }
+#endif
+ } else
+ strlcpy(ctrl->phy_label, PPTP_CTRL_LISTENER_LABEL(ctrl),
+ sizeof(ctrl->phy_label));
+
+ if ((ival = pptp_ctrl_config_int(ctrl, "pptp.echo_interval", 0)) != 0)
+ ctrl->echo_interval = ival;
+
+ if ((ival = pptp_ctrl_config_int(ctrl, "pptp.echo_timeout", 0)) != 0)
+ ctrl->echo_timeout = ival;
+
+ if (pptp_ctrl_start(ctrl) != 0)
+ goto reigai;
+
+ slist_add(&_this->ctrl_list, ctrl);
+
+ return;
+reigai:
+ close(sock);
+ pptp_ctrl_destroy(ctrl);
+ return;
+}
+
+/** PPTPコントロールが終了後に通知してきます。*/
+void
+pptpd_ctrl_finished_notify(pptpd *_this, pptp_ctrl *ctrl)
+{
+ pptp_ctrl *ctrl1;
+ int i, nctrl;
+
+ PPTPD_ASSERT(_this != NULL);
+ PPTPD_ASSERT(ctrl != NULL);
+
+ nctrl = 0;
+ for (i = 0; i < slist_length(&_this->ctrl_list); i++) {
+ ctrl1 = slist_get(&_this->ctrl_list, i);
+ if (ctrl1 == ctrl) {
+ slist_remove(&_this->ctrl_list, i);
+ break;
+ }
+ }
+ pptp_ctrl_destroy(ctrl);
+
+ PPTPD_DBG((_this, LOG_DEBUG, "Remains %d ctrls", nctrl));
+ if (pptpd_is_shutting_down(_this) && nctrl == 0)
+ // シャットダウン中最後の一人
+ pptpd_stop_immediatly(_this);
+}
+
+/***********************************************************************
+ * その他、ユーティリティ関数
+ ***********************************************************************/
+/** このインスタンスに基づいたラベルから始まるログを記録します。 */
+static void
+pptpd_log(pptpd *_this, int prio, const char *fmt, ...)
+{
+ char logbuf[BUFSIZ];
+ va_list ap;
+
+ PPTPD_ASSERT(_this != NULL);
+ va_start(ap, fmt);
+#ifdef PPTPD_MULITPLE
+ snprintf(logbuf, sizeof(logbuf), "pptpd id=%u %s", _this->id, fmt);
+#else
+ snprintf(logbuf, sizeof(logbuf), "pptpd %s", fmt);
+#endif
+ vlog_printf(prio, logbuf, ap);
+ va_end(ap);
+}
+
+static int
+pptp_call_cmp(const void *a0, const void *b0)
+{
+ return ((uint32_t)a0 - (uint32_t)b0);
+}
+
+static uint32_t
+pptp_call_hash(const void *ctx, int size)
+{
+ return (uint32_t)ctx % size;
+}
+
+/** GREパケットヘッダを文字列として */
+static void
+pptp_gre_header_string(struct pptp_gre_header *grehdr, char *buf, int lbuf)
+{
+ snprintf(buf, lbuf,
+ "[%s%s%s%s%s%s] ver=%d "
+ "protocol_type=%04x payload_length=%d call_id=%d",
+ (grehdr->C != 0)? "C" : "", (grehdr->R != 0)? "R" : "",
+ (grehdr->K != 0)? "K" : "", (grehdr->S != 0)? "S" : "",
+ (grehdr->s != 0)? "s" : "", (grehdr->A != 0)? "A" : "", grehdr->ver,
+ grehdr->protocol_type, grehdr->payload_length, grehdr->call_id);
+}