/*
 * dctpRcv.c -- support routienes for receiving DCCP packets.  Receive processing starts with
 * an encapsulation-specific routine called by the OS-support code, then proceeds
 * through the 16 steps of the RFC 4340 pseudocode.
 *
 * External API routines:
 *    dctpaNatV4Rcv -- called by the platform-support code with a DCCP packet in
 *                     NATv4 encapsulation.
 *
 * Copyright (C) 2008 Tom Phelan
 *
 * This file is part of dccp-tp.
 *
 * Dccp-tp is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Dccp-tp is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with dccp-tp.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Documentation and source code for dccp-tp is available at
 * http://www.phelan-4.com/dccp-tp/.
 */

#include "dctpCore.h"
#include "dctpSupport.h"
#include "dctpInternal.h"

/*
 * Prototypes for internal functions
 */
static int v4NatToCanonHdr(dctpPacket *pkt);
static int rcvDoSteps(dctpSocket *sock, dctpPacket *pkt);
static dctpSocket *rcvStep3ListenState(dctpSocket *sock, dctpPacket *pkt);
static int rcvStep4RequestState(dctpSocket *sock, dctpPacket *pkt);
static int rcvStep5Sync(dctpSocket *sock, dctpPacket *pkt);
static int rcvStep6ChkSeqNums(dctpSocket *sock, dctpPacket *pkt);
static int rcvStep7UnexPktTypes(dctpSocket *sock, dctpPacket *pkt);
static int rcvStep8Options(dctpSocket *sock, dctpPacket *pkt);
static int rcvStep9Reset(dctpSocket *sock, dctpPacket *pkt);
static int rcvStep10Request(dctpSocket *sock, dctpPacket *pkt);
static int rcvStep11Respond(dctpSocket *sock, dctpPacket *pkt);
static int rcvStep12Partopen(dctpSocket *sock, dctpPacket *pkt);
static int rcvStep13CloseReq(dctpSocket *sock, dctpPacket *pkt);
static int rcvStep14Close(dctpSocket *sock, dctpPacket *pkt);
static int rcvStep15Sync(dctpSocket *sock, dctpPacket *pkt);
static int rcvStep16ProcessData(dctpSocket *sock, dctpPacket *pkt);
static int processOptions(dctpSocket *sock, dctpPacket *pkt);
static int validSeqNo(uint64_t wl, uint64_t no, uint64_t wh);
static void updateGSR(dctpSocket *sock, dctpPacket *pkt);
static void timeWaitTimeout(void *arg);
static void partOpenTimeout(void *arg);

extern int dctpiCcid2SenderOpen(dctpSocket *sock);
extern int dctpiCcid2RcvrOpen(dctpSocket *sock);
extern int dctpiCcid3SenderOpen(dctpSocket *sock);
extern int dctpiCcid3RcvrOpen(dctpSocket *sock);

#define DCTP_MAXCCIDNUMBER   3

static int (*ccidSenderOpenFuncs[])(dctpSocket *sock) = {
    NULL,
    NULL,
    dctpiCcid2SenderOpen,
    dctpiCcid3SenderOpen,
};

static int (*ccidRcvrOpenFuncs[])(dctpSocket *sock) = {
    NULL,
    NULL,
    dctpiCcid2RcvrOpen,
    dctpiCcid3RcvrOpen,
};

/*
 * dctpaNatV4Rcv -- process an incoming DCCP packet in NATv4 encapsulation
 *
 * Inputs:  pkt -- a pointer to a DCCP packet
 *                 On entry:
 *                   pkt->appdata points to dccp hdr
 *                   pkt->dccphdr points to dccp hdr
 *                   pkt->iphdr points to incoming interface (uint32_t), followed by ip hdr
 *                   pkt->appdatalen equals appdata plus dccp hdr
 *
 * Caller is not responsible for freeing pkt.
 */
