/***********************************************************************
*
* tunnel.c
*
* Functions for manipulating L2TP tunnel objects.
*
* Copyright (C) 2002 Roaring Penguin Software Inc.
*
* This software may be distributed under the terms of the GNU General
* Public License, Version 2, or (at your option) any later version.
*
* LIC: GPL
*
***********************************************************************/

static char const RCSID[] =
"$Id: tunnel.c,v 1.2 2002/09/30 19:45:00 dskoll Exp $";

#include "l2tp.h"
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

/* Hash tables of all tunnels */
static hash_table tunnels_by_my_id;
static hash_table tunnels_by_peer_address;

static uint16_t tunnel_make_tid(void);
static l2tp_tunnel *tunnel_new(EventSelector *es);
static void tunnel_free(l2tp_tunnel *tunnel);
static int tunnel_send_SCCRQ(l2tp_tunnel *tunnel);
static void tunnel_handle_SCCRQ(l2tp_dgram *dgram,
					 EventSelector *es,
					 struct sockaddr_in *from);
static void tunnel_handle_SCCRP(l2tp_tunnel *tunnel,
				l2tp_dgram *dgram);
static void tunnel_handle_SCCCN(l2tp_tunnel *tunnel,
				l2tp_dgram *dgram);
static void tunnel_handle_ICRQ(l2tp_tunnel *tunnel,
			       l2tp_dgram *dgram);
static void tunnel_process_received_datagram(l2tp_tunnel *tunnel,
					     l2tp_dgram *dgram);
static void tunnel_schedule_ack(l2tp_tunnel *tunnel);
static void tunnel_do_ack(EventSelector *es, int fd, unsigned int flags,
			  void *data);
static void tunnel_handle_timeout(EventSelector *es, int fd,
				  unsigned int flags, void *data);
static void tunnel_finally_cleanup(EventSelector *es, int fd,
				   unsigned int flags, void *data);

static void tunnel_do_hello(EventSelector *es, int fd,
			    unsigned int flags, void *data);

static void tunnel_dequeue_acked_packets(l2tp_tunnel *tunnel);

static void tunnel_send_StopCCN(l2tp_tunnel *tunnel,
				int result_code,
				int error_code,
				char const *fmt, ...);
static void tunnel_schedule_destruction(l2tp_tunnel *tunnel);
static int tunnel_set_params(l2tp_tunnel *tunnel, l2tp_dgram *dgram);

static int tunnel_outstanding_frames(l2tp_tunnel *tunnel);
static void tunnel_setup_hello(l2tp_tunnel *tunnel);
static void tunnel_tell_sessions_tunnel_open(l2tp_tunnel *tunnel);
static l2tp_tunnel *tunnel_establish(l2tp_peer *peer, EventSelector *es);
static l2tp_tunnel *tunnel_find_bypeer(struct sockaddr_in addr);

static char *state_names[] = {
    "idle", "wait-ctl-reply", "wait-ctl-conn", "established",
    "received-stop-ccn", "sent-stop-ccn"
};

#define VENDOR_STR "Roaring Penguin Software Inc."

/* Comparison of serial numbers according to RFC 1982 */
#define SERIAL_GT(a, b) \
(((a) > (b) && (a) - (b) < 32768) || ((a) < (b) && (b) - (a) > 32768))

#define SERIAL_LT(a, b) \
(((a) < (b) && (b) - (a) < 32768) || ((a) > (b) && (a) - (b) > 32768))

/**********************************************************************
* %FUNCTION: tunnel_set_state
* %ARGUMENTS:
*  tunnel -- the tunnel
*  state -- new state
* %RETURNS:
*  Nothing
***********************************************************************/
static void
tunnel_set_state(l2tp_tunnel *tunnel, int state)
{
    if (state == tunnel->state) return;
    DBG(l2tp_db(DBG_TUNNEL, "tunnel(%s) state %s -> %s\n",
	   l2tp_debug_tunnel_to_str(tunnel),
	   state_names[tunnel->state],
	   state_names[state]));
    tunnel->state = state;
}

/**********************************************************************
* %FUNCTION: tunnel_find_by_my_id
* %ARGUMENTS:
*  id -- tunnel ID
* %RETURNS:
*  A tunnel whose my_id field equals id, or NULL if no such tunnel
***********************************************************************/
l2tp_tunnel *
l2tp_tunnel_find_by_my_id(uint16_t id)
{
    l2tp_tunnel candidate;
    candidate.my_id = id;
    return hash_find(&tunnels_by_my_id, &candidate);
}

/**********************************************************************
* %FUNCTION: tunnel_find_bypeer
* %ARGUMENTS:
*  addr -- peer address
* %RETURNS:
*  A tunnel whose peer_addr field equals addr, or NULL if no such tunnel
***********************************************************************/
static l2tp_tunnel *
tunnel_find_bypeer(struct sockaddr_in addr)
{
    l2tp_tunnel candidate;
    candidate.peer_addr = addr;
    return hash_find(&tunnels_by_peer_address, &candidate);
}

/**********************************************************************
* %FUNCTION: tunnel_flow_control_stats
* %ARGUMENTS:
*  tunnel -- a tunnel
* %RETURNS:
*  A string representing flow-control info (used for debugging)
***********************************************************************/
static char const *
tunnel_flow_control_stats(l2tp_tunnel *tunnel)
{
    static char buf[256];

    snprintf(buf, sizeof(buf), "rws=%d cwnd=%d ssthresh=%d outstanding=%d",
	     (int) tunnel->peer_rws,
	     tunnel->cwnd,
	     tunnel->ssthresh,
	     tunnel_outstanding_frames(tunnel));
    return buf;
}

/**********************************************************************
* %FUNCTION: tunnel_outstanding_frames
* %ARGUMENTS:
*  tunnel -- a tunnel
* %RETURNS:
*  The number of outstanding (transmitted, but not ack'd) frames.
***********************************************************************/
static int
tunnel_outstanding_frames(l2tp_tunnel *tunnel)
{
    int Ns = (int) tunnel->Ns_on_wire;
    int Nr = (int) tunnel->peer_Nr;
    if (Ns >= Nr) return Ns - Nr;
    return 65536 - Nr + Ns;
}

/**********************************************************************
* %FUNCTION: tunnel_make_tid
* %ARGUMENTS:
*  None
* %RETURNS:
*  An unused, random tunnel ID.
***********************************************************************/
static uint16_t tunnel_make_tid(void)
{
    uint16_t id;
    for(;;) {
	L2TP_RANDOM_FILL(id);
	if (!id) continue;
	if (!l2tp_tunnel_find_by_my_id(id)) return id;
    }
}

/**********************************************************************
* %FUNCTION: tunnel_enqueue_dgram
* %ARGUMENTS:
*  tunnel -- the tunnel
*  dgram -- an L2TP datagram to place at the tail of the transmit queue
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Adds datagram to end of transmit queue.
***********************************************************************/
static void
tunnel_enqueue_dgram(l2tp_tunnel *tunnel,
		     l2tp_dgram  *dgram)
{
    dgram->next = NULL;
    if (tunnel->xmit_queue_tail) {
	tunnel->xmit_queue_tail->next = dgram;
	tunnel->xmit_queue_tail = dgram;
    } else {
	tunnel->xmit_queue_head = dgram;
	tunnel->xmit_queue_tail = dgram;
    }
    if (!tunnel->xmit_new_dgrams) {
	tunnel->xmit_new_dgrams = dgram;
    }

    dgram->Ns = tunnel->Ns;
    tunnel->Ns++;
    DBG(l2tp_db(DBG_FLOW, "tunnel_enqueue_dgram(%s, %s) %s\n",
	   l2tp_debug_tunnel_to_str(tunnel),
	   l2tp_debug_message_type_to_str(dgram->msg_type),
	   tunnel_flow_control_stats(tunnel)));

}

