/* $OpenBSD: pptp_call.c,v 1.7 2012/09/18 13:14:08 yasuoka Exp $ */ /*- * 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.7 2012/09/18 13:14:08 yasuoka Exp $ */ /**@file PPTP Call */ /* currently it supports PAC mode only */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_LIBSOCKUTIL #include #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" #include "npppd.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, int); static char * pptp_call_state_string(int); static int pptp_call_ppp_output (npppd_ppp *, unsigned char *, int, int); static void pptp_call_closed_by_ppp (npppd_ppp *); /* 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))) /* round-up division */ #define RUPDIV(n,d) (((n) + ((d) - ((n) % (d)))) / (d)) /* * instance related functions */ pptp_call * pptp_call_create(void) { pptp_call *_this; if ((_this = malloc(sizeof(pptp_call))) == NULL) return NULL; return _this; } 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; } int pptp_call_start(pptp_call *_this) { if (pptp_call_bind_ppp(_this) != 0) return 1; return 0; } 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; } void pptp_call_destroy(pptp_call *_this) { PPTP_CALL_ASSERT(_this != NULL); free(_this); } 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); } /* * PPTP control packet I/O */ 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)); } /* receive Set-Link-Info */ /* XXX: this implementation is only supporting "Sync-Frame", * ACCM (Asynchronous Control Character Map) will be discarded */ 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 /* Some route implementation which has "PPTP pass-through" function * will discard 1723/tcp packet between PAC and PNS, when it recognize * SLI from PAC. (for example, FLASHWAVE by Fujitsu). * * To avoid avobe situation, npppd send well-known SLI only. */ 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 /* Receive Call-Clear-Request */ static int pptp_call_recv_CCR(pptp_call *_this, u_char *pkt, int lpkt) { struct pptp_ccr *ccr; /* check size */ PPTP_CALL_ASSERT(lpkt >= sizeof(struct pptp_ccr)); if (lpkt < sizeof(struct pptp_ccr)) { /* never happen, should KASSERT() here? */ return 1; } ccr = (struct pptp_ccr *)pkt; /* convert byte-order */ ccr->call_id = ntohs(ccr->call_id); pptp_call_log(_this, LOG_INFO, "RecvCCR call_id=%u", ccr->call_id); return 0; } /* Send 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)); } /* Send 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; } /* Receive Outgoing-Call-Request */ static int pptp_call_recv_OCRQ(pptp_call *_this, u_char *pkt, int lpkt) { char logbuf[512]; struct pptp_ocrq *ocrq; /* check size */ 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; /* convert byte-order */ 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 I/O */ /* Reciver packet via 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; int delayed = 0; 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) { /* nothing to do */ } else if (SEQ_LT(ack, _this->snd_una)) { delayed = 1; } 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 */ /* check sequence# */ if (SEQ_LT(seq, _this->rcv_nxt)) { /* delayed delivery? */ if (SEQ_LT(seq, _this->rcv_nxt - PPTP_CALL_DELAY_LIMIT)) { reason = "out of sequence"; goto bad_pkt; } delayed = 1; } else if (SEQ_GE(seq, _this->rcv_nxt + _this->maxwinsz)){ /* MUST Process them */ /* XXX FIXME: if over 4096 packets lost, it can not * fix MPPE state */ 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)); } if (!delayed) { seq++; /* XXX : TODO: should it counts lost packets ppp->ierrors * and update ppp->ierrors counter? */ _this->rcv_nxt = seq; if (SEQ_SUB(seq, _this->rcv_acked) > RUPDIV(_this->winsz, 2)) { /* * Multi-packet acknowledgement. * send ack when it reachs to half of window size */ 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, delayed); 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); } /* output to GRE */ /* flags: fseq: contain SEQ field, fack: contain ACK field */ 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 header */ 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 (PPTP_CTRL_CONF(_this->ctrl)->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 physical layer functions */ /* notify to ppp that the PPTP physical layer is already downed */ static void pptp_call_notify_down(pptp_call *_this) { if (_this->ppp != NULL) ppp_phy_downed(_this->ppp); } /* input packet to ppp */ static void pptp_call_ppp_input(pptp_call *_this, u_char *pkt, int pktlen, int delayed) { 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, delayed ? PPP_IO_FLAGS_DELAYED : 0); if (_this->ppp == NULL) /* ppp is freed */ return; if (rval != 0) { ppp->ierrors++; } else { ppp->ipackets++; ppp->ibytes += pktlen; } } /* it called when ppp outputs packet */ 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; } /* it called when pptp call was closed at 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; /* do this before pptp_call_disconnect() */ _this->ppp = NULL; 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"); } /* bind() for ppp */ static int pptp_call_bind_ppp(pptp_call *_this) { npppd_ppp *ppp; ppp = NULL; if ((ppp = ppp_create()) == NULL) goto fail; PPTP_CALL_ASSERT(_this->ppp == NULL); if (_this->ppp != NULL) return -1; _this->ppp = ppp; ppp->phy_context = _this; ppp->tunnel_type = NPPPD_TUNNEL_PPTP; ppp->send_packet = pptp_call_ppp_output; ppp->phy_close = pptp_call_closed_by_ppp; strlcpy(ppp->phy_label, PPTP_CTRL_LISTENER_TUN_NAME(_this->ctrl), sizeof(ppp->phy_label)); PPTP_CALL_ASSERT(sizeof(ppp->phy_info) >= _this->ctrl->peer.ss_len); memcpy(&ppp->phy_info, &_this->ctrl->peer, MIN(sizeof(ppp->phy_info), _this->ctrl->peer.ss_len)); if (ppp_init(npppd_get_npppd(), ppp) != 0) goto fail; pptp_call_log(_this, LOG_NOTICE, "logtype=PPPBind ppp=%d", ppp->id); ppp_start(ppp); return 0; fail: pptp_call_log(_this, LOG_ERR, "failed binding ppp"); if (ppp != NULL) ppp_destroy(ppp); _this->ppp = NULL; return 1; } /* * utility functions */ /* logging with the label for the instance */ 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_MULTIPLE 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); } /* convert Outgoing-Call-Request packet to strings */ 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); } /* convert Outgoing-Call-Reply packet to strings */ 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"; }