void dctpaNatV4Rcv(dctpPacket *pkt) {
    dctpUdpHdr *udphdr;
    dctpSocket *sock, *nsock;
    uint_t csumcov;

    /* Convert header to canonical format */
    if (v4NatToCanonHdr(pkt) < 0) {
	dctpoPacketFree(pkt);
	return;
    }
    /* Step 1: Check header basics -- need to know encap to do this */
    if (pkt->appdatalen < pkt->chdr.doffset) {
	/* Packet not long enough to include DCCP header */
	dctpoPacketFree(pkt);
	return;
    }
    pkt->appdata += pkt->chdr.doffset;
    pkt->appdatalen -= pkt->chdr.doffset;
    udphdr = ((dctpUdpHdr *)(pkt->dccphdr - sizeof(dctpUdpHdr)));
    /* Must have used UDP checksum */
    if (udphdr->csum == 0) {
	dctpoPacketFree(pkt);
	return;
    }
    /* Checksum must have covered UDP + DCCP headers */
    csumcov = dctpoNtohs(udphdr->csumcov);
    if ((csumcov < (sizeof(dctpUdpHdr) + pkt->chdr.doffset)) ||
	(csumcov > ((sizeof(dctpUdpHdr) + pkt->chdr.doffset + pkt->appdatalen)))) {
	dctpoPacketFree(pkt);
	return;
    }
    /* Step 2: Check ports and process TIMEWAIT state */
    if ((sock = dctpiGetNatV4IncomingSocket(pkt)) != NULL) {
	/* Found associated socket */
	dctpoSocketLock(sock);
	if (sock->state == DCTPSOCK_TIMEWAIT) {
	    if (pkt->chdr.type != DCTPPKT_RESET) {
		dctpiSendReset(NULL, pkt, DCTPRESET_NOCONNECTION, 0, 0, 0, DCTPENCAP_RAW);
	    }
	    dctpoSocketUnlock(sock);
	    dctpoPacketFree(pkt);
	    return;
	}
    } else {
	/* No associated socket, get rid of it */
	if (pkt->chdr.type != DCTPPKT_RESET) {
	    dctpiSendReset(NULL, pkt, DCTPRESET_NOCONNECTION, 0, 0, 0, DCTPENCAP_RAW);
	}
	dctpoPacketFree(pkt);
	return;
    }
    /* Step 3: Process LISTEN state */
    if ((nsock = rcvStep3ListenState(sock, pkt)) == NULL) {
	/* Invalid packet in LISTEN state, get rid of it */
	dctpoSocketUnlock(sock);
	dctpoPacketFree(pkt);
	return;
    } else if (nsock != sock) {
	sock = nsock;
    }
    /* Do steps that don't need to know about encapsulation */
    if (rcvDoSteps(sock, pkt)) {
	dctpoPacketFree(pkt);
    }
    dctpoSocketUnlock(sock);
}

/*
 * Convert incoming NATv4 header to canonical format
 */
static int v4NatToCanonHdr(dctpPacket *pkt) {
    uint_t minhdrlen;

    /* Need to add setting ECN later */
    if (pkt->appdatalen < DCTP_NATMINPKTLEN) {
	return(-1);
    }
    pkt->chdr.type = pkt->dccphdr[5] & 0x0f;
    pkt->chdr.ccval = pkt->dccphdr[5] >> 4;
    pkt->chdr.sport = *((uint16_t *)&(pkt->dccphdr[0]));
    pkt->chdr.dport = *((uint16_t *)&(pkt->dccphdr[2]));
    pkt->chdr.seqno = ((uint64_t)dctpoNtohs(*((uint16_t *)&(pkt->dccphdr[6]))) << 32) +
	dctpoNtohl(*((uint32_t *)&(pkt->dccphdr[8])));
    pkt->chdr.hasAck = 0;
    pkt->chdr.shortSeq = 0;
    pkt->chdr.hasSC = 0;
    pkt->chdr.hasReset = 0;
    switch (pkt->chdr.type) {
    case DCTPPKT_REQUEST:
	minhdrlen = 16;
	pkt->chdr.hasSC = 1;
	pkt->chdr.scode = *((uint32_t *)&(pkt->dccphdr[12]));
	break;
    case DCTPPKT_RESPONSE:
	minhdrlen = 24;
	pkt->chdr.hasSC = 1;
	pkt->chdr.scode = *((uint32_t *)&(pkt->dccphdr[20]));
	pkt->chdr.hasAck = 1;
	break;
    case DCTPPKT_DATA:
	minhdrlen = 12;
	break;
    case DCTPPKT_ACK:
    case DCTPPKT_DATAACK:
    case DCTPPKT_CLOSEREQ:
    case DCTPPKT_CLOSE:
    case DCTPPKT_SYNC:
    case DCTPPKT_SYNCACK:
	minhdrlen = 20;
	pkt->chdr.hasAck = 1;
	break;
    case DCTPPKT_RESET:
	minhdrlen = 24;
	pkt->chdr.hasAck = 1;
	pkt->chdr.hasReset = 1;
	pkt->chdr.rcode = pkt->dccphdr[20];
	pkt->chdr.rdata1 = pkt->dccphdr[21];
	pkt->chdr.rdata2 = pkt->dccphdr[22];
	pkt->chdr.rdata3 = pkt->dccphdr[23];
	break;
    default:
	return(-1);
    }
    if (pkt->chdr.hasAck) {
	pkt->chdr.ackno = ((uint64_t)dctpoNtohs(*((uint16_t *)&(pkt->dccphdr[14]))) << 32) +
	    dctpoNtohl(*((uint32_t *)&(pkt->dccphdr[16])));
    }
    pkt->chdr.doffset = pkt->dccphdr[4]*4;
    if (pkt->chdr.doffset < minhdrlen) {
	return(-1);
    }
    pkt->chdr.options = pkt->dccphdr + minhdrlen;
    return(0);
}

