Logo Search packages:      
Sourcecode: pptpd version File versions  Download package

ctrlpacket.c

/*
 * ctrlpacket.c
 *
 * PPTP Control Message packet reading, formatting and writing.
 *
 * $Id: ctrlpacket.c,v 1.5 2004/04/29 06:58:45 quozl Exp $
 */

#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(unsigned char *packet, unsigned char *rply_packet, ssize_t * rply_size);
static void deal_stop_ctrl_conn(unsigned char *packet, unsigned char *rply_packet, ssize_t * rply_size);
static void deal_out_call(unsigned char *packet, unsigned char *rply_packet, ssize_t * rply_size);
static void deal_echo(unsigned char *packet, unsigned char *rply_packet, ssize_t * rply_size);
static void deal_call_clr(unsigned char *packet, unsigned char *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, unsigned char *packet, unsigned char *rply_packet, ssize_t * rply_size)
{

      size_t bytes_read;
      int pptp_ctrl_type;     /* 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.
 */
size_t send_pptp_packet(int clientFd, unsigned char *packet, size_t packet_size)
{

      size_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 %d bytes to the client.", 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 */


      /* 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=%d): %s", 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=%d,length=%d): %s", 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(unsigned char *packet, unsigned char *rply_packet, ssize_t * rply_size)
{
      struct pptp_start_ctrl_conn_rqst *start_ctrl_conn_rqst;
      struct pptp_start_ctrl_conn_rply start_ctrl_conn_rply;

      start_ctrl_conn_rqst = (struct pptp_start_ctrl_conn_rqst *) packet;

      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);
      bzero(start_ctrl_conn_rply.hostname, MAX_HOSTNAME_SIZE);
      strncpy(start_ctrl_conn_rply.hostname, PPTP_HOSTNAME, MAX_HOSTNAME_SIZE);
      bzero(start_ctrl_conn_rply.vendor, MAX_VENDOR_SIZE);
      strncpy(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(unsigned char *packet, unsigned char *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, unsigned char *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, unsigned char *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, unsigned char *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)
            syslog(LOG_ERR, "CTRL: Ignored a SET LINK INFO packet with real ACCMs!");
      else if(pptpctrl_debug)
            syslog(LOG_DEBUG, "CTRL: Got a SET LINK INFO packet with standard ACCMs");
}

void make_echo_req_packet(unsigned char *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(unsigned char *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(unsigned char *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

Generated by  Doxygen 1.6.0   Back to index