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