/*
 * Do receive steps that operate on canonical header.  Each step returns 0 if
 * processing continues with the next step or -1 to stop processing and discard
 * packet.
 */
static int rcvDoSteps(dctpSocket *sock, dctpPacket *pkt) {
    /* Step 4: Prepare sequence numbers in REQUEST */
    if (rcvStep4RequestState(sock, pkt)) {
	return(-1);
    }
    /* Step 5: Prepare sequence numbers for Sync */
    if (rcvStep5Sync(sock, pkt)) {
	return(-1);
    }
    /* Step 6: Check sequence numbers */
    if (rcvStep6ChkSeqNums(sock, pkt)) {
	return(-1);
    }
    /* Step 7: Check for unexpected packet types */
    if (rcvStep7UnexPktTypes(sock, pkt)) {
	return(-1);
    }
    /* Step 8: Process options and mark achnowledgements */
    if (rcvStep8Options(sock, pkt)) {
	return(-1);
    }
    /* Step 9: Process reset */
    if (rcvStep9Reset(sock, pkt)) {
	return(-1);
    }
    /* Step 10: Process REQUEST state (second part) */
    if (rcvStep10Request(sock, pkt)) {
	return(-1);
    }
    /* Step 11: Process RESPOND state */
    if (rcvStep11Respond(sock, pkt)) {
	return(-1);
    }
    /* Step 12: Process PARTOPEN state */
    if (rcvStep12Partopen(sock, pkt)) {
	return(-1);
    }
    /* Step 13: Process CloseReq */
    if (rcvStep13CloseReq(sock, pkt)) {
	return(-1);
    }
    /* Step 14: Process Close */
    if (rcvStep14Close(sock, pkt)) {
	return(-1);
    }
    /* Step 15: Process Sync */
    if (rcvStep15Sync(sock, pkt)) {
	return(-1);
    }
    /* Step 16: Process data */
    if (rcvStep16ProcessData(sock, pkt)) {
	return(-1);
    }
    return(0);
}

static dctpSocket *rcvStep3ListenState(dctpSocket *sock, dctpPacket *pkt) {
    dctpSocket *nsock;

    if (sock->state == DCTPSOCK_LISTEN) {
	if ((pkt->chdr.type == DCTPPKT_REQUEST) ||
	    (sock->cookie != NULL)) {
	    if ((nsock = dctpiCloneSocket(sock, pkt)) == NULL) {
		return(NULL);
	    }
	    dctpoSocketUnlock(sock);
	    dctpoSocketLock(nsock);
	    nsock->state = DCTPSOCK_RESPOND;
	    nsock->iss = (sock->cookie) ? sock->cookie->iss : dctpoRandomSeqno();
	    nsock->gss = nsock->iss;
	    nsock->awl = nsock->iss;
	    nsock->awlinit = 1;
	    nsock->awh = nsock->iss;
	    nsock->gar = nsock->iss;
	    nsock->isr = (sock->cookie) ? sock->cookie->isr : pkt->chdr.seqno;
	    nsock->gsr = nsock->isr;
	    nsock->fgsr = dctpiSeqNoSub(nsock->isr, 1);
	    nsock->swl = nsock->isr;
	    nsock->swh = dctpiSeqNoAdd(nsock->gsr,
				       (nsock->remoteFeatsCurr.seqWindow -
					nsock->remoteFeatsCurr.seqWindow/4));
	    if (sock->cookie) {
		dctpoFree(sock->cookie);
		sock->cookie = NULL;
	    }
	    return(nsock);
	} else {
	    if (pkt->chdr.type != DCTPPKT_RESET) {
		dctpiSendReset(sock, pkt, DCTPRESET_NOCONNECTION, 0, 0, 0, sock->encap);
	    }
	    return(NULL);
	}
    } else {
	return(sock);
    }
}

