diff options
author | YASUOKA Masahiko <yasuoka@cvs.openbsd.org> | 2010-01-11 04:20:58 +0000 |
---|---|---|
committer | YASUOKA Masahiko <yasuoka@cvs.openbsd.org> | 2010-01-11 04:20:58 +0000 |
commit | f24f75f44d582e005fed41d187261a034bb7628a (patch) | |
tree | c2b790f0da2c27916ed9341222df263ee87ec950 /usr.sbin/npppd/pptp | |
parent | 1087250c10f476d6aedd9c44ca4ba96ce45792f6 (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.h | 423 | ||||
-rw-r--r-- | usr.sbin/npppd/pptp/pptp_call.c | 852 | ||||
-rw-r--r-- | usr.sbin/npppd/pptp/pptp_ctrl.c | 1168 | ||||
-rw-r--r-- | usr.sbin/npppd/pptp/pptp_local.h | 177 | ||||
-rw-r--r-- | usr.sbin/npppd/pptp/pptp_subr.c | 144 | ||||
-rw-r--r-- | usr.sbin/npppd/pptp/pptp_subr.h | 50 | ||||
-rw-r--r-- | usr.sbin/npppd/pptp/pptpd.c | 1151 |
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); +} |