/**********************************************************************
* %FUNCTION: tunnel_dequeue_head
* %ARGUMENTS:
*  tunnel -- the tunnel
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Dequeues datagram from head of transmit queue and frees it
***********************************************************************/
static void
tunnel_dequeue_head(l2tp_tunnel *tunnel)
{
    l2tp_dgram *dgram = tunnel->xmit_queue_head;
    if (dgram) {
	tunnel->xmit_queue_head = dgram->next;
	if (tunnel->xmit_new_dgrams == dgram) {
	    tunnel->xmit_new_dgrams = dgram->next;
	}
	if (tunnel->xmit_queue_tail == dgram) {
	    tunnel->xmit_queue_tail = NULL;
	}
	l2tp_dgram_free(dgram);
    }
}

/**********************************************************************
* %FUNCTION: tunnel_xmit_queued_messages
* %ARGUMENTS:
*  tunnel -- the tunnel
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Transmits as many control messages as possible from the queue.
***********************************************************************/
static void
tunnel_xmit_queued_messages(l2tp_tunnel *tunnel)
{
    l2tp_dgram *dgram;
    struct timeval t;

    dgram = tunnel->xmit_new_dgrams;
    if (!dgram) return;

    DBG(l2tp_db(DBG_FLOW, "xmit_queued(%s): %s\n",
	   l2tp_debug_tunnel_to_str(tunnel),
	   tunnel_flow_control_stats(tunnel)));
    while (dgram) {
	/* If window is closed, we can't transmit anything */
	if (tunnel_outstanding_frames(tunnel) >= tunnel->cwnd) {
	    break;
	}

	/* Update Nr */
	dgram->Nr = tunnel->Nr;

	/* Tid might have changed if call was initiated before
	   tunnel establishment was complete */
	dgram->tid = tunnel->assigned_id;

	if (l2tp_dgram_send_to_wire(dgram, &tunnel->peer_addr) < 0) {
	    break;
	}

	/* Cancel any pending ack */
	if (tunnel->ack_handler) {
	    Event_DelHandler(tunnel->es, tunnel->ack_handler);
	    tunnel->ack_handler = NULL;
	}

	tunnel->xmit_new_dgrams = dgram->next;
	tunnel->Ns_on_wire = dgram->Ns + 1;
	DBG(l2tp_db(DBG_FLOW, "loop in xmit_queued(%s): %s\n",
	       l2tp_debug_tunnel_to_str(tunnel),
	       tunnel_flow_control_stats(tunnel)));
	dgram = dgram->next;
    }

    t.tv_sec = tunnel->timeout;
    t.tv_usec = 0;

    /* Set retransmission timer */
    if (tunnel->timeout_handler) {
	Event_ChangeTimeout(tunnel->timeout_handler, t);
    } else {
	tunnel->timeout_handler =
	    Event_AddTimerHandler(tunnel->es, t,
				  tunnel_handle_timeout, tunnel);
    }
}

/**********************************************************************
* %FUNCTION: tunnel_xmit_control_message
* %ARGUMENTS:
*  tunnel -- the tunnel
*  dgram -- the datagram to transmit.  After return from this
*           function, the tunnel "owns" the dgram and the caller should
*           no longer use it.
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Transmits a control message.  If there is no room in the transmit
*  window, queues the message.
***********************************************************************/
void
l2tp_tunnel_xmit_control_message(l2tp_tunnel *tunnel,
				 l2tp_dgram  *dgram)
{
    /* Queue the datagram in case we need to retransmit it */
    tunnel_enqueue_dgram(tunnel, dgram);

    /* Run the queue */
    tunnel_xmit_queued_messages(tunnel);
}

static unsigned int
tunnel_compute_peerhash(void *data)
{
    return (unsigned int) (((l2tp_tunnel *)data)->peer_addr.sin_addr.s_addr);
}

static int
tunnel_compare_peer(void *d1, void *d2)
{
    return (((l2tp_tunnel *)d1)->peer_addr.sin_addr.s_addr) !=
	(((l2tp_tunnel *)d2)->peer_addr.sin_addr.s_addr);
}

/**********************************************************************
* %FUNCTION: tunnel_compute_hash_my_id
* %ARGUMENTS:
*  data -- a void pointer which is really a tunnel
* %RETURNS:
*  My tunnel ID
***********************************************************************/
static unsigned int
tunnel_compute_hash_my_id(void *data)
{
    return (unsigned int) (((l2tp_tunnel *) data)->my_id);
}

/**********************************************************************
* %FUNCTION: tunnel_compare_my_id
* %ARGUMENTS:
*  item1 -- first tunnel
*  item2 -- second tunnel
* %RETURNS:
*  0 if both tunnels have same ID, non-zero otherwise
***********************************************************************/
static int
tunnel_compare_my_id(void *item1, void *item2)
{
    return ((l2tp_tunnel *) item1)->my_id != ((l2tp_tunnel *) item2)->my_id;
}

/**********************************************************************
* %FUNCTION: tunnel_cleanup
* %ARGUMENTS:
*  data -- event selector
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Shuts down all tunnels and waits for them to exit
***********************************************************************/
static void
tunnel_cleanup(void *data)
{
    EventSelector *es = (EventSelector *) data;
    int i;

    /* Stop all tunnels */
    l2tp_tunnel_stop_all("Shutting down");

    while (hash_num_entries(&tunnels_by_my_id)) {
	i = Event_HandleEvent(es);
	if (i < 0) {
	    exit(EXIT_FAILURE);
	}
    }
}

/**********************************************************************
* %FUNCTION: tunnel_init
* %ARGUMENTS:
*  es -- event selector
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Initializes static tunnel data structures.
***********************************************************************/
void
l2tp_tunnel_init(EventSelector *es)
{
    hash_init(&tunnels_by_my_id,
	      offsetof(l2tp_tunnel, hash_by_my_id),
	      tunnel_compute_hash_my_id,
	      tunnel_compare_my_id);
    hash_init(&tunnels_by_peer_address,
	      offsetof(l2tp_tunnel, hash_by_peer),
	      tunnel_compute_peerhash,
	      tunnel_compare_peer);

    l2tp_register_shutdown_handler(tunnel_cleanup, es);
}

/**********************************************************************
* %FUNCTION: tunnel_new
* %ARGUMENTS:
*  es -- event selector
* %RETURNS:
*  A newly-allocated and initialized l2tp_tunnel object
***********************************************************************/
static l2tp_tunnel *
tunnel_new(EventSelector *es)
{
    l2tp_tunnel *tunnel = malloc(sizeof(l2tp_tunnel));
    if (!tunnel) return NULL;

    memset(tunnel, 0, sizeof(l2tp_tunnel));
    l2tp_session_hash_init(&tunnel->sessions_by_my_id);
    tunnel->rws = 4;
    tunnel->peer_rws = 1;
    tunnel->es = es;
    tunnel->timeout = 1;
    tunnel->my_id = tunnel_make_tid();
    tunnel->ssthresh = 1;
    tunnel->cwnd = 1;

    hash_insert(&tunnels_by_my_id, tunnel);
    DBG(l2tp_db(DBG_TUNNEL, "tunnel_new() -> %s\n", l2tp_debug_tunnel_to_str(tunnel)));
    return tunnel;
}