static int rcvStep4RequestState(dctpSocket *sock, dctpPacket *pkt) {
    if (sock->state == DCTPSOCK_REQUEST) {
	if (((pkt->chdr.type == DCTPPKT_RESPONSE) || (pkt->chdr.type == DCTPPKT_RESET)) &&
	    validSeqNo(sock->awl, pkt->chdr.ackno, sock->awh)) {
		sock->gsr = pkt->chdr.seqno;
		sock->isr = pkt->chdr.seqno;
		sock->swl = sock->isr;
		sock->swh = dctpiSeqNoAdd(sock->gsr,
					  (sock->remoteFeatsCurr.seqWindow -
					   sock->remoteFeatsCurr.seqWindow/4));
		return(0);
	} else {
	    dctpiSendReset(sock, NULL, DCTPRESET_PACKETERROR, pkt->chdr.type,
			   0, 0, sock->encap);
	    return(-1);
	}
    }
    return(0);
}

static int rcvStep5Sync(dctpSocket *sock, dctpPacket *pkt) {
    if ((pkt->chdr.type == DCTPPKT_SYNC) || (pkt->chdr.type == DCTPPKT_SYNCACK)) {
	if (validSeqNo(sock->awl, pkt->chdr.ackno, sock->awh)) {
	    updateGSR(sock, pkt);
	    return(0);
	}
	return(-1);
    }
    return(0);
}

static int rcvStep6ChkSeqNums(dctpSocket *sock, dctpPacket *pkt) {
    uint64_t lswl, lawl;

    if ((sock->encap & DCTPENCAP_RAWANY) &&
	(pkt->chdr.shortSeq) &&
	(!(sock->flags & DCTPSOCK_O_SHORTSEQ))) {
	return(-1);
    }
    if ((pkt->chdr.type == DCTPPKT_CLOSEREQ) || (pkt->chdr.type == DCTPPKT_CLOSE) ||
	(pkt->chdr.type == DCTPPKT_RESET)) {
	lswl = (sock->gsr + 1) & DCTP_48BITMASK;
	lawl = sock->gar;
    } else {
	lswl = sock->swl;
	lawl = sock->awl;
    }
    if (!validSeqNo(lswl, pkt->chdr.seqno, sock->swh)) {
	dctpiSendSync(sock, (pkt->chdr.type == DCTPPKT_RESET) ? sock->gsr : pkt->chdr.seqno);
	return(-1);
    }
    if (((pkt->chdr.type == DCTPPKT_RESET) && (sock->state == DCTPSOCK_RESPOND) &&
	(pkt->chdr.ackno != 0)) &&
	((pkt->chdr.hasAck) && (!validSeqNo(lawl, pkt->chdr.ackno, sock->awh)))) {
	dctpiSendSync(sock, (pkt->chdr.type == DCTPPKT_RESET) ? sock->gsr : pkt->chdr.seqno);
	return(-1);
    }
    updateGSR(sock, pkt);
    if (pkt->chdr.hasAck && (pkt->chdr.type != DCTPPKT_SYNC)) {
	if (dctpiCmpSeqNo(pkt->chdr.ackno, sock->gar) > 0) {
	    sock->gar = pkt->chdr.ackno;
	}
    }
    return(0);
}

static int rcvStep7UnexPktTypes(dctpSocket *sock, dctpPacket *pkt) {
    if ((sock->server && (pkt->chdr.type == DCTPPKT_CLOSEREQ)) ||
	(sock->server && (pkt->chdr.type == DCTPPKT_RESPONSE)) ||
	(!sock->server && (pkt->chdr.type == DCTPPKT_REQUEST)) ||
	((sock->state >= DCTPSOCK_OPEN) && (pkt->chdr.type == DCTPPKT_REQUEST) &&
	 (dctpiCmpSeqNo(pkt->chdr.seqno, sock->osr) >= 0)) ||
	 ((sock->state >= DCTPSOCK_OPEN) && (pkt->chdr.type == DCTPPKT_RESPONSE) &&
	  (dctpiCmpSeqNo(pkt->chdr.seqno, sock->osr) >= 0)) ||
	 ((sock->state == DCTPSOCK_RESPOND) && (pkt->chdr.type == DCTPPKT_DATA))) {
	dctpiSendSync(sock, pkt->chdr.seqno);
	return(-1);
    }
    return(0);
}

static int rcvStep8Options(dctpSocket *sock, dctpPacket *pkt) {
    /* Process options */
    if (processOptions(sock, pkt)) {
	return(-1);
    }
    return(0);
}

static int rcvStep9Reset(dctpSocket *sock, dctpPacket *pkt) {
    if (pkt->chdr.type == DCTPPKT_RESET) {
	/* Tear down connection */
	if (sock->ccidSenderFuncs) sock->ccidSenderFuncs->destroyCcidInfo(sock);
	if (sock->ccidRcvrFuncs) sock->ccidRcvrFuncs->destroyCcidInfo(sock);
	sock->state = DCTPSOCK_TIMEWAIT;
	dctpoSocketSignal(sock);
	dctpoStartTimer(&(sock->timer), DCTP_TIMEWAITTIME, timeWaitTimeout, sock);
	return(-1);
    }
    return(0);
}

