Viewing file:
call.c (18.26 KB) -rw-rw-r--Select action/file-type:

(
+) |

(
+) |

(
+) |
Code (
+) |
Session (
+) |

(
+) |
SDB (
+) |

(
+) |

(
+) |

(
+) |

(
+) |

(
+) |
/*
* Layer Two Tunnelling Protocol Daemon
* Copyright (C) 1998 Adtran, Inc.
* Copyright (C) 2002 Jeff McAdams
*
* Mark Spencer
*
* This software is distributed under the terms
* of the GPL, which you should have received
* along with this source.
*
* Handle a call as a separate thread
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <termios.h>
#include "l2tp.h"
#ifdef USE_KERNEL
#include <sys/ioctl.h>
#endif
struct buffer *new_payload (struct sockaddr_in peer)
{
struct buffer *tmp = new_buf (MAX_RECV_SIZE);
if (!tmp)
return NULL;
tmp->peer = peer;
tmp->start += sizeof (struct payload_hdr);
tmp->len = 0;
return tmp;
}
inline void recycle_payload (struct buffer *buf, struct sockaddr_in peer)
{
buf->start = buf->rstart + sizeof (struct payload_hdr);
buf->len = 0;
buf->peer = peer;
}
void add_payload_hdr (struct tunnel *t, struct call *c, struct buffer *buf)
{
struct payload_hdr *p;
buf->start -= sizeof (struct payload_hdr);
buf->len += sizeof (struct payload_hdr);
/* Account for no offset */
buf->start += 4;
buf->len -= 4;
if (!c->fbit && !c->ourfbit)
{
/* Forget about Ns and Nr fields then */
buf->start += 4;
buf->len -= 4;
}
if (!c->lbit)
{
/* Forget about specifying the length */
buf->start += 2;
buf->len -= 2;
}
p = (struct payload_hdr *) buf->start;
/* p->ver = htons(c->lbit | c->rbit | c->fbit | c->ourfbit | VER_L2TP); */
p->ver = htons (c->lbit | c->fbit | c->ourfbit | VER_L2TP);
if (c->lbit)
{
p->length = htons ((_u16) buf->len);
}
else
{
p = (struct payload_hdr *) (((char *) p) - 2);
}
p->tid = htons (t->tid);
p->cid = htons (c->cid);
if (c->fbit || c->ourfbit)
{
p->Ns = htons (c->data_seq_num);
p->Nr = htons (c->data_rec_seq_num);
}
c->data_seq_num++;
/* c->rbit=0; */
}
int read_packet (struct buffer *buf, int fd, int convert)
{
unsigned char c;
unsigned char escape = 0;
unsigned char *p;
static unsigned char rbuf[MAX_RECV_SIZE];
static int pos = 0;
static int max = 0;
int res;
int errors = 0;
/* Read a packet, doing async->sync conversion if necessary */
p = buf->start;
while (1)
{
if (pos >= max)
{
max = read (fd, rbuf, sizeof (rbuf));
res = max;
pos = 0;
}
else
{
res = 1;
}
c = rbuf[pos++];
if (res < 1)
{
if (res == 0)
{
/*
* Hmm.. Nothing to read. It happens
*/
return 0;
/* } else if ((errno == EINTR ) || (errno == EAGAIN)) { */
}
else if ((errno == EIO) || (errno == EINTR) || (errno == EAGAIN))
{
/*
* Oops, we were interrupted!
* Or, we ran out of data too soon
* anyway, we discared whatever it is we
* have
*/
return 0;
}
errors++;
log (LOG_DEBUG, "%s: Error %d (%s)\n", __FUNCTION__, errno,
strerror (errno));
if (errors > 10)
{
log (LOG_DEBUG,
"%s: Too many errors. Declaring call dead.\n",
__FUNCTION__);
return -errno;
}
continue;
}
switch (c)
{
case PPP_FLAG:
if (escape)
{
log (LOG_DEBUG, "%s: got an escaped PPP_FLAG\n",
__FUNCTION__);
return -EINVAL;
}
if (convert)
{
if (!buf->len)
break;
/* Drop the FCS */
buf->len -= 2;
}
else
{
if (buf->len < buf->maxlen)
{
*p = c;
p++;
buf->len++;
}
}
return buf->len;
case PPP_ESCAPE:
escape = PPP_TRANS;
if (convert)
break;
default:
if (convert)
c ^= escape;
escape = 0;
if (buf->len < buf->maxlen)
{
*p = c;
p++;
buf->len++;
break;
};
log (LOG_WARN, "%s: read overrun\n", __FUNCTION__);
return -EINVAL;
}
}
/* I should never get here */
log (LOG_WARN, "%s: You should not see this message. If you do, please
enter a bug report at http://sourceforge.net/projects/l2tpd", __FUNCTION__);
return -EINVAL;
}
void call_close (struct call *c)
{
struct buffer *buf;
struct schedule_entry *se, *ose;
struct call *tmp, *tmp2;
if (!c || !c->container)
{
log (LOG_DEBUG, "%s: called on null call or containerless call\n",
__FUNCTION__);
return;
}
if (c == c->container->self)
{
/*
* We're actually closing the
* entire tunnel
*/
/* First deschedule any remaining packet transmissions
for this tunnel. That means Hello's and any reminaing
packets scheduled for transmission. This is a very
nasty little piece of code here. */
se = events;
ose = NULL;
while (se)
{
if ((((struct buffer *) se->data)->tunnel == c->container)
|| ((struct tunnel *) se->data == c->container))
{
#ifdef DEBUG_CLOSE
log (LOG_DEBUG, "%s: Descheduling event\n", __FUNCTION__);
#endif
if (ose)
{
ose->next = se->next;
if ((struct tunnel *) se->data != c->container)
toss ((struct buffer *) (se->data));
free (se);
se = ose->next;
}
else
{
events = se->next;
if ((struct tunnel *) se->data != c->container)
toss ((struct buffer *) (se->data));
free (se);
se = events;
}
}
else
{
ose = se;
se = se->next;
}
}
if (c->closing)
{
/* Really close this tunnel, as our
StopCCN has been ack'd */
#ifdef DEBUG_CLOSE
log (LOG_DEBUG, "%s: Actually closing tunnel %d\n", __FUNCTION__,
c->container->ourtid);
#endif
#ifdef USE_KERNEL
if (kernel_support)
ioctl (server_socket, L2TPIOCDELTUNNEL, c->container->ourtid);
#endif
destroy_tunnel (c->container);
return;
}
/*
* We need to close, but need to provide reliable delivery
* of the final StopCCN. We record our state to know when
* we have actually received an ACK on our StopCCN
*/
c->closeSs = c->container->control_seq_num;
buf = new_outgoing (c->container);
add_message_type_avp (buf, StopCCN);
if (c->container->hbit)
{
mk_challenge (c->container->chal_them.vector, VECTOR_SIZE);
add_randvect_avp (buf, c->container->chal_them.vector,
VECTOR_SIZE);
}
add_tunnelid_avp (buf, c->container->ourtid);
if (c->result < 0)
c->result = RESULT_CLEAR;
if (c->error < 0)
c->error = 0;
add_result_code_avp (buf, c->result, c->error, c->errormsg,
strlen (c->errormsg));
add_control_hdr (c->container, c, buf);
if (packet_dump)
do_packet_dump (buf);
#ifdef DEBUG_CLOSE
log (LOG_DEBUG, "%s: enqueing close message for tunnel\n",
__FUNCTION__);
#endif
control_xmit (buf);
/*
* We also need to stop all traffic on any calls contained
* within us.
*/
tmp = c->container->call_head;
while (tmp)
{
tmp2 = tmp->next;
tmp->needclose = 0;
tmp->closing = -1;
call_close (tmp);
tmp = tmp2;
}
log (LOG_LOG,
"%s : Connection %d closed to %s, port %d (%s)\n", __FUNCTION__,
c->container->tid,
IPADDY (c->container->peer.sin_addr),
ntohs (c->container->peer.sin_port), c->errormsg);
}
else
{
/*
* Just close a call
*/
#ifdef USE_KERNEL
struct l2tp_call_opts co;
#endif
if (c->zlb_xmit)
deschedule (c->zlb_xmit);
/* if (c->dethrottle) deschedule(c->dethrottle); */
if (c->closing)
{
#ifdef DEBUG_CLOSE
log (LOG_DEBUG, "%s: Actually closing call %d\n", __FUNCTION__,
c->ourcid);
#endif
destroy_call (c);
return;
}
#ifdef USE_KERNEL
if (kernel_support)
{
co.ourtid = c->container->ourtid;
co.ourcid = c->ourcid;
ioctl (server_socket, L2TPIOCGETCALLOPTS, &co);
co.flags = co.flags & ~L2TP_FLAG_CALL_UP;
ioctl (server_socket, L2TPIOCSETCALLOPTS, &co);
}
#endif
c->closeSs = c->container->control_seq_num;
buf = new_outgoing (c->container);
add_message_type_avp (buf, CDN);
if (c->container->hbit)
{
mk_challenge (c->container->chal_them.vector, VECTOR_SIZE);
add_randvect_avp (buf, c->container->chal_them.vector,
VECTOR_SIZE);
}
if (c->result < 0)
c->result = RESULT_CLEAR;
if (c->error < 0)
c->error = 0;
add_result_code_avp (buf, c->result, c->error, c->errormsg,
strlen (c->errormsg));
#ifdef TEST_HIDDEN
add_callid_avp (buf, c->ourcid, c->container);
#else
add_callid_avp (buf, c->ourcid);
#endif
add_control_hdr (c->container, c, buf);
if (packet_dump)
do_packet_dump (buf);
#ifdef DEBUG_CLOSE
log (LOG_DEBUG, "%s: enqueuing close message for call %d\n",
__FUNCTION__, c->ourcid);
#endif
control_xmit (buf);
log (LOG_LOG, "%s: Call %d to %s disconnected\n", __FUNCTION__,
c->ourcid, IPADDY (c->container->peer.sin_addr));
}
/*
* Note that we're in the process of closing now
*/
c->closing = -1;
}
void destroy_call (struct call *c)
{
/*
* Here, we unconditionally destroy a call.
*/
struct call *p;
struct timeval tv;
pid_t pid;
/*
* Close the tty
*/
if (c->fd > 0)
close (c->fd);
/* if (c->dethrottle) deschedule(c->dethrottle); */
if (c->zlb_xmit)
deschedule (c->zlb_xmit);
#ifdef IP_ALLOCATION
if (c->addr)
unreserve_addr (c->addr);
#endif
/*
* Kill off pppd and wait for it to
* return to us. This should only be called
* in rare cases if pppd hasn't already died
* voluntarily
*/
pid = c->pppd;
if (pid)
{
/* Set c->pppd to zero to prevent recursion with child_handler */
c->pppd = 0;
kill (pid, SIGTERM);
waitpid (pid, NULL, 0);
}
if (c->container)
{
#ifdef USE_KERNEL
if (kernel_support)
ioctl (server_socket, L2TPIOCDELCALL,
(c->container->ourtid << 16) | (c->ourcid));
#endif
p = c->container->call_head;
/*
* Remove us from the call list, although
* we might not actually be there
*/
if (p)
{
if (p == c)
{
c->container->call_head = c->next;
c->container->count--;
}
else
{
while (p->next && (p->next != c))
p = p->next;
if (p->next)
{
p->next = c->next;
c->container->count--;
}
}
}
}
if (c->lac)
{
c->lac->c = NULL;
if (c->lac->redial && (c->lac->rtimeout > 0) && !c->lac->rsched &&
c->lac->active)
{
#ifdef DEBUG_MAGIC
log (LOG_LOG, "%s: Will redial in %d seconds\n", __FUNCTION__,
c->lac->rtimeout);
#endif
tv.tv_sec = c->lac->rtimeout;
tv.tv_usec = 0;
c->lac->rsched = schedule (tv, magic_lac_dial, c->lac);
}
}
free (c);
}
struct call *new_call (struct tunnel *parent)
{
char entropy_buf[2] = "\0";
struct call *tmp = malloc (sizeof (struct call));
if (!tmp)
return NULL;
tmp->tx_pkts = 0;
tmp->rx_pkts = 0;
tmp->tx_bytes = 0;
tmp->rx_bytes = 0;
tmp->zlb_xmit = NULL;
/* tmp->throttle = 0; */
/* tmp->dethrottle=NULL; */
tmp->prx = 0;
/* tmp->rbit = 0; */
tmp->msgtype = 0;
/* tmp->timeout = 0; */
tmp->data_seq_num = 0;
tmp->data_rec_seq_num = 0;
tmp->pLr = -1;
tmp->nego = 0;
tmp->debug = 0;
tmp->seq_reqd = 0;
tmp->state = 0; /* Nothing so far */
if (parent->self)
{
#ifndef TESTING
#ifdef USE_KERNEL
if (kernel_support)
tmp->ourcid =
ioctl (server_socket, L2TPIOCADDCALL, parent->ourtid << 16);
else
#endif
/* while(get_call(parent->ourtid, (tmp->ourcid = (rand() && 0xFFFF)),0,0)); */
/* FIXME: What about possibility of multiple random #'s??? */
/* tmp->ourcid = (rand () & 0xFFFF); */
get_entropy(entropy_buf, 2);
{
int *temp;
temp = (int *)entropy_buf;
tmp->ourcid = *temp & 0xFFFF;
#ifdef DEBUG_ENTROPY
log(LOG_DEBUG, "ourcid = %u, entropy_buf = %hx\n", tmp->ourcid, *temp);
#endif
}
#else
tmp->ourcid = 0x6227;
#endif
}
tmp->dialed[0] = 0;
tmp->dialing[0] = 0;
tmp->subaddy[0] = 0;
tmp->physchan = -1;
tmp->serno = 0;
tmp->bearer = -1;
tmp->cid = -1;
tmp->qcid = -1;
tmp->container = parent;
/* tmp->rws = -1; */
tmp->fd = -1;
tmp->oldptyconf = malloc (sizeof (struct termios));
tmp->pnu = 0;
tmp->cnu = 0;
tmp->needclose = 0;
tmp->closing = 0;
tmp->die = 0;
tmp->pppd = 0;
tmp->error = -1;
tmp->result = -1;
tmp->errormsg[0] = 0;
tmp->fbit = 0;
tmp->cid = 0;
tmp->lbit = 0;
/* Inherit LAC and LNS from parent */
tmp->lns = parent->lns;
tmp->lac = parent->lac;
tmp->addr = 0;
/* tmp->ourrws = DEFAULT_RWS_SIZE; */
/* if (tmp->ourrws >= 0)
tmp->ourfbit = FBIT;
else */
tmp->ourfbit = 0; /* initialize to 0 since we don't actually use this
value at this point anywhere in the code (I don't
think) We might just be able to remove it completely */
tmp->dial_no[0] = '\0'; /* jz: dialing number for outgoing call */
return tmp;
}
struct call *get_tunnel (int tunnel, unsigned int addr, int port)
{
struct tunnel *st;
if (tunnel)
{
st = tunnels.head;
while (st)
{
if (st->ourtid == tunnel)
{
return st->self;
}
st = st->next;
}
}
return NULL;
}
struct call *get_call (int tunnel, int call, unsigned int addr, int port)
{
/*
* Figure out which call struct should handle this.
* If we have tunnel and call ID's then they are unique.
* Otherwise, if the tunnel is 0, look for an existing connection
* or create a new tunnel.
*/
struct tunnel *st;
struct call *sc;
if (tunnel)
{
st = tunnels.head;
while (st)
{
if (st->ourtid == tunnel)
{
if (call)
{
sc = st->call_head;
while (sc)
{
if (sc->ourcid == call)
return sc;
sc = sc->next;
}
log (LOG_DEBUG, "%s: can't find call %d in tunnel %d\n",
__FUNCTION__, call, tunnel);
return NULL;
}
else
{
return st->self;
}
}
st = st->next;
}
log (LOG_DEBUG, "%s:can't find tunnel %d\n", __FUNCTION__, tunnel);
return NULL;
}
else
{
#ifdef USE_KERNEL
struct l2tp_tunnel_opts to;
#endif
/* You can't specify a call number if you haven't specified
a tunnel silly! */
if (call)
{
log (LOG_WARN,
"%s: call ID specified, but no tunnel ID specified. tossing.\n",
__FUNCTION__);
return NULL;
}
/*
* Well, nothing appropriate... Let's add a new tunnel, if
* we are not at capacity.
*/
if (debug_tunnel)
{
log (LOG_DEBUG,
"%s: allocating new tunnel for host %s, port %d.\n",
__FUNCTION__, IPADDY (addr), ntohs (port));
}
if (!(st = new_tunnel ()))
{
log (LOG_WARN,
"%s: unable to allocate new tunnel for host %s, port %d.\n",
__FUNCTION__, IPADDY (addr), ntohs (port));
return NULL;
};
st->peer.sin_family = AF_INET;
st->peer.sin_port = port;
bcopy (&addr, &st->peer.sin_addr, sizeof (addr));
#ifdef USE_KERNEL
if (kernel_support)
{
/* Update kernel as to peer's location */
to.ourtid = st->ourtid;
ioctl (server_socket, L2TPIOCGETTUNOPTS, &to);
bcopy (&st->peer, &to.peer, sizeof (st->peer));
to.addrlen = sizeof (st->peer);
ioctl (server_socket, L2TPIOCSETTUNOPTS, &to);
}
#endif
st->next = tunnels.head;
tunnels.head = st;
tunnels.count++;
return st->self;
}
}