/**********************************************************************
* %FUNCTION: tunnel_free
* %ARGUMENTS:
*  tunnel -- tunnel to free
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Frees all memory used by tunnel
***********************************************************************/
static void
tunnel_free(l2tp_tunnel *tunnel)
{
    void *cursor;
    l2tp_session *ses;
    EventSelector *es = tunnel->es;

    DBG(l2tp_db(DBG_TUNNEL, "tunnel_free(%s)\n", l2tp_debug_tunnel_to_str(tunnel)));

    hash_remove(&tunnels_by_my_id, tunnel);
    hash_remove(&tunnels_by_peer_address, tunnel);

    for (ses = hash_start(&tunnel->sessions_by_my_id, &cursor);
	 ses ;
	 ses = hash_next(&tunnel->sessions_by_my_id, &cursor)) {
	l2tp_session_free(ses, "Tunnel closing down");
    }
    if (tunnel->hello_handler) Event_DelHandler(es, tunnel->hello_handler);
    if (tunnel->timeout_handler) Event_DelHandler(es, tunnel->timeout_handler);
    if (tunnel->ack_handler) Event_DelHandler(es, tunnel->ack_handler);

    while(tunnel->xmit_queue_head) {
	tunnel_dequeue_head(tunnel);
    }
    memset(tunnel, 0, sizeof(l2tp_tunnel));
    free(tunnel);
}

/**********************************************************************
* %FUNCTION: tunnel_establish
* %ARGUMENTS:
*  peer -- peer with which to establish tunnel
*  es -- event selector for event loop
* %RETURNS:
*  A newly-allocated tunnel, or NULL on error.
* %DESCRIPTION:
*  Begins tunnel establishment to peer.
***********************************************************************/
static l2tp_tunnel *
tunnel_establish(l2tp_peer *peer, EventSelector *es)
{
    l2tp_tunnel *tunnel;

    tunnel = tunnel_new(es);
    if (!tunnel) return NULL;

    tunnel->peer = peer;
    tunnel->peer_addr = peer->addr;

    hash_insert(&tunnels_by_peer_address, tunnel);
    tunnel_send_SCCRQ(tunnel);
    tunnel_set_state(tunnel, TUNNEL_WAIT_CTL_REPLY);
    return tunnel;
}

/**********************************************************************
* %FUNCTION: tunnel_send_SCCRQ
* %ARGUMENTS:
*  tunnel -- the tunnel we wish to establish
* %RETURNS:
*  0 if we handed the datagram off to control transport, -1 otherwise.
* %DESCRIPTION:
*  Sends SCCRQ to establish a tunnel.
***********************************************************************/
static int
tunnel_send_SCCRQ(l2tp_tunnel *tunnel)
{
    uint16_t u16;
    uint32_t u32;
    unsigned char tie_breaker[8];
    unsigned char challenge[16];
    int old_hide;

    l2tp_dgram *dgram = l2tp_dgram_new_control(MESSAGE_SCCRQ, 0, 0);
    if (!dgram) return -1;


    /* Add the AVP's */
    /* HACK!  Cisco equipment cannot handle hidden AVP's in SCCRQ.
       So we temporarily disable AVP hiding */
    old_hide = tunnel->peer->hide_avps;
    tunnel->peer->hide_avps = 0;

    /* Protocol version */
    u16 = htons(0x0100);
    l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
		  sizeof(u16), VENDOR_IETF, AVP_PROTOCOL_VERSION, &u16);

    /* Framing capabilities -- hard-coded as sync and async */
    u32 = htonl(3);
    l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
		  sizeof(u32), VENDOR_IETF, AVP_FRAMING_CAPABILITIES, &u32);

    /* Host name */
    l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
		  strlen(Hostname), VENDOR_IETF, AVP_HOST_NAME,
		  Hostname);

    /* Assigned ID */
    u16 = htons(tunnel->my_id);
    l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
		  sizeof(u16), VENDOR_IETF, AVP_ASSIGNED_TUNNEL_ID, &u16);

    /* Receive window size */
    u16 = htons(tunnel->rws);
    l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
		  sizeof(u16), VENDOR_IETF, AVP_RECEIVE_WINDOW_SIZE, &u16);

    /* Tie-breaker */
    l2tp_random_fill(tie_breaker, sizeof(tie_breaker));
    l2tp_dgram_add_avp(dgram, tunnel, NOT_MANDATORY,
		  sizeof(tie_breaker), VENDOR_IETF, AVP_TIE_BREAKER,
		  tie_breaker);

    /* Vendor name */
    l2tp_dgram_add_avp(dgram, tunnel, NOT_MANDATORY,
		  strlen(VENDOR_STR), VENDOR_IETF,
		  AVP_VENDOR_NAME, VENDOR_STR);

    /* Challenge */
    if (tunnel->peer->secret_len) {
	l2tp_random_fill(challenge, sizeof(challenge));
	l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
			   sizeof(challenge), VENDOR_IETF,
			   AVP_CHALLENGE, challenge);

	/* Compute and save expected response */
	l2tp_auth_gen_response(MESSAGE_SCCRP, tunnel->peer->secret,
			       challenge, sizeof(challenge), tunnel->expected_response);
    }

    tunnel->peer->hide_avps = old_hide;
    /* And ship it out */
    l2tp_tunnel_xmit_control_message(tunnel, dgram);
    return 1;
}

/**********************************************************************
* %FUNCTION: tunnel_handle_received_control_datagram
* %ARGUMENTS:
*  dgram -- received datagram
*  es -- event selector
*  from -- address of sender
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Handles a received control datagram
***********************************************************************/
void
l2tp_tunnel_handle_received_control_datagram(l2tp_dgram *dgram,
					     EventSelector *es,
					     struct sockaddr_in *from)
{
    l2tp_tunnel *tunnel;

    /* If it's SCCRQ, then handle it */
    if (dgram->msg_type == MESSAGE_SCCRQ) {
	tunnel_handle_SCCRQ(dgram, es, from);
	return;
    }

    /* Find the tunnel to which it refers */
    if (dgram->tid == 0) {
	l2tp_set_errmsg("Invalid control message - tunnel ID = 0");
	return;
    }
    tunnel = l2tp_tunnel_find_by_my_id(dgram->tid);
    if (!tunnel) {
	/* TODO: Send error message back? */
	l2tp_set_errmsg("Invalid control message - unknown tunnel ID %d",
		   (int) dgram->tid);
	return;
    }

    /* Verify that source address is the tunnel's peer */
    if (tunnel->peer->validate_peer_ip) {
	if (from->sin_addr.s_addr != tunnel->peer_addr.sin_addr.s_addr) {
	    l2tp_set_errmsg("Invalid control message for tunnel %s - not sent from peer",
			    l2tp_debug_tunnel_to_str(tunnel));
	    return;
	}
    }

    /* Set port for replies */
    tunnel->peer_addr.sin_port = from->sin_port;

    /* Schedule an ACK for 100ms from now, but do not ack ZLB's */
    if (dgram->msg_type != MESSAGE_ZLB) {
	tunnel_schedule_ack(tunnel);
    }

    /* If it's an old datagram, ignore it */
    if (dgram->Ns != tunnel->Nr) {
	if (SERIAL_LT(dgram->Ns, tunnel->Nr)) {
	    /* Old packet: Drop it */
	    /* Throw away ack'd packets in our xmit queue */
	    tunnel_dequeue_acked_packets(tunnel);
	    return;
	}
	/* Out-of-order packet or intermediate dropped packets.
	   TODO: Look into handling this better */
	return;
    }

    /* Do not increment if we got ZLB */
    if (dgram->msg_type != MESSAGE_ZLB) {
	tunnel->Nr++;
    }

    /* Update peer_Nr */
    if (SERIAL_GT(dgram->Nr, tunnel->peer_Nr)) {
	tunnel->peer_Nr = dgram->Nr;
    }

    /* Reschedule HELLO handler for 60 seconds in future */
    if (tunnel->state != TUNNEL_RECEIVED_STOP_CCN &&
	tunnel->state != TUNNEL_SENT_STOP_CCN &&
	tunnel->hello_handler != NULL) {
	struct timeval t;
	t.tv_sec = 60;
	t.tv_usec = 0;
	Event_ChangeTimeout(tunnel->hello_handler, t);
    }

    /* Reset retransmission stuff */
    tunnel->retransmissions = 0;
    tunnel->timeout = 1;