int dctpiOpenCcids(dctpSocket *sock) {
    if ((sock->localFeatsCurr.ccid[0] > DCTP_MAXCCIDNUMBER) ||
	(ccidSenderOpenFuncs[sock->localFeatsCurr.ccid[0]] == NULL) ||
	(ccidSenderOpenFuncs[sock->localFeatsCurr.ccid[0]](sock) < 0) ||
	(sock->remoteFeatsCurr.ccid[0] > DCTP_MAXCCIDNUMBER) ||
	(ccidRcvrOpenFuncs[sock->remoteFeatsCurr.ccid[0]] == NULL) ||
	(ccidRcvrOpenFuncs[sock->remoteFeatsCurr.ccid[0]](sock) < 0)) {
	dctpiCloseConnection(sock, NULL);
	return(-1);
    } else {
	return(0);
    }
}

static int rcvStep10Request(dctpSocket *sock, dctpPacket *pkt) {
    if (sock->state == DCTPSOCK_REQUEST) {
	sock->state = DCTPSOCK_PARTOPEN;
	dctpoSocketSignal(sock);
	if (dctpiOpenCcids(sock) < 0) {
	    return(-1);
	}
	sock->timeval = DCTP_INITRTO;
	dctpoStartTimer(&(sock->timer), sock->timeval, partOpenTimeout, sock);
    }
    return(0);
}

static int rcvStep11Respond(dctpSocket *sock, dctpPacket *pkt) {
    if (sock->state == DCTPSOCK_RESPOND) {
	if (pkt->chdr.type == DCTPPKT_REQUEST) {
	    dctpiSendResponse(sock, NULL);
	    if (sock->flags & DCTPSOCK_O_INITCOOKIE) {
		dctpiDestroySock(sock);
	    }
	} else {
	    sock->osr = pkt->chdr.seqno;
	    sock->state = DCTPSOCK_OPEN;
	    if (dctpiOpenCcids(sock) < 0) {
		return(-1);
	    }
	    dctpiAddWaitingSocket(sock);
	    dctpoSocketSignal(sock->parent);
	}
    }
    return(0);
}

static int rcvStep12Partopen(dctpSocket *sock, dctpPacket *pkt) {
    if (sock->state == DCTPSOCK_PARTOPEN) {
	if (pkt->chdr.type == DCTPPKT_RESPONSE) {
	    dctpiSendAck(sock);
	} else if (pkt->chdr.type != DCTPPKT_SYNC) {
	    sock->osr = pkt->chdr.seqno;
	    sock->state = DCTPSOCK_OPEN;
	    dctpoStopTimer(&(sock->timer));
	    dctpoSocketSignal(sock);
	}
    }
    return(0);
}

static int rcvStep13CloseReq(dctpSocket *sock, dctpPacket *pkt) {
    if ((pkt->chdr.type == DCTPPKT_CLOSEREQ) && (sock->state < DCTPSOCK_CLOSEREQ)) {
	sock->timeval = (sock->ccidSenderFuncs) ?
	    2*(sock->ccidSenderFuncs->getCurrentRTO(sock)) :
	    2*DCTP_INITRTO;
	sock->state = DCTPSOCK_CLOSING;
	if (sock->ccidSenderFuncs) sock->ccidSenderFuncs->destroyCcidInfo(sock);
	if (sock->ccidRcvrFuncs) sock->ccidRcvrFuncs->destroyCcidInfo(sock);
	dctpiSendClose(sock);
	dctpoSocketSignal(sock);
	/* Set CLOSING timer */
	dctpoStartTimer(&(sock->timer), sock->timeval, dctpiCloseReqTimeout, sock);
    }
    return(0);
}

static int rcvStep14Close(dctpSocket *sock, dctpPacket *pkt) {
    if (pkt->chdr.type == DCTPPKT_CLOSE) {
	sock->state = DCTPSOCK_CLOSED;
	if (sock->ccidSenderFuncs) sock->ccidSenderFuncs->destroyCcidInfo(sock);
	if (sock->ccidRcvrFuncs) sock->ccidRcvrFuncs->destroyCcidInfo(sock);
	dctpiSendReset(sock, NULL, DCTPRESET_CLOSED, 0, 0, 0, sock->encap);
	dctpoSocketSignal(sock);
    }
    return(0);
}

static int rcvStep15Sync(dctpSocket *sock, dctpPacket *pkt) {
    if (pkt->chdr.type == DCTPPKT_SYNC) {
	dctpiSendSyncAck(sock, pkt->chdr.seqno);
    }
    return(0);
}

