1
0
This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
TP-Link_Archer-XR500v/BBA1.5_platform/apps/public/xl2tpd-1.1.12/call.c
2024-07-22 01:58:46 -03:00

693 lines
18 KiB
C

/*
* 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"
#include "ipsecmast.h"
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 there was a short read, then see what is about */
if (res < 1)
{
if (res == 0)
{
/*
* Hmm.. Nothing to read. It happens
*/
return 0;
}
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++;
l2tp_log (LOG_DEBUG, "%s: Error %d (%s)\n", __FUNCTION__, errno,
strerror (errno));
if (errors > 10)
{
l2tp_log (LOG_DEBUG,
"%s: Too many errors. Declaring call dead.\n",
__FUNCTION__);
pos=0;
max=0;
return -errno;
}
continue;
}
switch (c)
{
case PPP_FLAG:
if (escape)
{
l2tp_log (LOG_DEBUG, "%s: got an escaped PPP_FLAG\n",
__FUNCTION__);
pos=0;
max=0;
return -EINVAL;
}
if (convert)
{
if (buf->len >= 2) {
/* must be the end, drop the FCS */
buf->len -= 2;
}
else if (buf->len == 1) {
/* Do nothing, just return the single character*/
}
else {
/* if the buffer is empty, then we have the beginning
* of a packet, not the end
*/
break;
}
}
else
{
/* if there is space, then insert the byte */
if (buf->len < buf->maxlen)
{
*p = c;
p++;
buf->len++;
}
}
/* return what we have now */
return buf->len;
case PPP_ESCAPE:
escape = PPP_TRANS;
if (convert)
break;
/* fall through */
default:
if (convert)
c ^= escape;
escape = 0;
if (buf->len < buf->maxlen)
{
*p = c;
p++;
buf->len++;
break;
}
l2tp_log (LOG_WARNING, "%s: read overrun\n", __FUNCTION__);
pos=0;
max=0;
return -EINVAL;
}
}
/* I should never get here */
l2tp_log (LOG_WARNING, "%s: You should not see this message. If you do, please enter "
"a bug report at http://lists.xelerance.com/mailman/listinfo/xl2tpd", __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)
{
l2tp_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
l2tp_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
l2tp_log (LOG_DEBUG, "%s: Actually closing tunnel %d\n", __FUNCTION__,
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 (gconfig.packet_dump)
do_packet_dump (buf);
#ifdef DEBUG_CLOSE
l2tp_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;
}
l2tp_log (LOG_INFO,
"Connection %d closed to %s, port %d (%s)\n",
c->container->tid,
IPADDY (c->container->peer.sin_addr),
ntohs (c->container->peer.sin_port), c->errormsg);
}
else
{
/*
* Just close a call
*/
if (c->zlb_xmit)
deschedule (c->zlb_xmit);
/* if (c->dethrottle) deschedule(c->dethrottle); */
if (c->closing)
{
#ifdef DEBUG_CLOSE
l2tp_log (LOG_DEBUG, "%s: Actually closing call %d\n", __FUNCTION__,
c->ourcid);
#endif
destroy_call (c);
return;
}
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 (gconfig.packet_dump)
do_packet_dump (buf);
#ifdef DEBUG_CLOSE
l2tp_log (LOG_DEBUG, "%s: enqueuing close message for call %d\n",
__FUNCTION__, c->ourcid);
#endif
control_xmit (buf);
l2tp_log (LOG_INFO, "%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;
/* There is a bug in some pppd versions where sending a SIGTERM
does not actually seem to kill pppd, and xl2tpd waits indefinately
using waitpid, not accepting any new connections either. Therefor
we now use some more force and send it a SIGKILL instead of SIGTERM.
One confirmed buggy version of pppd is ppp-2.4.2-6.4.RHEL4
See http://bugs.xelerance.com/view.php?id=739
*/
#ifdef TRUST_PPPD_TO_DIE
#ifdef DEBUG_PPPD
l2tp_log (LOG_DEBUG, "Trustingly terminating pppd: sending TERM signal to pid %d\n", pid);
#endif
kill (pid, SIGTERM);
#else
#ifdef DEBUG_PPPD
l2tp_log (LOG_DEBUG, "Untrustingly terminating pppd: sending KILL signal to pid %d\n", pid);
#endif
kill (pid, SIGKILL);
#endif
waitpid (pid, NULL, 0);
#ifdef DEBUG_PPPD
l2tp_log (LOG_DEBUG, "pppd %d successfully terminated\n", pid);
#endif
}
if (c->container)
{
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
l2tp_log (LOG_DEBUG, "Will redial in %d seconds\n",
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)
{
unsigned 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
/* 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);
{
unsigned short *temp;
temp = (unsigned short *)entropy_buf;
tmp->ourcid = *temp & 0xFFFF;
#ifdef DEBUG_ENTROPY
l2tp_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,
IPsecSAref_t refme, IPsecSAref_t refhim)
{
/*
* 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 &&
(gconfig.ipsecsaref==0 ||
(st->refhim == refhim
|| refhim==IPSEC_SAREF_NULL
|| st->refhim==IPSEC_SAREF_NULL)))
{
if (call)
{
sc = st->call_head;
while (sc)
{
/* confirm that this is in fact a call with the right SA! */
if (sc->ourcid == call) return sc;
sc = sc->next;
}
l2tp_log (LOG_DEBUG, "%s: can't find call %d in tunnel %d\n (ref=%d/%d)",
__FUNCTION__, call, tunnel, refme, refhim);
return NULL;
}
else
{
return st->self;
}
}
st = st->next;
}
l2tp_log (LOG_INFO, "Can not find tunnel %u (refhim=%u)\n",
tunnel, refhim);
return NULL;
}
else
{
/* You can't specify a call number if you haven't specified
a tunnel silly! */
if (call)
{
l2tp_log (LOG_WARNING,
"%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 (gconfig.debug_tunnel)
{
l2tp_log (LOG_DEBUG,
"%s: allocating new tunnel for host %s, port %d.\n",
__FUNCTION__, IPADDY (addr), ntohs (port));
}
if (!(st = new_tunnel ()))
{
l2tp_log (LOG_WARNING,
"%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;
st->refme = refme;
st->refhim = refhim;
bcopy (&addr, &st->peer.sin_addr, sizeof (addr));
st->next = tunnels.head;
tunnels.head = st;
tunnels.count++;
return st->self;
}
}