    /* Throw away ack'd packets in our xmit queue */
    tunnel_dequeue_acked_packets(tunnel);

    /* Let the specific tunnel handle it */
    tunnel_process_received_datagram(tunnel, dgram);

    /* Run the xmit queue -- window may have opened */
    tunnel_xmit_queued_messages(tunnel);

    /* Destroy tunnel if required and if xmit queue empty */
    if (!tunnel->xmit_queue_head) {
	if (tunnel->timeout_handler) {
	    Event_DelHandler(tunnel->es, tunnel->timeout_handler);
	    tunnel->timeout_handler = NULL;
	}
	if (tunnel->state == TUNNEL_RECEIVED_STOP_CCN) {
	    tunnel_schedule_destruction(tunnel);
	} else if (tunnel->state == TUNNEL_SENT_STOP_CCN) {
	    /* Our stop-CCN has been ack'd, destroy NOW */
	    tunnel_free(tunnel);
	}
    }
}

/**********************************************************************
* %FUNCTION: tunnel_handle_SCCRQ
* %ARGUMENTS:
*  dgram -- the received datagram
*  es -- event selector
*  from -- address of sender
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Handles an incoming SCCRQ
***********************************************************************/
static void
tunnel_handle_SCCRQ(l2tp_dgram *dgram,
		    EventSelector *es,
		    struct sockaddr_in *from)
{
    l2tp_tunnel *tunnel = NULL;

    uint16_t u16;
    uint32_t u32;
    unsigned char challenge[16];

    /* TODO: Check if this is a retransmitted SCCRQ */
    /* Allocate a tunnel */
    tunnel = tunnel_new(es);
    if (!tunnel) {
	l2tp_set_errmsg("Unable to allocate new tunnel");
	return;
    }

    tunnel->peer_addr = *from;

    hash_insert(&tunnels_by_peer_address, tunnel);

    /* We've received our first control datagram (SCCRQ) */
    tunnel->Nr = 1;

    if (tunnel_set_params(tunnel, dgram) < 0) return;

    /* Hunky-dory.  Send SCCRP */
    dgram = l2tp_dgram_new_control(MESSAGE_SCCRP, tunnel->assigned_id, 0);
    if (!dgram) {
	/* Doh! Out of resources.  Not much chance of StopCCN working... */
	tunnel_send_StopCCN(tunnel,
			    RESULT_GENERAL_ERROR, ERROR_OUT_OF_RESOURCES,
			    "Out of memory");
	return;
    }

    /* Protocol version */
    u16 = htons(0x0100);
    l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
		  sizeof(u16), VENDOR_IETF, AVP_PROTOCOL_VERSION, &u16);

    /* Framing capabilities -- hard-coded as sync and async */
    u32 = htonl(3);
    l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
		  sizeof(u32), VENDOR_IETF, AVP_FRAMING_CAPABILITIES, &u32);

    /* Host name */
    l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
		  strlen(Hostname), VENDOR_IETF, AVP_HOST_NAME,
		  Hostname);

    /* Assigned ID */
    u16 = htons(tunnel->my_id);
    l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
		  sizeof(u16), VENDOR_IETF, AVP_ASSIGNED_TUNNEL_ID, &u16);

    /* Receive window size */
    u16 = htons(tunnel->rws);
    l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
		  sizeof(u16), VENDOR_IETF, AVP_RECEIVE_WINDOW_SIZE, &u16);

    /* Vendor name */
    l2tp_dgram_add_avp(dgram, tunnel, NOT_MANDATORY,
		  strlen(VENDOR_STR), VENDOR_IETF,
		  AVP_VENDOR_NAME, VENDOR_STR);

    if (tunnel->peer->secret_len) {
	/* Response */
	l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
			   MD5LEN, VENDOR_IETF, AVP_CHALLENGE_RESPONSE,
			   tunnel->response);

	/* Challenge */
	l2tp_random_fill(challenge, sizeof(challenge));
	l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
			   sizeof(challenge), VENDOR_IETF,
			   AVP_CHALLENGE, challenge);

	/* Compute and save expected response */
	l2tp_auth_gen_response(MESSAGE_SCCCN, tunnel->peer->secret,
			       challenge, sizeof(challenge), tunnel->expected_response);
    }

    tunnel_set_state(tunnel, TUNNEL_WAIT_CTL_CONN);

    /* And ship it out */
    l2tp_tunnel_xmit_control_message(tunnel, dgram);
}

/**********************************************************************
* %FUNCTION: tunnel_schedule_ack
* %ARGUMENTS:
*  tunnel -- the tunnel
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Schedules an ack for 100ms from now.  The hope is we'll be able to
*  piggy-back the ack on a packet in the queue; if not, we'll send a ZLB.
***********************************************************************/
static void
tunnel_schedule_ack(l2tp_tunnel *tunnel)
{
    struct timeval t;

    DBG(l2tp_db(DBG_FLOW, "tunnel_schedule_ack(%s)\n",
	   l2tp_debug_tunnel_to_str(tunnel)));
    /* Already scheduled?  Do nothing */
    if (tunnel->ack_handler) return;

    t.tv_sec = 0;
    t.tv_usec = 100000;
    tunnel->ack_handler = Event_AddTimerHandler(tunnel->es,
						t, tunnel_do_ack, tunnel);
}

/**********************************************************************
* %FUNCTION: tunnel_do_ack
* %ARGUMENTS:
*  es -- event selector
*  fd, flags -- not used
*  data -- pointer to tunnel which needs ack
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  If there is a frame on our queue, update it's Nr and run queue; if not,
*  send a ZLB immediately.
***********************************************************************/
static void
tunnel_do_ack(EventSelector *es,
	      int fd,
	      unsigned int flags,
	      void *data)
{
    l2tp_tunnel *tunnel = (l2tp_tunnel *) data;

    /* Ack handler has fired */
    tunnel->ack_handler = NULL;

    DBG(l2tp_db(DBG_FLOW, "tunnel_do_ack(%s)\n",
	   l2tp_debug_tunnel_to_str(tunnel)));

    /* If nothing is on the queue, add a ZLB */
    /* Or, if we cannot transmit because of flow-control, send ZLB */
    if (!tunnel->xmit_new_dgrams ||
	tunnel_outstanding_frames(tunnel) >= tunnel->cwnd) {
	tunnel_send_ZLB(tunnel);
	return;
    }

    /* Run the queue */
    tunnel_xmit_queued_messages(tunnel);
}

/**********************************************************************
* %FUNCTION: tunnel_dequeue_acked_packets
* %ARGUMENTS:
*  tunnel -- the tunnel
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Discards all acknowledged packets from our xmit queue.
***********************************************************************/
static void
tunnel_dequeue_acked_packets(l2tp_tunnel *tunnel)
{
    l2tp_dgram *dgram = tunnel->xmit_queue_head;
    DBG(l2tp_db(DBG_FLOW, "tunnel_dequeue_acked_packets(%s) %s\n",
       l2tp_debug_tunnel_to_str(tunnel), tunnel_flow_control_stats(tunnel)));
    while (dgram) {
	if (SERIAL_LT(dgram->Ns, tunnel->peer_Nr)) {
	    tunnel_dequeue_head(tunnel);
	    if (tunnel->cwnd < tunnel->ssthresh) {
		/* Slow start: increment CWND for each packet dequeued */
		tunnel->cwnd++;
		if (tunnel->cwnd > tunnel->peer_rws) {
		    tunnel->cwnd = tunnel->peer_rws;
		}
	    } else {
		/* Congestion avoidance: increment CWND for each CWND
		   packets dequeued */
		tunnel->cwnd_counter++;
		if (tunnel->cwnd_counter >= tunnel->cwnd) {
		    tunnel->cwnd_counter = 0;
		    tunnel->cwnd++;
		    if (tunnel->cwnd > tunnel->peer_rws) {
			tunnel->cwnd = tunnel->peer_rws;
		    }
		}
	    }
	} else {
	    break;
	}
	dgram = tunnel->xmit_queue_head;
    }
}