static int rcvStep16ProcessData(dctpSocket *sock, dctpPacket *pkt) {
    if (sock->ccidRcvrFuncs) sock->ccidRcvrFuncs->rcvAckblePacket(sock, pkt);
    if (sock->ccidSenderFuncs) sock->ccidSenderFuncs->rcvAckblePacket(sock, pkt);
    switch (pkt->chdr.type) {
    case DCTPPKT_DATA:
    case DCTPPKT_DATAACK:
	return(dctpiAddRcvPacket(sock, pkt));
    case DCTPPKT_REQUEST:
    case DCTPPKT_RESPONSE:
	/* Data from these packet types can only be accepted once */
	if ((pkt->appdatalen > 0) && (!sock->reqDataQd)) {
	    sock->reqDataQd = 1;
	    return(dctpiAddRcvPacket(sock, pkt));
	}
    default:
	return(-1);
    }
}

/*
 * Process the options in a packet
 */
 static int processOptions(dctpSocket *sock, dctpPacket *pkt) {
    uint8_t *ops = pkt->chdr.options;
    uint_t mandatory = 0;     /* 0 -- no mandatory processing for this pass
			       * 1 -- the mandatory option was found on the previous pass
			       * 2 -- the option on the previous pass was treated as mandatory
			       */
    uint_t op, oplen, plen;
    uint64_t ndpcount, avAckno = (uint64_t)(-1);
    uint32_t tsecho, etime;

    while (ops < pkt->appdata) {
	mandatory = 1;
	if (mandatory >= 2) mandatory = 0;
	else if (mandatory == 1) mandatory = 2;
	op = *ops++;
	/* Check one-byte options first */
	if (op == DCTPOPTION_PADDING) {
	    /* Nothing to do for padding, go to next pass */
	    continue;
	} else if (op == DCTPOPTION_MANDATORY) {
	    /* Option found in next pass will be treated as mandatory */
	    mandatory = 1;
	} else if (op == DCTPOPTION_SLOWRECEIVER) {
	    if (sock->ccidSenderFuncs) sock->ccidSenderFuncs->rcvdSlowRcvr(sock);
	} else if (op <= DCTPOPTION_1BYTEMAX) {
	    /* Ignore unkown options unless mandatory */
	    if (mandatory) {
		sock->state = DCTPSOCK_CLOSED;
		dctpoSocketSignal(sock);
		if (sock->ccidSenderFuncs) sock->ccidSenderFuncs->destroyCcidInfo(sock);
		if (sock->ccidRcvrFuncs) sock->ccidRcvrFuncs->destroyCcidInfo(sock);
		dctpiSendReset(sock, pkt, DCTPRESET_MANDATORYERROR, op, 0, 0, sock->encap);
		return(-1);
	    }
	} else {
	    /* Now check mutli-byte options */
	    oplen = *ops++;
	    if ((oplen < DCTPOPTION_MINLENGTH) || ((ops + oplen - 2) > pkt->appdata)) {
		if (mandatory) {
		    goto mandatoryError;
		} else {
		    return(0);
		}
	    }
	    switch (op) {
	    case DCTPOPTION_CHANGEL:
	    case DCTPOPTION_CHANGER:
	    case DCTPOPTION_CONFIRML:
	    case DCTPOPTION_CONFIRMR:
		if (oplen < DCTPOPTION_FEATMINLENGTH) {
		    if (mandatory) goto mandatoryError;
		} else if (dctpiRcvdFeatureOption(sock, pkt, op, ops, oplen, mandatory) < 0) {
		    goto mandatoryError;
		}
		ops += oplen - 2;
		break;
	    case DCTPOPTION_INITCOOKIE:
		/* Option processed before this point, so skip */
		ops += oplen - 2;
		break;
	    case DCTPOPTION_NDPCOUNT:
		if (oplen > DCTPOPTION_NDPCOUNTMAXLEN) {
		    if (mandatory) goto mandatoryError;
		    else return(0);
		}
		oplen -= 2;
		ndpcount = 0;
		while (oplen--) {
		    ndpcount <<= 8;
		    ndpcount |= *ops++;
		}
		if (sock->ccidSenderFuncs) sock->ccidSenderFuncs->rcvdNdpCount(sock, pkt, ndpcount);
		break;
	    case DCTPOPTION_ACKVECTORN0:
	    case DCTPOPTION_ACKVECTORN1:
	    case DCTPOPTION_DATADROPPED:
		if (!pkt->chdr.hasAck) {
		    /* Ignore on packets with no ackno */
		    if (mandatory) goto mandatoryError;
		    else {
			ops += oplen - 2;
			break;
		    }
		}
		if (sock->ccidSenderFuncs) {
		    if (op == DCTPOPTION_DATADROPPED) {
			sock->ccidSenderFuncs->rcvdDataDropped(sock, pkt->chdr.ackno, ops, oplen - 2);
		    } else {
			if (avAckno == (uint64_t)(-1)) {
			    avAckno = pkt->chdr.ackno;
			}
			avAckno = 
			    sock->ccidSenderFuncs->rcvdAckVector(sock, avAckno, ops, oplen - 2,
								 (op == DCTPOPTION_ACKVECTORN0) ? 0 : 1);
		    }
		}
		ops += oplen - 2;
		break;
	    case DCTPOPTION_TIMESTAMP:
		if (oplen != DCTPOPTION_TIMESTAMPLENGTH) {
		    if (mandatory) goto mandatoryError;
		    else {
			ops += oplen - 2;
			break;
		    }
		}
		sock->pendingTimestampEcho = 1;
		sock->pendingTimestamp = *((uint32_t *)ops);
		sock->pendingTimestampRcvd = dctpoNow();
		ops += 4;
		break;
	    case DCTPOPTION_TIMESTAMPECHO:
		switch (oplen) {
		case DCTPOPTION_TIMESTAMPECHOSMALLLEN:
		    plen = 0;
		    break;
		case DCTPOPTION_TIMESTAMPECHOMEDLEN:
		    plen = 2;
		    break;
		case DCTPOPTION_TIMESTAMPECHOLONGLEN:
		    plen = 4;
		    break;
		default:
		    if (mandatory) goto mandatoryError;
		    else {
			ops += oplen - 2;
			continue;
		    }
		}
		tsecho = ((*ops++) << 24) | ((*ops++) << 16) | ((*ops++) << 8) | (*ops++);
		etime = 0;
		while (plen--) {
		    etime <<= 8;
		    etime |= *ops++;
		}
		if (sock->ccidSenderFuncs)
		    sock->ccidSenderFuncs->rcvdTimestampEcho(sock, tsecho, etime);
		break;
	    case DCTPOPTION_ELAPSEDTIME:
		if (!pkt->chdr.hasAck ||
		    ((oplen != DCTPOPTION_ELAPSEDTIMESMALL) &&
		     (oplen != DCTPOPTION_ELAPSEDTIMELARGE))) {
		    if (mandatory) goto mandatoryError;
		    else {
			ops += oplen - 2;
			break;
		    }
		}
		if (sock->ccidSenderFuncs) {
		    sock->ccidSenderFuncs->rcvdElapsedTime(sock, pkt->chdr.ackno,
							   (oplen == DCTPOPTION_ELAPSEDTIMESMALL) ?
							   ((*ops << 8) | *(ops + 1)) :
							   ((*ops << 24) | (*(ops + 1) << 16) |
							    (*(ops + 2) << 8) | *(ops + 3)));
		}
		ops += oplen - 2;
		break;
	    case DCTPOPTION_DATACHECKSUM:
	    default:
		if ((op >= DCTPOPTION_CCIDOPSTART) && (op <= DCTPOPTION_CCIDOPEND)) {
		    if (sock->ccidSenderFuncs &&
			sock->ccidSenderFuncs->processCcidOption(sock, pkt, op, oplen - 2, ops) &&
			mandatory) {
			goto mandatoryError;
		    }
		} else if (mandatory) {
		    goto mandatoryError;
		}
		ops += oplen - 2;
	    }
	}
    }
    return(0);
 mandatoryError:
    /* Here for mandatory option with error.  Send reset and terminate */
    sock->state = DCTPSOCK_CLOSED;
    dctpoSocketSignal(sock);
    if (sock->ccidSenderFuncs) sock->ccidSenderFuncs->destroyCcidInfo(sock);
    if (sock->ccidRcvrFuncs) sock->ccidRcvrFuncs->destroyCcidInfo(sock);
    dctpiSendReset(sock, pkt, DCTPRESET_MANDATORYERROR, op,
		   (ops < pkt->appdata) ? *ops++ : 0,
		   (ops < pkt->appdata) ? *ops++ : 0, sock->encap);
    return(-1);
}

