123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706 |
- /*
- * ctrlpacket.c
- *
- * PPTP Control Message packet reading, formatting and writing.
- */
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #if HAVE_SYSLOG_H
- #include <syslog.h>
- #else
- #include "our_syslog.h"
- #endif
- #include <signal.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <time.h>
- #include <sys/time.h>
- #include <netinet/in.h>
- #include <unistd.h>
- #include <string.h>
- #include <errno.h>
- #include "pptpdefs.h"
- #include "pptpctrl.h"
- #include "ctrlpacket.h"
- #ifndef HAVE_STRERROR
- #include "compat.h"
- #endif
- /* Local function prototypes */
- static ssize_t read_pptp_header(int clientFd, unsigned char *packet, int *ctrl_message_type);
- static void deal_start_ctrl_conn(void *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size);
- static void deal_stop_ctrl_conn(void *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size);
- static void deal_out_call(unsigned char *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size);
- static void deal_echo(unsigned char *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size);
- static void deal_call_clr(unsigned char *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size);
- static void deal_set_link_info(unsigned char *packet);
- static u_int16_t getcall();
- static u_int16_t freecall();
- #if notyet
- static int make_out_call_rqst(unsigned char *rply_packet, ssize_t * rply_size);
- #endif
- /*
- * read_pptp_packet
- *
- * Sees if a packet can be read and if so what type of packet it is. The
- * method then calls the appropriate function to examine the details of the
- * packet and form a suitable reply packet.
- *
- * args: clientFd (IN) - Client socket to read from.
- * packet (OUT) - Packet read from the client.
- * rply_packet (OUT) - Reply packet for the client.
- * rply_size (OUT) - Size of the reply packet.
- *
- * retn: PPTP control message type of the packet on success.
- * -1 on retryable error.
- * 0 on error to abort on.
- */
- int read_pptp_packet(int clientFd, void *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
- {
- ssize_t bytes_read;
- int pptp_ctrl_type = 0; /* Control Message Type */
- /* read a packet and parse header */
- if ((bytes_read = read_pptp_header(clientFd, packet, &pptp_ctrl_type)) <= 0) {
- /* error reading packet */
- syslog(LOG_ERR, "CTRL: couldn't read packet header (%s)", bytes_read ? "retry" : "exit");
- return bytes_read;
- }
- /* launch appropriate method to form suitable reply to the packet */
- switch (pptp_ctrl_type) {
- case START_CTRL_CONN_RQST: /* Start Control Connection Request */
- deal_start_ctrl_conn(packet, rply_packet, rply_size);
- break;
- case STOP_CTRL_CONN_RQST:
- deal_stop_ctrl_conn(packet, rply_packet, rply_size);
- break;
- case OUT_CALL_RQST: /* Outgoing Call Request */
- deal_out_call(packet, rply_packet, rply_size);
- break;
- case ECHO_RQST: /* Echo Request */
- deal_echo(packet, rply_packet, rply_size);
- break;
- case CALL_CLR_RQST: /* Call Clear Request (Disconnect Request) */
- deal_call_clr(packet, rply_packet, rply_size);
- break;
- case SET_LINK_INFO: /* Set Link Info */
- /* no reply packet but process it */
- deal_set_link_info(packet);
- break;
- case ECHO_RPLY: /* Echo Reply */
- case STOP_CTRL_CONN_RPLY: /* Stop Control Connection Reply */
- case CALL_DISCONN_NTFY: /* Call Disconnect Notify */
- /* no reply packet */
- break;
- default:
- syslog(LOG_ERR, "CTRL: PPTP Control Message type %d not supported.", pptp_ctrl_type);
- pptp_ctrl_type = -1;
- }
- return pptp_ctrl_type;
- }
- /*
- * send_pptp_packet
- *
- * Sends a PPTP packet to a file descriptor.
- *
- * args: clientFd (IN) - file descriptor to write the packet to.
- * packet (IN) - the packet data to write.
- * packet_size (IN) - the packet size.
- *
- * retn: Number of bytes written on success.
- * -1 on write failure.
- */
- ssize_t send_pptp_packet(int clientFd, void *packet, size_t packet_size)
- {
- ssize_t bytes_written;
- if ((bytes_written = write(clientFd, packet, packet_size)) == -1) {
- /* write failed */
- syslog(LOG_ERR, "CTRL: Couldn't write packet to client.");
- return -1;
- } else {
- /* debugging */
- if (pptpctrl_debug) {
- syslog(LOG_DEBUG, "CTRL: I wrote %lu bytes to the client.", (unsigned long) packet_size);
- syslog(LOG_DEBUG, "CTRL: Sent packet to client");
- }
- return bytes_written;
- }
- }
- /*
- * ignoreErrno
- *
- * Check if an errno represents a read error which should be ignored, and
- * put back to be select()ed on again later.
- *
- * Very similar to the function in Squid by Duane Wessels (under GPL).
- *
- * args: an errno value
- *
- * retn: 1 if the error is unimportant
- * 0 if the error is important
- */
- static int ignoreErrno(int ierrno) {
- switch (ierrno) {
- case EAGAIN: /* nothing to read */
- case EINTR: /* signal received */
- #ifdef ERESTART
- #if ERESTART != EINTR
- case ERESTART: /* signal received, should restart syscall */
- #endif
- #endif
- #if EWOULDBLOCK != EAGAIN
- case EWOULDBLOCK: /* shouldn't get this one but anyway, just in case */
- #endif
- return 1;
- default:
- return 0;
- }
- }
- /*
- * read_pptp_header
- *
- * Reads a packet from a file descriptor and determines whether it is a
- * valid PPTP Control Message. If a valid PPTP Control Message is detected
- * it extracts the Control Message type from the packet header.
- *
- * args: clientFd (IN) - Clients file descriptor.
- * packet (OUT) - Packet we read from the client.
- * pptp_ctrl_type (OUT) - PPTP Control Message type of the packet.
- *
- * retn: Number of bytes read on success.
- * -1 on retryable error.
- * 0 on error to exit on.
- */
- ssize_t read_pptp_header(int clientFd, unsigned char *packet, int *pptp_ctrl_type)
- {
- ssize_t bytes_ttl, bytes_this; /* quantities read (total and this read) */
- u_int16_t length; /* length of this packet */
- struct pptp_header *header; /* the received header */
- static char *buffer = NULL; /* buffer between calls */
- static int buffered = 0; /* size of buffer */
- *pptp_ctrl_type = 0; /* initialise return arg */
- /* read any previously buffered data */
- if (buffered) {
- memcpy(packet, buffer, buffered);
- free(buffer);
- buffer = NULL;
- bytes_ttl = buffered;
- buffered = 0;
- if (pptpctrl_debug)
- syslog(LOG_DEBUG, "CTRL: Read in previous incomplete ctrl packet");
- } else
- bytes_ttl = 0;
- /* try and get the length in */
- if (bytes_ttl < 2) {
- bytes_this = read(clientFd, packet + bytes_ttl, 2 - bytes_ttl);
- switch (bytes_this) {
- case -1:
- if (ignoreErrno(errno)) {
- /* re-tryable error, re-buffer and return */
- if (bytes_ttl) {
- buffered = bytes_ttl;
- buffer = malloc(bytes_ttl);
- if (!buffer)
- return(0);
- memcpy(buffer, packet, bytes_ttl);
- }
- syslog(LOG_ERR, "CTRL: Error reading ctrl packet length (bytes_ttl=%lu): %s", (unsigned long) bytes_ttl, strerror(errno));
- return -1;
- }
- /* FALLTHRU */
- case 0:
- syslog(LOG_ERR, "CTRL: EOF or bad error reading ctrl packet length.");
- return 0;
- default:
- bytes_ttl += bytes_this;
- /* Not enough data to proceed */
- if (bytes_ttl == 1) {
- buffered = bytes_ttl;
- buffer = malloc(bytes_ttl);
- if (!buffer)
- return(0);
- memcpy(buffer, packet, bytes_ttl);
- if (pptpctrl_debug)
- syslog(LOG_DEBUG, "CTRL: Incomplete ctrl packet length, retry later");
- return -1;
- }
- }
- }
- /* OK, we have (at least) the first 2 bytes, and there is data waiting
- *
- * length includes the header, so a length less than 2 is someone
- * trying to hack into us or a badly corrupted packet.
- * Why not require length to be at least 10? Since we later cast
- * packet to struct pptp_header and use at least the 10 first bytes..
- * Thanks to Timo Sirainen for mentioning this.
- */
- length = htons(*(u_int16_t *) packet);
- if (length <= 10 || length > PPTP_MAX_CTRL_PCKT_SIZE) {
- syslog(LOG_ERR, "CTRL: 11 < Control packet (length=%d) < "
- "PPTP_MAX_CTRL_PCKT_SIZE (%d)",
- length, PPTP_MAX_CTRL_PCKT_SIZE);
- /* we loose sync (unless we malloc something big, which isn't a good
- * idea - potential DoS) so we must close connection (draft states that
- * if you loose sync you must close the control connection immediately)
- */
- return 0;
- }
- /* Now read the actual control packet */
- bytes_this = read(clientFd, packet + bytes_ttl, length - bytes_ttl);
- switch (bytes_this) {
- case -1:
- if(ignoreErrno(errno)) {
- /* re-tryable error, re-buffer and return */
- if (bytes_ttl) {
- buffered = bytes_ttl;
- buffer = malloc(bytes_ttl);
- if (!buffer)
- return(0);
- memcpy(buffer, packet, bytes_ttl);
- }
- syslog(LOG_ERR, "CTRL: Error reading ctrl packet (bytes_ttl=%lu,length=%d): %s", (unsigned long) bytes_ttl, length, strerror(errno));
- return -1;
- }
- /* FALLTHRU */
- case 0:
- syslog(LOG_ERR, "CTRL: EOF or bad error reading ctrl packet.");
- return 0;
- default:
- bytes_ttl += bytes_this;
- /* not enough data to proceed */
- if (bytes_ttl != length) {
- buffered = bytes_ttl;
- buffer = malloc(bytes_ttl);
- if (!buffer)
- return(0);
- memcpy(buffer, packet, bytes_ttl);
- if (pptpctrl_debug)
- syslog(LOG_DEBUG, "CTRL: Incomplete ctrl packet, retry later");
- return -1;
- }
- }
- /* We got one :-) */
- /* Cast the packet into the PPTP Control Message format */
- header = (struct pptp_header *) packet;
- /* Packet sanity check on magic cookie */
- if (ntohl(header->magic) != PPTP_MAGIC_COOKIE) {
- /* Oops! Not a valid Control Message */
- syslog(LOG_ERR, "CTRL: Bad magic cookie - lost syncronization, closing control connection.");
- /* draft states loss of syncronization must result in
- * immediate closing of the control connection
- */
- return 0;
- }
- /* Woohoo! Looks like we got a valid PPTP packet */
- *pptp_ctrl_type = (int) (ntohs(header->ctrl_type));
- if (pptpctrl_debug)
- syslog(LOG_DEBUG, "CTRL: Received PPTP Control Message (type: %d)", *pptp_ctrl_type);
- return bytes_ttl;
- }
- /* Macros to use in making response packets */
- #define MAKE_CTRL_HEADER(where, what) \
- where.header.length = htons(sizeof(where)); \
- where.header.pptp_type = htons(PPTP_CTRL_MESSAGE); \
- where.header.magic = htonl(PPTP_MAGIC_COOKIE); \
- where.header.ctrl_type = htons(what); \
- where.header.reserved0 = htons(RESERVED)
- #define COPY_CTRL_PACKET(from, to, size) \
- memcpy(to, &from, ((*size) = sizeof(from)))
- #define DEBUG_PACKET(what) \
- if(pptpctrl_debug) \
- syslog(LOG_DEBUG, "CTRL: Made a " what " packet")
- /*
- * deal_start_ctrl_conn
- *
- * This method 'deals' with a START-CONTROL-CONNECTION-REQUEST. After
- * stripping down the connection request a suitable reply is formed and
- * stored in 'rply_packet' ready for sending.
- *
- * args: packet (IN) - the packet that we have to deal with (should be a
- * START-CONTROL-CONNECTION-REQUEST packet)
- * rply_packet (OUT) - suitable reply to the 'packet' we got.
- * rply_size (OUT) - size of the reply packet
- */
- void deal_start_ctrl_conn(void *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
- {
- struct pptp_start_ctrl_conn_rply start_ctrl_conn_rply;
- MAKE_CTRL_HEADER(start_ctrl_conn_rply, START_CTRL_CONN_RPLY);
- start_ctrl_conn_rply.version = htons(PPTP_VERSION);
- start_ctrl_conn_rply.result_code = CONNECTED;
- start_ctrl_conn_rply.error_code = NO_ERROR;
- start_ctrl_conn_rply.framing_cap = htons(OUR_FRAMING);
- start_ctrl_conn_rply.bearer_cap = htons(OUR_BEARER);
- start_ctrl_conn_rply.max_channels = htons(MAX_CHANNELS);
- start_ctrl_conn_rply.firmware_rev = htons(PPTP_FIRMWARE_VERSION);
- memset(start_ctrl_conn_rply.hostname, 0, MAX_HOSTNAME_SIZE);
- strncpy((char *)start_ctrl_conn_rply.hostname, PPTP_HOSTNAME, MAX_HOSTNAME_SIZE);
- memset(start_ctrl_conn_rply.vendor, 0, MAX_VENDOR_SIZE);
- strncpy((char *)start_ctrl_conn_rply.vendor, PPTP_VENDOR, MAX_VENDOR_SIZE);
- COPY_CTRL_PACKET(start_ctrl_conn_rply, rply_packet, rply_size);
- DEBUG_PACKET("START CTRL CONN RPLY");
- }
- /*
- * deal_stop_ctrl_conn
- *
- * This method response to a STOP-CONTROL-CONNECTION-REQUEST with a
- * STOP-CONTROL-CONNECTION-REPLY.
- */
- void deal_stop_ctrl_conn(void *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
- {
- struct pptp_stop_ctrl_conn_rply stop_ctrl_conn_rply;
- MAKE_CTRL_HEADER(stop_ctrl_conn_rply, STOP_CTRL_CONN_RPLY);
- stop_ctrl_conn_rply.result_code = DISCONNECTED;
- stop_ctrl_conn_rply.error_code = NO_ERROR;
- stop_ctrl_conn_rply.reserved1 = htons(RESERVED);
- COPY_CTRL_PACKET(stop_ctrl_conn_rply, rply_packet, rply_size);
- DEBUG_PACKET("STOP CTRL CONN RPLY");
- }
- /*
- * deal_out_call
- *
- * This method 'deals' with a OUT-GOING-CALL-REQUEST. After
- * stripping down the request a suitable reply is formed and stored in
- * 'rply_packet' ready for sending.
- *
- * args: packet (IN) - the packet that we have to deal with (should be a
- * OUT-GOING-CALL-REQUEST packet)
- * rply_packet (OUT) - suitable reply to the 'packet' we got.
- * rply_size (OUT) - size of the reply packet
- *
- */
- void deal_out_call(unsigned char *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
- {
- u_int16_t pac_call_id;
- struct pptp_out_call_rqst *out_call_rqst;
- struct pptp_out_call_rply out_call_rply;
- out_call_rqst = (struct pptp_out_call_rqst *) packet;
- if ((pac_call_id = getcall()) == htons(-1)) {
- /* XXX should reject call */
- syslog(LOG_ERR, "CTRL: No free Call IDs!");
- pac_call_id = 0;
- }
- MAKE_CTRL_HEADER(out_call_rply, OUT_CALL_RPLY);
- /* call_id is used for ctrl, call_id_peer is used for GRE
- * call_id_peer is what we were sent by the other end in ctrl initilization
- */
- out_call_rply.call_id = pac_call_id;
- out_call_rply.call_id_peer = out_call_rqst->call_id;
- out_call_rply.result_code = CONNECTED;
- out_call_rply.error_code = NO_ERROR;
- out_call_rply.cause_code = NO_ERROR;
- /* maybe limit to pppd speed? but pppd doesn't accept 10Mbps as a speed and yet
- * still performs at over 115200, eg, 60kbyte/sec and higher observed.
- */
- out_call_rply.speed = out_call_rqst->max_bps;
- /* lets match their window size for now... was htons(PCKT_RECV_WINDOW_SIZE)
- */
- out_call_rply.pckt_recv_size = out_call_rqst->pckt_recv_size;
- if(pptpctrl_debug)
- syslog(LOG_DEBUG, "CTRL: Set parameters to %d maxbps, %d window size",
- ntohl(out_call_rply.speed), ntohs(out_call_rply.pckt_recv_size));
- out_call_rply.pckt_delay = htons(PCKT_PROCESS_DELAY);
- out_call_rply.channel_id = htonl(CHANNEL_ID);
- COPY_CTRL_PACKET(out_call_rply, rply_packet, rply_size);
- DEBUG_PACKET("OUT CALL RPLY");
- }
- /*
- * deal_echo
- *
- * This method 'deals' with a ECHO-REQUEST. After stripping down the
- * connection request a suitable reply is formed and stored in
- * 'rply_packet' ready for sending.
- *
- * args: packet (IN) - the packet that we have to deal with (should be a
- * ECHO-REQUEST packet)
- * rply_packet (OUT) - suitable reply to the 'packet' we got.
- * rply_size (OUT) - size of the reply packet
- *
- */
- void deal_echo(unsigned char *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
- {
- struct pptp_echo_rqst *echo_rqst;
- struct pptp_echo_rply echo_rply;
- echo_rqst = (struct pptp_echo_rqst *) packet;
- MAKE_CTRL_HEADER(echo_rply, ECHO_RPLY);
- echo_rply.identifier = echo_rqst->identifier;
- echo_rply.result_code = CONNECTED;
- echo_rply.error_code = NO_ERROR;
- echo_rply.reserved1 = htons(RESERVED);
- COPY_CTRL_PACKET(echo_rply, rply_packet, rply_size);
- DEBUG_PACKET("ECHO RPLY");
- }
- /*
- * deal_call_clr
- *
- * This method 'deals' with a CALL-CLEAR-REQUEST. After stripping down the
- * connection request a suitable reply is formed and stored in
- * 'rply_packet' ready for sending.
- *
- * args: packet (IN) - the packet that we have to deal with (should be a
- * CALL-CLEAR-REQUEST packet)
- * rply_packet (OUT) - suitable reply to the 'packet' we got.
- * rply_size (OUT) - size of the reply packet
- *
- */
- void deal_call_clr(unsigned char *packet, struct pptp_out_call_rply *rply_packet, ssize_t *rply_size)
- {
- struct pptp_call_disconn_ntfy call_disconn_ntfy;
- u_int16_t pac_call_id;
- /* Form a reply
- * The reply packet is a CALL-DISCONECT-NOTIFY
- * In single call mode we don't care what peer's call ID is, so don't even bother looking
- */
- if ((pac_call_id = freecall()) == htons(-1)) {
- /* XXX should return an error */
- syslog(LOG_ERR, "CTRL: Could not free Call ID [call clear]!");
- }
- MAKE_CTRL_HEADER(call_disconn_ntfy, CALL_DISCONN_NTFY);
- call_disconn_ntfy.call_id = pac_call_id;
- call_disconn_ntfy.result_code = CALL_CLEAR_REQUEST; /* disconnected by call_clr_rqst */
- call_disconn_ntfy.error_code = NO_ERROR;
- call_disconn_ntfy.cause_code = htons(NO_ERROR);
- call_disconn_ntfy.reserved1 = htons(RESERVED);
- memset(call_disconn_ntfy.call_stats, 0, 128);
- COPY_CTRL_PACKET(call_disconn_ntfy, rply_packet, rply_size);
- DEBUG_PACKET("CALL DISCONNECT RPLY");
- }
- /*
- * deal_set_link_info
- *
- * @FIXME This function is *not* completed
- *
- * This method 'deals' with a SET-LINK-INFO. After stripping down the
- * connection request a suitable reply is formed and stored in
- * 'rply_packet' ready for sending.
- *
- * args: packet (IN) - the packet that we have to deal with (should be a
- * SET-LINK-INFO packet)
- * rply_packet (OUT) - suitable reply to the 'packet' we got.
- * rply_size (OUT) - size of the reply packet
- *
- */
- void deal_set_link_info(unsigned char *packet)
- {
- struct pptp_set_link_info *set_link_info;
- set_link_info = (struct pptp_set_link_info *) packet;
- if (set_link_info->send_accm != 0xffffffff || set_link_info->recv_accm != 0xffffffff) {
- /* Async-Control-Character-Map (ACCM) are bits that
- show which control characters should be escaped by the
- PPP implementation ... pptpd leaves pppd to negotiate
- that via LCP and does not process SET LINK INFO
- packets ... this is not complaint with the RFC but
- still works. */
- if (pptpctrl_debug)
- syslog(LOG_DEBUG, "CTRL: Ignored a SET LINK INFO packet with real ACCMs! (intentional non-compliance with section 2.15 of RFC 2637, ACCM is negotiated by PPP LCP asyncmap)");
- } else if (pptpctrl_debug)
- syslog(LOG_DEBUG, "CTRL: Got a SET LINK INFO packet with standard ACCMs");
- }
- void make_echo_req_packet(struct pptp_out_call_rply *rply_packet, ssize_t * rply_size, u_int32_t echo_id)
- {
- struct pptp_echo_rqst echo_packet;
- MAKE_CTRL_HEADER(echo_packet, ECHO_RQST);
- echo_packet.identifier = echo_id;
- COPY_CTRL_PACKET(echo_packet, rply_packet, rply_size);
- DEBUG_PACKET("ECHO REQ");
- }
- void make_stop_ctrl_req(struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
- {
- struct pptp_stop_ctrl_conn_rqst stop_ctrl;
- MAKE_CTRL_HEADER(stop_ctrl, STOP_CTRL_CONN_RQST);
- stop_ctrl.reason = GENERAL_STOP_CTRL;
- stop_ctrl.reserved1 = RESERVED;
- stop_ctrl.reserved2 = htons(RESERVED);
- COPY_CTRL_PACKET(stop_ctrl, rply_packet, rply_size);
- DEBUG_PACKET("STOP CTRL REQ");
- }
- void make_call_admin_shutdown(struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
- {
- struct pptp_call_disconn_ntfy call_disconn_ntfy;
- u_int16_t pac_call_id;
- /* Form a reply
- * The reply packet is a CALL-DISCONECT-NOTIFY
- * In single call mode we don't care what peer's call ID is, so don't even bother looking
- */
- if ((pac_call_id = freecall()) == htons(-1)) {
- /* XXX should return an error */
- syslog(LOG_ERR, "CTRL: Could not free Call ID [admin shutdown]!");
- }
- MAKE_CTRL_HEADER(call_disconn_ntfy, CALL_DISCONN_NTFY);
- call_disconn_ntfy.call_id = pac_call_id;
- call_disconn_ntfy.result_code = ADMIN_SHUTDOWN; /* disconnected by admin shutdown */
- call_disconn_ntfy.error_code = NO_ERROR;
- call_disconn_ntfy.cause_code = htons(NO_ERROR);
- call_disconn_ntfy.reserved1 = htons(RESERVED);
- memset(call_disconn_ntfy.call_stats, 0, 128);
- COPY_CTRL_PACKET(call_disconn_ntfy, rply_packet, rply_size);
- DEBUG_PACKET("CALL DISCONNECT RPLY");
- }
- #if PNS_MODE
- /* out of date. really PNS isn't 'trivially different', it's quite different */
- #define C_BITS (sizeof(unsigned int) * 8)
- #define C_SEG(x) (x/C_BITS)
- #define C_BIT(x) ((1U)<<(x%C_BITS))
- static unsigned int activeCalls[(MAX_CALLS / C_BITS) + 1];
- /*
- * get_call_id
- *
- * Assigns a call ID and peer call ID to the session.
- *
- * args: call_id (OUT) - the call ID for the session
- * retn: 0 on success, -1 on failure
- */
- int get_call_id(u_int16_t * loc)
- {
- for (i = 0; i < MAX_CALLS; i++) {
- if (!(activeCalls[C_SEG(i)] & C_BIT(i))) {
- activeCalls[C_SEG(i)] |= C_BIT(i);
- *loc = i;
- return 0;
- }
- }
- return -1;
- }
- /*
- * free_call_id
- *
- * args: call_id (IN) - the call ID for a terminated session
- * retn: 0 on success, -1 on failure
- */
- int free_call_id(u_int16_t call_id)
- {
- if (!(activeCalls[C_SEG(i)] & C_BIT(i)))
- return -1;
- activeCalls[C_SEG(i)] &= ~C_BIT(i);
- return 0;
- }
- #else
- static int _pac_call_id;
- static u_int16_t _pac_init = 0;
- /*
- * getcall
- *
- * Assigns a call ID to the session and stores/returns it
- *
- * we only permit one call at a time, so the chance of wrapping 65k on one
- * control connection is zero to none...
- */
- u_int16_t getcall()
- {
- static u_int16_t i = 0;
- extern u_int16_t unique_call_id;
- /* Start with a random Call ID. This is to allocate unique
- * Call ID's across multiple TCP PPTP connections. In this
- * way remote clients masqueraded by a firewall will put
- * unique peer call ID's into GRE packets that will have the
- * same source IP address of the firewall. */
- if (!i) {
- if (unique_call_id == 0xFFFF) {
- struct timeval tv;
- if (gettimeofday(&tv, NULL) == 0) {
- i = ((tv.tv_sec & 0x0FFF) << 4) +
- (tv.tv_usec >> 16);
- }
- } else {
- i = unique_call_id;
- }
- }
- if(!_pac_init) {
- _pac_call_id = htons(-1);
- _pac_init = 1;
- }
- if(_pac_call_id != htons(-1))
- syslog(LOG_ERR, "CTRL: Asked to allocate call id when call open, not handled well");
- _pac_call_id = htons(i);
- i++;
- return _pac_call_id;
- }
- /*
- * freecall
- *
- * Notes termination of current call
- *
- * retn: -1 on failure, PAC call ID on success
- */
- u_int16_t freecall()
- {
- u_int16_t ret;
- if(!_pac_init) {
- _pac_call_id = htons(-1);
- _pac_init = 1;
- }
- ret = _pac_call_id;
- if(_pac_call_id == htons(-1))
- syslog(LOG_ERR, "CTRL: Asked to free call when no call open, not handled well");
- _pac_call_id = htons(-1);
- return ret;
- }
- #endif
|