/**********************************************************************
* %FUNCTION: tunnel_handle_timeout
* %ARGUMENTS:
*  es -- event selector
*  fd, flags -- not used
*  data -- pointer to tunnel which needs ack
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Called when retransmission timer fires.
***********************************************************************/
static void
tunnel_handle_timeout(EventSelector *es,
		      int fd,
		      unsigned int flags,
		      void *data)
{
    l2tp_tunnel *tunnel = (l2tp_tunnel *) data;

    /* Timeout handler has fired */
    tunnel->timeout_handler = NULL;

    /* Reset xmit_new_dgrams */
    tunnel->xmit_new_dgrams = tunnel->xmit_queue_head;

    /* Set Ns on wire to Ns of first frame in queue */
    if (tunnel->xmit_queue_head) {
	tunnel->Ns_on_wire = tunnel->xmit_queue_head->Ns;
    }

    /* Go back to slow-start phase */
    tunnel->ssthresh = tunnel->cwnd / 2;
    if (!tunnel->ssthresh) tunnel->ssthresh = 1;
    tunnel->cwnd = 1;
    tunnel->cwnd_counter = 0;

    tunnel->retransmissions++;
    if (tunnel->retransmissions >= MAX_RETRANSMISSIONS) {
	l2tp_set_errmsg("Too many retransmissions on tunnel (%s); closing down",
		   l2tp_debug_tunnel_to_str(tunnel));
	/* Close tunnel... */
	tunnel_free(tunnel);
	return;
    }

    /* Double timeout, capping at 8 seconds */
    if (tunnel->timeout < 8) {
	tunnel->timeout *= 2;
    }

    /* Run the queue */
    tunnel_xmit_queued_messages(tunnel);
}

/**********************************************************************
* %FUNCTION: tunnel_send_StopCCN
* %ARGUMENTS:
*  tunnel -- the tunnel
*  result_code -- what to put in result-code AVP
*  error_code -- what to put in error-code field
*  fmt -- format string for error message
*  ... -- args for formatting error message
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Sends a StopCCN control message.
***********************************************************************/
static void
tunnel_send_StopCCN(l2tp_tunnel *tunnel,
		    int result_code,
		    int error_code,
		    char const *fmt, ...)
{
    char buf[256];
    va_list ap;
    uint16_t u16;
    uint16_t len;
    l2tp_dgram *dgram;

    /* Build the buffer for the result-code AVP */
    buf[0] = result_code / 256;
    buf[1] = result_code & 255;
    buf[2] = error_code / 256;
    buf[3] = error_code & 255;

    va_start(ap, fmt);
    vsnprintf(buf+4, 256-4, fmt, ap);
    buf[255] = 0;
    va_end(ap);

    DBG(l2tp_db(DBG_TUNNEL, "tunnel_send_StopCCN(%s, %d, %d, %s)\n",
	   l2tp_debug_tunnel_to_str(tunnel), result_code, error_code, buf+4));

    len = 4 + strlen(buf+4);
    /* Build the datagram */
    dgram = l2tp_dgram_new_control(MESSAGE_StopCCN, tunnel->assigned_id, 0);
    if (!dgram) return;

    /* Add assigned tunnel ID */
    u16 = htons(tunnel->my_id);
    l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
		  sizeof(u16), VENDOR_IETF, AVP_ASSIGNED_TUNNEL_ID, &u16);

    /* Add result code */
    l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
		  len, VENDOR_IETF, AVP_RESULT_CODE, buf);

    /* TODO: Clean up */
    tunnel_set_state(tunnel, TUNNEL_SENT_STOP_CCN);

    /* Ship it out */
    l2tp_tunnel_xmit_control_message(tunnel, dgram);
}

/**********************************************************************
* %FUNCTION: tunnel_handle_StopCCN
* %ARGUMENTS:
*  tunnel -- the tunnel
*  dgram -- received datagram
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Processes a received StopCCN frame (shuts tunnel down)
***********************************************************************/
static void
tunnel_handle_StopCCN(l2tp_tunnel *tunnel,
		       l2tp_dgram *dgram)
{
    unsigned char *val;
    int mandatory, hidden;
    uint16_t len, vendor, type;
    int err;
    l2tp_session *ses;
    void *cursor;

    /* Shut down all the sessions */
    for (ses = hash_start(&tunnel->sessions_by_my_id, &cursor);
	 ses ;
	 ses = hash_next(&tunnel->sessions_by_my_id, &cursor)) {
	hash_remove(&tunnel->sessions_by_my_id, ses);
	l2tp_session_free(ses, "Tunnel closing down");
    }

    tunnel_set_state(tunnel, TUNNEL_RECEIVED_STOP_CCN);

    /* If there are any queued datagrams waiting for transmission,
       nuke them and adjust tunnel's Ns to whatever peer has ack'd */
    /* TODO: Is this correct? */
    if (tunnel->xmit_queue_head) {
	tunnel->Ns = tunnel->peer_Nr;
	while(tunnel->xmit_queue_head) {
	    tunnel_dequeue_head(tunnel);
	}
    }

    /* Parse the AVP's */
    while(1) {
	val = l2tp_dgram_pull_avp(dgram, tunnel, &mandatory, &hidden,
			     &len, &vendor, &type, &err);
	if (!val) {
	    break;
	}

	/* Only care about assigned tunnel ID.  TODO: Fix this! */
	if (vendor != VENDOR_IETF || type != AVP_ASSIGNED_TUNNEL_ID) {
	    continue;
	}

	if (len == 2) {
	    tunnel->assigned_id = ((unsigned int) val[0]) * 256 +
		(unsigned int) val[1];
	}
    }

    /* No point in delaying ack; there will never be a datagram for
       piggy-back.  So cancel ack timer and shoot out a ZLB now */
    if (tunnel->ack_handler) {
	Event_DelHandler(tunnel->es, tunnel->ack_handler);
	tunnel->ack_handler = NULL;
    }
    tunnel_send_ZLB(tunnel);
    /* We'll be scheduled for destruction after this function returns */
}

/**********************************************************************
* %FUNCTION: tunnel_process_received_datagram
* %ARGUMENTS:
*  tunnel -- the tunnel
*  dgram -- the received datagram
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Handles a received control message for this tunnel
***********************************************************************/
static void
tunnel_process_received_datagram(l2tp_tunnel *tunnel,
				 l2tp_dgram *dgram)
{
    l2tp_session *ses = NULL;

    /* If message is associated with existing session, find session */

    DBG(l2tp_db(DBG_TUNNEL, "tunnel_process_received_datagram(%s, %s)\n",
	   l2tp_debug_tunnel_to_str(tunnel),
	   l2tp_debug_message_type_to_str(dgram->msg_type)));
    switch (dgram->msg_type) {
    case MESSAGE_OCRP:
    case MESSAGE_OCCN:
    case MESSAGE_ICRP:
    case MESSAGE_ICCN:
    case MESSAGE_CDN:
	ses = l2tp_tunnel_find_session(tunnel, dgram->sid);
	if (!ses) {
	    l2tp_set_errmsg("Session-related message for unknown session %d",
		       (int) dgram->sid);
	    return;
	}
    }

    switch (dgram->msg_type) {
    case MESSAGE_StopCCN:
	tunnel_handle_StopCCN(tunnel, dgram);
	return;
    case MESSAGE_SCCRP:
	tunnel_handle_SCCRP(tunnel, dgram);
	return;
    case MESSAGE_SCCCN:
	tunnel_handle_SCCCN(tunnel, dgram);
	return;
    case MESSAGE_ICRQ:
	tunnel_handle_ICRQ(tunnel, dgram);
	return;
    case MESSAGE_CDN:
	l2tp_session_handle_CDN(ses, dgram);
	return;
    case MESSAGE_ICRP:
	l2tp_session_handle_ICRP(ses, dgram);
	return;
    case MESSAGE_ICCN:
	l2tp_session_handle_ICCN(ses, dgram);
	return;
    }
}