/*
 * Useful 48-bit circular arithmentic routines
 */

/*
 * validSeqNo -- returns 1 if wl <= no <= wh, 0 otherwise
 */
static int validSeqNo(uint64_t wl, uint64_t no, uint64_t wh) {
    if (dctpiCmpSeqNo(no, wl) < 0) {
	return(0);
    }
    if (dctpiCmpSeqNo(no, wh) > 0) {
	return(0);
    }
    return(1);
}

/*
 * dctpiSeqNoAdd -- adds to 48-bit numbers, returns result
 */
uint64_t dctpiSeqNoAdd(uint64_t n1, uint64_t n2) {
    return((n1 + n2) & DCTP_48BITMASK);
}

/*
 * dctpiSeqNoSub -- subtracts to 48-bit numbers, returns result
 */
uint64_t dctpiSeqNoSub(uint64_t n1, uint64_t n2) {
    return((n1 - n2) & DCTP_48BITMASK);
}

/*
 * dctpiCmpSeqNo -- compares to 48-bit numbers in circular space.
 *
 * Returns:  -1 if n1 < n2
 *            0 if n1 == n2
 *            1 if n1 > n2
 */
int dctpiCmpSeqNo(uint64_t n1, uint64_t n2) {
    if (n1 == n2) return(0);
    n1 <<= 16;
    n2 <<= 16;
    if (((int64_t)n1 - (int64_t)n2) < 0) return(-1);
    else return(1);
}