/**********************************************************************
* %FUNCTION: tunnel_finally_cleanup
* %ARGUMENTS:
*  es -- event selector
*  fd, flags -- ignored
*  data -- the tunnel
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Deallocates all tunnel state
***********************************************************************/
static void
tunnel_finally_cleanup(EventSelector *es,
		       int fd,
		       unsigned int flags,
		       void *data)
{
    l2tp_tunnel *tunnel = (l2tp_tunnel *) data;

    /* Hello handler has fired */
    tunnel->hello_handler = NULL;
    tunnel_free(tunnel);
}

/**********************************************************************
* %FUNCTION: tunnel_schedule_destruction
* %ARGUMENTS:
*  tunnel -- tunnel which is to be destroyed
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Schedules tunnel for destruction 31 seconds hence.
***********************************************************************/
static void
tunnel_schedule_destruction(l2tp_tunnel *tunnel)
{
    struct timeval t;
    void *cursor;
    l2tp_session *ses;

    t.tv_sec = 31;
    t.tv_usec = 0;

    DBG(l2tp_db(DBG_TUNNEL, "tunnel_schedule_destruction(%s)\n",
	   l2tp_debug_tunnel_to_str(tunnel)));
    /* Shut down the tunnel in 31 seconds - (ab)use the hello handler */
    if (tunnel->hello_handler) {
	Event_DelHandler(tunnel->es, tunnel->hello_handler);
    }
    tunnel->hello_handler =
	Event_AddTimerHandler(tunnel->es, t,
			      tunnel_finally_cleanup, tunnel);

    /* Kill the sessions now */
    for (ses = hash_start(&tunnel->sessions_by_my_id, &cursor);
	 ses ;
	 ses = hash_next(&tunnel->sessions_by_my_id, &cursor)) {
	l2tp_session_free(ses, "Tunnel closing down");
    }

    /* Clear hash table */
    l2tp_session_hash_init(&tunnel->sessions_by_my_id);
}

/**********************************************************************
* %FUNCTION: tunnel_send_ZLB
* %ARGUMENTS:
*  tunnel -- the tunnel
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Sends a ZLB ack packet.
***********************************************************************/
void
tunnel_send_ZLB(l2tp_tunnel *tunnel)
{
    l2tp_dgram *dgram =
	l2tp_dgram_new_control(MESSAGE_ZLB, tunnel->assigned_id, 0);
    if (!dgram) {
	l2tp_set_errmsg("tunnel_send_ZLB: Out of memory");
	return;
    }
    dgram->Nr = tunnel->Nr;
    dgram->Ns = tunnel->Ns;
    l2tp_dgram_send_to_wire(dgram, &tunnel->peer_addr);
    l2tp_dgram_free(dgram);
}

/**********************************************************************
* %FUNCTION: tunnel_handle_SCCRP
* %ARGUMENTS:
*  tunnel -- the tunnel
*  dgram -- the incoming datagram
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Handles an incoming SCCRP
***********************************************************************/
static void
tunnel_handle_SCCRP(l2tp_tunnel *tunnel,
		    l2tp_dgram *dgram)
{

    /* TODO: If we don't get challenge response at all, barf */
    /* Are we expecing SCCRP? */
    if (tunnel->state != TUNNEL_WAIT_CTL_REPLY) {
	tunnel_send_StopCCN(tunnel, RESULT_FSM_ERROR, 0, "Not expecting SCCRP");
	return;
    }

    /* Extract tunnel params */
    if (tunnel_set_params(tunnel, dgram) < 0) return;

    tunnel_set_state(tunnel, TUNNEL_ESTABLISHED);
    tunnel_setup_hello(tunnel);

    /* Hunky-dory.  Send SCCCN */
    dgram = l2tp_dgram_new_control(MESSAGE_SCCCN, tunnel->assigned_id, 0);
    if (!dgram) {
	/* Doh! Out of resources.  Not much chance of StopCCN working... */
	tunnel_send_StopCCN(tunnel,
			    RESULT_GENERAL_ERROR, ERROR_OUT_OF_RESOURCES,
			    "Out of memory");
	return;
    }

    /* Add response */
    if (tunnel->peer->secret_len) {
	l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
			   MD5LEN, VENDOR_IETF, AVP_CHALLENGE_RESPONSE,
			   tunnel->response);
    }

    /* Ship it out */
    l2tp_tunnel_xmit_control_message(tunnel, dgram);

    /* Tell sessions tunnel has been established */
    tunnel_tell_sessions_tunnel_open(tunnel);
}

/**********************************************************************
* %FUNCTION: tunnel_set_params
* %ARGUMENTS:
*  tunnel -- the tunnel
*  dgram -- incoming SCCRQ or SCCRP datagram
* %RETURNS:
*  0 if OK, non-zero on error
* %DESCRIPTION:
*  Sets up initial tunnel parameters (assigned ID, etc.)
***********************************************************************/
static int
tunnel_set_params(l2tp_tunnel *tunnel,
		  l2tp_dgram *dgram)
{
    unsigned char *val;
    int mandatory, hidden;
    uint16_t len, vendor, type;
    int err = 0;
    int found_response = 0;

    uint16_t u16;

    /* Find peer */
    tunnel->peer = l2tp_peer_find(&tunnel->peer_addr);

    /* Get assigned tunnel ID */
    val = l2tp_dgram_search_avp(dgram, tunnel, &mandatory, &hidden, &len,
			   VENDOR_IETF, AVP_ASSIGNED_TUNNEL_ID);
    if (!val) {
	l2tp_set_errmsg("No assigned tunnel ID AVP in SCCRQ");
	tunnel_free(tunnel);
	return -1;
    }

    if (!l2tp_dgram_validate_avp(VENDOR_IETF, AVP_ASSIGNED_TUNNEL_ID,
			    len, mandatory)) {
	tunnel_free(tunnel);
	return -1;
    }

    /* Set tid */
    tunnel->assigned_id = ((unsigned int) val[0]) * 256 + (unsigned int) val[1];
    if (!tunnel->assigned_id) {
	l2tp_set_errmsg("Invalid assigned-tunnel-ID of zero");
	tunnel_free(tunnel);
	return -1;
    }

    /* Validate peer */
    if (!tunnel->peer) {
	l2tp_set_errmsg("Peer %s is not authorized to create a tunnel",
		   inet_ntoa(tunnel->peer_addr.sin_addr));
	tunnel_send_StopCCN(tunnel, RESULT_NOAUTH, ERROR_OK, "%s", l2tp_get_errmsg());
	return -1;
    }

    /* Pull out and examine AVP's */
    while(1) {
	val = l2tp_dgram_pull_avp(dgram, tunnel, &mandatory, &hidden,
			     &len, &vendor, &type, &err);
	if (!val) {
	    if (err) {
		tunnel_send_StopCCN(tunnel, RESULT_GENERAL_ERROR,
				    ERROR_BAD_VALUE, "%s", l2tp_get_errmsg());
		return -1;
	    }
	    break;
	}

	/* Unknown vendor?  Ignore, unless mandatory */
	if (vendor != VENDOR_IETF) {
	    if (!mandatory) continue;
	    tunnel_send_StopCCN(tunnel, RESULT_GENERAL_ERROR,
				ERROR_UNKNOWN_AVP_WITH_M_BIT,
				"Unknown mandatory attribute (vendor=%d, type=%d) in %s",
				(int) vendor, (int) type,
				l2tp_debug_avp_type_to_str(dgram->msg_type));
	    return -1;
	}

	/* Validate AVP, ignore invalid AVP's without M bit set */
	if (!l2tp_dgram_validate_avp(vendor, type, len, mandatory)) {
	    if (!mandatory) continue;
	    tunnel_send_StopCCN(tunnel, RESULT_GENERAL_ERROR,
				ERROR_BAD_LENGTH, "%s", l2tp_get_errmsg());
	    return -1;
	}

	switch(type) {
	case AVP_PROTOCOL_VERSION:
	    u16 = ((uint16_t) val[0]) * 256 + val[1];
	    if (u16 != 0x100) {
		tunnel_send_StopCCN(tunnel, RESULT_UNSUPPORTED_VERSION,
				    0x100, "Unsupported protocol version");
		return -1;
	    }
	    break;

	case AVP_HOST_NAME:
	    if (len >= MAX_HOSTNAME) len = MAX_HOSTNAME-1;
	    memcpy(tunnel->peer_hostname, val, len);
	    tunnel->peer_hostname[len+1] = 0;
	    DBG(l2tp_db(DBG_TUNNEL, "%s: Peer host name is '%s'\n",
		   l2tp_debug_tunnel_to_str(tunnel), tunnel->peer_hostname));
	    break;

	case AVP_FRAMING_CAPABILITIES:
	    /* TODO: Do we care about framing capabilities? */
	    break;

	case AVP_ASSIGNED_TUNNEL_ID:
	    /* Already been handled */
	    break;

	case AVP_BEARER_CAPABILITIES:
	    /* TODO: Do we care? */
	    break;

	case AVP_RECEIVE_WINDOW_SIZE:
	    u16 = ((uint16_t) val[0]) * 256 + val[1];
	    /* Silently correct bogus rwin */
	    if (u16 < 1) u16 = 1;
	    tunnel->peer_rws = u16;
	    tunnel->ssthresh = u16;
	    break;

	case AVP_CHALLENGE:
	    if (tunnel->peer->secret_len) {
		l2tp_auth_gen_response(
		    ((dgram->msg_type == MESSAGE_SCCRQ) ? MESSAGE_SCCRP : MESSAGE_SCCCN),
		    tunnel->peer->secret,
		    val, len, tunnel->response);
	    }
	    break;

	case AVP_CHALLENGE_RESPONSE:
	    /* Length has been validated by l2tp_dgram_validate_avp */
	    if (tunnel->peer->secret_len) {
		if (memcmp(val, tunnel->expected_response, MD5LEN)) {
		    tunnel_send_StopCCN(tunnel, RESULT_NOAUTH, ERROR_BAD_VALUE,
					"Incorrect challenge response");
		    return -1;
		}
	    }
	    found_response = 1;
	    break;

	case AVP_TIE_BREAKER:
	    /* TODO: Handle tie-breaker */
	    break;

	case AVP_FIRMWARE_REVISION:
	    /* TODO: Do we care? */
	    break;

	case AVP_VENDOR_NAME:
	    /* TODO: Do we care? */
	    break;

	default:
	    /* TODO: Maybe print an error? */
	    break;
	}
    }

    if (tunnel->peer->secret_len &&
	!found_response &&
	dgram->msg_type != MESSAGE_SCCRQ) {
	tunnel_send_StopCCN(tunnel, RESULT_NOAUTH, 0,
			    "Missing challenge-response");
	return -1;
    }
    return 0;
}

/**********************************************************************
* %FUNCTION: tunnel_setup_hello
* %ARGUMENTS:
*  tunnel -- the tunnel
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Sets up timer for sending HELLO messages
***********************************************************************/
static void
tunnel_setup_hello(l2tp_tunnel *tunnel)
{
    struct timeval t;

    t.tv_sec = 60;
    t.tv_usec = 0;

    if (tunnel->hello_handler) {
	Event_DelHandler(tunnel->es, tunnel->hello_handler);
    }
    tunnel->hello_handler = Event_AddTimerHandler(tunnel->es, t,
						  tunnel_do_hello, tunnel);
}

/**********************************************************************
* %FUNCTION: tunnel_do_hello
* %ARGUMENTS:
*  es -- event selector
*  fd, flags -- ignored
*  data -- the tunnel
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Deallocates all tunnel state
***********************************************************************/
static void
tunnel_do_hello(EventSelector *es,
		int fd,
		unsigned int flags,
		void *data)
{
    l2tp_tunnel *tunnel = (l2tp_tunnel *) data;
    l2tp_dgram *dgram;

    /* Hello handler has fired */
    tunnel->hello_handler = NULL;

    /* Reschedule HELLO timer */
    tunnel_setup_hello(tunnel);

    /* Send a HELLO message */
    dgram = l2tp_dgram_new_control(MESSAGE_HELLO, tunnel->assigned_id, 0);
    if (dgram) l2tp_tunnel_xmit_control_message(tunnel, dgram);
}

/**********************************************************************
* %FUNCTION: tunnel_handle_SCCCN
* %ARGUMENTS:
*  tunnel -- the tunnel
*  dgram -- the incoming datagram
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Handles an incoming SCCCN
***********************************************************************/
static void
tunnel_handle_SCCCN(l2tp_tunnel *tunnel,
		    l2tp_dgram *dgram)
{
    unsigned char *val;
    uint16_t len;
    int hidden, mandatory;

    /* Are we expecing SCCCN? */
    if (tunnel->state != TUNNEL_WAIT_CTL_CONN) {
	tunnel_send_StopCCN(tunnel, RESULT_FSM_ERROR, 0, "Not expecting SCCCN");
	return;
    }

    /* Check challenge response */
    if (tunnel->peer->secret_len) {
	val = l2tp_dgram_search_avp(dgram, tunnel, &mandatory, &hidden, &len,
				    VENDOR_IETF, AVP_CHALLENGE_RESPONSE);
	if (!val) {
	    tunnel_send_StopCCN(tunnel, RESULT_NOAUTH, 0,
				"Missing challenge-response");
	    return;
	}

	if (!l2tp_dgram_validate_avp(VENDOR_IETF, AVP_CHALLENGE_RESPONSE,
				     len, mandatory)) {
	    tunnel_send_StopCCN(tunnel, RESULT_GENERAL_ERROR, ERROR_BAD_LENGTH,
				"Invalid challenge-response");
	    return;
	}
	if (memcmp(val, tunnel->expected_response, MD5LEN)) {
	    tunnel_send_StopCCN(tunnel, RESULT_NOAUTH, ERROR_BAD_VALUE,
				"Incorrect challenge response");
	    return;
	}
    }

    tunnel_set_state(tunnel, TUNNEL_ESTABLISHED);
    tunnel_setup_hello(tunnel);

    /* Tell sessions tunnel has been established */
    tunnel_tell_sessions_tunnel_open(tunnel);
}

/**********************************************************************
* %FUNCTION: tunnel_find_for_peer
* %ARGUMENTS:
*  peer -- an L2TP peer
*  es -- an event selector
* %RETURNS:
*  An existing tunnel to peer (if one exists) or a new one (if one does not),
*  or NULL if no tunnel could be established.  If the existing tunnel
*  is in the state RECEIVED_STOP_CCN or SENT_STOP_CCN, make a new one.
***********************************************************************/
l2tp_tunnel *
l2tp_tunnel_find_for_peer(l2tp_peer *peer,
		     EventSelector *es)
{
    l2tp_tunnel *tunnel = tunnel_find_bypeer(peer->addr);
    if (tunnel) {
	if (tunnel->state == TUNNEL_WAIT_CTL_REPLY ||
	    tunnel->state == TUNNEL_WAIT_CTL_CONN ||
	    tunnel->state == TUNNEL_ESTABLISHED) {
	    return tunnel;
	}
    }

    /* No tunnel, or tunnel in wrong state */
    return tunnel_establish(peer, es);
}