/*
 * Update Greatest Sequence Received and related state
 */
static void updateGSR(dctpSocket *sock, dctpPacket *pkt) {
    uint64_t swltemp;

    if (dctpiCmpSeqNo(pkt->chdr.seqno, sock->gsr) > 0) {
	sock->gsr = pkt->chdr.seqno;
	swltemp = dctpiSeqNoSub(sock->gsr, sock->remoteFeatsCurr.seqWindow/4);
	if (dctpiCmpSeqNo(sock->swl, swltemp) > 0) sock->swl = swltemp;
	sock->swh = dctpiSeqNoAdd(sock->gsr,
				  (sock->remoteFeatsCurr.seqWindow -
				   sock->remoteFeatsCurr.seqWindow/4));
    }
}

/*
 * Process timeout in the TIMEWAIT state
 */
static void timeWaitTimeout(void *arg) {
    dctpSocket *sock = (dctpSocket *)arg;

    dctpoSocketLock(sock);
    if (sock->destroyed) {
	dctpoSocketUnlock(sock);
	return;
    }
    if (sock->destroy) {
	dctpiDestroySock((dctpSocket *)arg);
    }
    dctpoSocketUnlock(sock);
    return;
}

/*
 * Process timeout in PARTOPEN state
 */
static void partOpenTimeout(void *arg) {
    dctpSocket *sock = (dctpSocket *)arg;

    dctpoSocketLock(sock);
    if (sock->destroyed) {
	dctpoSocketUnlock(sock);
	return;
    }
    if (sock->timeval < DCTP_MAXPARTOPENTIMEOUT) {
	sock->timeval *= 2;
	if (sock->timeval > DCTP_MAXPARTOPENTIMEOUT) {
	    sock->timeval = DCTP_MAXPARTOPENTIMEOUT;
	}
	dctpiSendAck(sock);
	dctpoStartTimer(&(sock->timer), sock->timeval, partOpenTimeout, sock);
    } else {
	sock->state = DCTPSOCK_CLOSED;
	dctpiSendReset(sock, NULL, DCTPRESET_ABORTED, 0, 0, 0, sock->encap);
	if (sock->ccidSenderFuncs) {
	    sock->ccidSenderFuncs->destroyCcidInfo(sock);
	}
	if (sock->ccidRcvrFuncs) {
	    sock->ccidRcvrFuncs->destroyCcidInfo(sock);
	}
	dctpoSocketSignal(sock);
    }
    dctpoSocketUnlock(sock);
}

/*
 * Process timeout in CLOSEREQ or CLOSE state
 */
void dctpiCloseReqTimeout(void *arg) {
    dctpSocket *sock = (dctpSocket *)arg;

    dctpoSocketLock(sock);
    if (sock->destroyed) {
	dctpoSocketUnlock(sock);
	return;
    }
    if (sock->timeval < DCTP_MAXCLOSETIMEOUT) {
	sock->timeval *= 2;
	if (sock->timeval > DCTP_MAXCLOSETIMEOUT) {
	    sock->timeval = DCTP_MAXCLOSETIMEOUT;
	}
	if (sock->state == DCTPSOCK_CLOSEREQ) {
	    dctpiSendClosereq(sock);
	} else {
	    dctpiSendClose(sock);
	}
	dctpoStartTimer(&(sock->timer), sock->timeval, dctpiCloseReqTimeout, sock);
    } else if (sock->state == DCTPSOCK_CLOSEREQ) {
	sock->state = DCTPSOCK_CLOSED;
	if (sock->destroy) dctpiDestroySock(sock);
    } else {
	sock->state = DCTPSOCK_TIMEWAIT;
	dctpoStartTimer(&(sock->timer), DCTP_TIMEWAITTIME, timeWaitTimeout, sock);
    }
    dctpoSocketUnlock(sock);
}