/**********************************************************************
* %FUNCTION: tunnel_find_session
* %ARGUMENTS:
*  sid -- session ID
* %RETURNS:
*  The session with specified ID, or NULL if no such session
***********************************************************************/
l2tp_session *
l2tp_tunnel_find_session(l2tp_tunnel *tunnel,
		    uint16_t sid)
{
    l2tp_session candidate;
    candidate.my_id = sid;
    return hash_find(&tunnel->sessions_by_my_id, &candidate);
}

/**********************************************************************
* %FUNCTION: tunnel_tell_sessions_tunnel_open
* %ARGUMENTS:
*  tunnel -- the tunnel
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Informs all waiting sessions that tunnel has moved into ESTABLISHED state.
***********************************************************************/
static void
tunnel_tell_sessions_tunnel_open(l2tp_tunnel *tunnel)
{
    l2tp_session *ses;
    void *cursor;

    for (ses = hash_start(&tunnel->sessions_by_my_id, &cursor);
	 ses ;
	 ses = hash_next(&tunnel->sessions_by_my_id, &cursor)) {
	l2tp_session_notify_tunnel_open(ses);
    }
}

/**********************************************************************
* %FUNCTION: tunnel_add_session
* %ARGUMENTS:
*  ses -- session to add
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Adds session to tunnel's hash table.  If tunnel is up, calls
*  l2tp_session_notify_tunnel_open
***********************************************************************/
void
l2tp_tunnel_add_session(l2tp_session *ses)
{
    l2tp_tunnel *tunnel = ses->tunnel;

    hash_insert(&tunnel->sessions_by_my_id, ses);
    if (tunnel->state == TUNNEL_ESTABLISHED) {
	l2tp_session_notify_tunnel_open(ses);
    }
}

/**********************************************************************
* %FUNCTION: tunnel_delete_session
* %ARGUMENTS:
*  ses -- session to delete
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Deletes session from tunnel's hash table and frees it.
***********************************************************************/
void
l2tp_tunnel_delete_session(l2tp_session *ses, char const *reason)
{
    l2tp_tunnel *tunnel = ses->tunnel;

    hash_remove(&tunnel->sessions_by_my_id, ses);
    l2tp_session_free(ses, reason);

    /* Tear down tunnel if so indicated */
    if (!hash_num_entries(&tunnel->sessions_by_my_id)) {
	if (!tunnel->peer->retain_tunnel) {
	    tunnel_send_StopCCN(tunnel,
				RESULT_GENERAL_REQUEST, 0,
				"Last session has closed");
	}
    }
}

/**********************************************************************
* %FUNCTION: tunnel_handle_ICRQ
* %ARGUMENTS:
*  tunnel -- the tunnel
*  dgram -- the datagram
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Handles ICRQ (Incoming Call ReQuest)
***********************************************************************/
static void
tunnel_handle_ICRQ(l2tp_tunnel *tunnel,
		   l2tp_dgram *dgram)
{
    uint16_t u16;
    unsigned char *val;
    uint16_t len;
    int mandatory, hidden;

    /* Get assigned session ID */
    val = l2tp_dgram_search_avp(dgram, tunnel, &mandatory, &hidden, &len,
			   VENDOR_IETF, AVP_ASSIGNED_SESSION_ID);
    if (!val) {
	l2tp_set_errmsg("No assigned tunnel ID AVP in ICRQ");
	return;
    }
    if (!l2tp_dgram_validate_avp(VENDOR_IETF, AVP_ASSIGNED_SESSION_ID,
			    len, mandatory)) {
	/* TODO: send CDN */
	return;
    }

    /* Set assigned session ID */
    u16 = ((uint16_t) val[0]) * 256 + (uint16_t) val[1];

    if (!u16) {
	/* TODO: send CDN */
	return;
    }

    /* Tunnel in wrong state? */
    if (tunnel->state != TUNNEL_ESTABLISHED) {
	/* TODO: Send CDN */
	return;
    }

    /* Set up new incoming call */
    /* TODO: Include calling number */
    l2tp_session_lns_handle_incoming_call(tunnel, u16, dgram, "");
}

/**********************************************************************
* %FUNCTION: l2tp_tunnel_stop_all
* %ARGUMENTS:
*  reason -- reason for stopping tunnels
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Stops all tunnels
***********************************************************************/
void
l2tp_tunnel_stop_all(char const *reason)
{
    l2tp_tunnel *tunnel;
    void *cursor;

    /* Send StopCCN on all tunnels except those which are scheduled for
       destruction */
    for (tunnel = hash_start(&tunnels_by_my_id, &cursor);
	 tunnel;
	 tunnel = hash_next(&tunnels_by_my_id, &cursor)) {
	l2tp_tunnel_stop_tunnel(tunnel, reason);
    }

}

/**********************************************************************
* %FUNCTION: l2tp_tunnel_stop_tunnel
* %ARGUMENTS:
*  tunnel -- tunnel to stop
*  reason -- reason for stopping tunnel
* %RETURNS:
*  Nothing
* %DESCRIPTION:
*  Stops a tunnels (sends StopCCN)
***********************************************************************/
void
l2tp_tunnel_stop_tunnel(l2tp_tunnel *tunnel,
			char const *reason)
{
    /* Do not send StopCCN if we've received one already */
    if (tunnel->state != TUNNEL_RECEIVED_STOP_CCN &&
	tunnel->state != TUNNEL_SENT_STOP_CCN) {
	tunnel_send_StopCCN(tunnel, RESULT_SHUTTING_DOWN, 0, reason);
    }
}

/**********************************************************************
* %FUNCTION: l2tp_num_tunnels
* %ARGUMENTS:
*  None
* %RETURNS:
*  The number of L2TP tunnels
***********************************************************************/
int
l2tp_num_tunnels(void)
{
    return hash_num_entries(&tunnels_by_my_id);
}

/**********************************************************************
* %FUNCTION: l2tp_first_tunnel
* %ARGUMENTS:
*  cursor -- cursor for keeping track of where we are in interation
* %RETURNS:
*  First L2TP tunnel
***********************************************************************/
l2tp_tunnel *
l2tp_first_tunnel(void **cursor)
{
    return hash_start(&tunnels_by_my_id, cursor);
}


/**********************************************************************
* %FUNCTION: l2tp_next_tunnel
* %ARGUMENTS:
*  cursor -- cursor for keeping track of where we are in interation
* %RETURNS:
*  Next L2TP tunnel
***********************************************************************/
l2tp_tunnel *
l2tp_next_tunnel(void **cursor)
{
    return hash_next(&tunnels_by_my_id, cursor);
}

/**********************************************************************
* %FUNCTION: l2tp_tunnel_state_name
* %ARGUMENTS:
*  tunnel -- the tunnel
* %RETURNS:
*  The name of the tunnel's state
***********************************************************************/
char const *
l2tp_tunnel_state_name(l2tp_tunnel *tunnel)
{
    return state_names[tunnel->state];
}

/**********************************************************************
* %FUNCTION: l2tp_tunnel_first_session
* %ARGUMENTS:
*  tunnel -- the tunnel
*  cursor -- cursor for hash table iteration
* %RETURNS:
*  First session in tunnel
***********************************************************************/
l2tp_session *
l2tp_tunnel_first_session(l2tp_tunnel *tunnel, void **cursor)
{
    return hash_start(&tunnel->sessions_by_my_id, cursor);
}

/**********************************************************************
* %FUNCTION: l2tp_tunnel_next_session
* %ARGUMENTS:
*  tunnel -- the tunnel
*  cursor -- cursor for hash table iteration
* %RETURNS:
*  Next session in tunnel
***********************************************************************/
l2tp_session *
l2tp_tunnel_next_session(l2tp_tunnel *tunnel, void **cursor)
{
    return hash_next(&tunnel->sessions_by_my_id, cursor);
}
