/*
 * dctpFeatureNegotiation.c -- support routines for feature negotiations.  Internal code only.
 *
 * The only non-static functions are dctpiAddFeatures, which adds feature negotiation
 * options to outbound packets, and dctpiRcvdFeatureOptions, which performs feature option
 * processing on incoming packets.
 *
 * 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 "dctpInternal.h"
#include "dctpSupport.h"

/*
 * Handle feature negotiation timeout
 */
static void featureTimeout(void *arg) {
    dctpSocket *sock = (dctpSocket *)arg;

    dctpoLog(DCTPLOG_DEBUG, "featureTimeout: negotiation timed out for sock 0x%x, server %d\n",
	     (uint32_t)sock, sock->server);
    dctpoSocketLock(sock);
    if (sock->destroyed) {
	dctpoSocketUnlock(sock);
	return;
    }
    if (sock->featTimeval < DCTPFEAT_MAXTIMEOUT) {
	sock->featTimeval *= 2;
    }
    sock->sendFeatureNegotiation = 1;
    if ((sock->state == DCTPSOCK_OPEN) || (sock->state == DCTPSOCK_PARTOPEN)) {
	dctpiSendAck(sock);
    }
    dctpoSocketUnlock(sock);
}

static uint_t confirmFeatCcid(uint8_t *ops, dctpFeatures *curr, dctpFeatures *desired) {
    uint_t i;

    *ops++ = curr->ccid[0];
    for (i = 0; (i < DCTPFEAT_CCIDSIZE) && (desired->ccid[i] != 0); ++i) {
	*ops++ = desired->ccid[i];
    }
    return(i + 1);
}

static uint_t confirmFeatAllowShortSeqNos(uint8_t *ops, dctpFeatures *curr,
					  dctpFeatures *desired) {
    *ops++ = curr->allowShortSeqnos;
    *ops++ = 0;
    if (desired->allowShortSeqnos) {
	*ops++ = 1;
	return(3);
    } else {
	return(2);
    }
}

static uint_t confirmFeatSeqWindow(uint8_t *ops, dctpFeatures *curr,
				   dctpFeatures *desired) {
    *((uint16_t *)ops) = dctpoHtons(curr->seqWindow >> 32);
    *((uint32_t *)(ops + 2)) = dctpoHtonl(curr->seqWindow & DCTP_32BITMASK);
    return(6);
}

static uint_t confirmFeatEcnIncapable(uint8_t *ops, dctpFeatures *curr,
				      dctpFeatures *desired) {
    *ops++ = curr->ecnIncapable;
    *ops++ = 0;
    if (desired->ecnIncapable) {
	*ops++ = 1;
	return(3);
    } else {
	return(2);
    }
}

static uint_t confirmFeatAckRatio(uint8_t *ops, dctpFeatures *curr,
				  dctpFeatures *desired) {
    *((uint16_t *)ops) = dctpoHtons(curr->ackRatio);
    return(2);
}

static uint_t confirmFeatSendAckVector(uint8_t *ops, dctpFeatures *curr,
				       dctpFeatures *desired) {
    *ops++ = curr->sendAckVector;
    *ops++ = 0;
    if (desired->sendAckVector) {
	*ops++ = 1;
	return(3);
    } else {
	return(2);
    }
}

static uint_t confirmFeatSendNdpCount(uint8_t *ops, dctpFeatures *curr,
				       dctpFeatures *desired) {
    *ops++ = curr->sendNDPCount;
    *ops++ = 0;
    if (desired->sendNDPCount) {
	*ops++ = 1;
	return(3);
    } else {
	return(2);
    }
}

static uint_t confirmFeatMinCsumCov(uint8_t *ops, dctpFeatures *curr,
				       dctpFeatures *desired) {
    *ops++ = curr->minCsumCov;
    *ops = desired->minCsumCov;
    return(2);
}

static uint_t confirmFeatCheckDataCsum(uint8_t *ops, dctpFeatures *curr,
				       dctpFeatures *desired) {
    *ops++ = curr->checkDataCsum;
    *ops++ = 0;
    if (desired->checkDataCsum) {
	*ops++ = 1;
	return(3);
    } else {
	return(2);
    }
}

static uint_t changeFeatCcid(uint8_t *ops, dctpFeatures *curr) {
    uint_t i;

    for (i = 0; (i < DCTPFEAT_CCIDSIZE) && (curr->ccid[i] != 0); ++i) {
	*ops++ = curr->ccid[i];
    }
    return(i);
}

static uint_t changeFeatAllowShortSeqNos(uint8_t *ops, dctpFeatures *curr) {
    *ops = curr->allowShortSeqnos;
    return(1);
}

static uint_t changeFeatSeqWindow(uint8_t *ops, dctpFeatures *curr) {
    *((uint16_t *)ops) = dctpoHtons(curr->seqWindow >> 32);
    *((uint32_t *)(ops + 2)) = dctpoHtonl(curr->seqWindow & DCTP_32BITMASK);
    return(6);
}

static uint_t changeFeatEcnIncapable(uint8_t *ops, dctpFeatures *curr) {
    *ops = curr->ecnIncapable;
    return(1);
}

static uint_t changeFeatAckRatio(uint8_t *ops, dctpFeatures *curr) {
    *((uint16_t *)ops) = dctpoHtons(curr->ackRatio);
    return(2);
}

static uint_t changeFeatSendAckVector(uint8_t *ops, dctpFeatures *curr) {
    *ops = curr->sendAckVector;
    return(1);
}

static uint_t changeFeatSendNdpCount(uint8_t *ops, dctpFeatures *curr) {
    *ops = curr->sendNDPCount;
    return(1);
}

static uint_t changeFeatMinCsumCov(uint8_t *ops, dctpFeatures *curr) {
    *ops = curr->minCsumCov;
    return(1);
}

static uint_t changeFeatCheckDataCsum(uint8_t *ops, dctpFeatures *curr) {
    *ops = curr->checkDataCsum;
    return(1);
}

static uint_t (*confirmFeatVals[DCTPFEAT_MAXFEATNO + 1])
    (uint8_t *ops, dctpFeatures *curr, dctpFeatures *desired) = {
	NULL,
	confirmFeatCcid,
	confirmFeatAllowShortSeqNos,
	confirmFeatSeqWindow,
	confirmFeatEcnIncapable,
	confirmFeatAckRatio,
	confirmFeatSendAckVector,
	confirmFeatSendNdpCount,
	confirmFeatMinCsumCov,
	confirmFeatCheckDataCsum
    };

static uint_t (*changeFeatVals[DCTPFEAT_MAXFEATNO + 1])
    (uint8_t *ops, dctpFeatures *curr) = {
	NULL,
	changeFeatCcid,
	changeFeatAllowShortSeqNos,
	changeFeatSeqWindow,
	changeFeatEcnIncapable,
	changeFeatAckRatio,
	changeFeatSendAckVector,
	changeFeatSendNdpCount,
	changeFeatMinCsumCov,
	changeFeatCheckDataCsum
    };

/*
 * Add feature negotiation options to a packet
 */
static void addFeatures(dctpSocket *sock, dctpPacket *pkt,
		      uint8_t *options, uint_t *oplen, uint_t local) {
    uint_t i, confirmOp, changeOp;
    uint8_t *state;
    dctpFeatures *curr, *desired;

    if (local) {
	confirmOp = DCTPOPTION_CONFIRML;
	changeOp = DCTPOPTION_CHANGEL;
	state = sock->localFeatsState;
	curr = &(sock->localFeatsCurr);
	desired = &(sock->localFeatsDesired);
    } else {
	confirmOp = DCTPOPTION_CONFIRMR;
	changeOp = DCTPOPTION_CHANGER;
	state = sock->remoteFeatsState;
	curr = &(sock->remoteFeatsCurr);
	desired = &(sock->remoteFeatsDesired);
    }
    for (i = 1; i <= DCTPFEAT_MAXFEATNO; ++i) {
	switch (state[i]) {
	case DCTPFEAT_CONFIRMING:
	    options[*oplen] = confirmOp;
	    options[*oplen + 2] = i;
	    options[*oplen + 1] = 3 +
		confirmFeatVals[i](&(options[*oplen + 3]), curr, desired);
	    *oplen += options[*oplen + 1];
	    state[i] = DCTPFEAT_STABLE;
	    break;
	case DCTPFEAT_MANUNSTABLE:
	    sock->fgss = pkt->chdr.seqno;
	    state[i] = DCTPFEAT_MANCHANGE;
	case DCTPFEAT_MANCHANGE:
	    options[(*oplen)++] = DCTPOPTION_MANDATORY;
	case DCTPFEAT_UNSTABLE:
	    if (state[i] == DCTPFEAT_UNSTABLE) {
		sock->fgss = pkt->chdr.seqno;
		state[i] = DCTPFEAT_CHANGING;
	    }
	case DCTPFEAT_CHANGING:
	    options[*oplen] = changeOp;
	    options[*oplen + 2] = i;
	    options[*oplen + 1] = 3 +
		changeFeatVals[i](&(options[*oplen + 3]), desired);
	    *oplen += options[*oplen + 1];
	    break;
	}
    }
}

void dctpiAddFeatures(dctpSocket *sock, dctpPacket *pkt,
		      uint8_t *options, uint_t *oplen) {
    uint8_t *state;
    uint_t i;

    if (sock->sendFeatureNegotiation) {
	/* Add local features */
	addFeatures(sock, pkt, options, oplen, 1);
	/* Add remote features */
	addFeatures(sock, pkt, options, oplen, 0);
	sock->sendFeatureNegotiation = 0;
	if (sock->featureNegotiationInProgress == 0) {
	    sock->featTimeval = (sock->ccidSenderFuncs) ?
		sock->ccidSenderFuncs->getCurrentRTO(sock)*2 : DCTP_INITRTO;
	}
	sock->featureNegotiationInProgress = 1;
	dctpoStartTimer(&(sock->featTimer), sock->featTimeval, featureTimeout, sock);
    } else if (sock->featureNegotiationInProgress) {
	sock->featureNegotiationInProgress = 0;
	state = sock->localFeatsState;
	for (i = 0; i < DCTPFEAT_MAXFEATNO; ++i) {
	    if (state[i] != DCTPFEAT_STABLE) {
		sock->featureNegotiationInProgress = 1;
		break;
	    }
	}
	if (sock->featureNegotiationInProgress == 0) {
	    state = sock->remoteFeatsState;
	    for (i = 0; i < DCTPFEAT_MAXFEATNO; ++i) {
		if (state[i] != DCTPFEAT_STABLE) {
		    sock->featureNegotiationInProgress = 1;
		    break;
		}
	    }
	}
	if (sock->featureNegotiationInProgress == 0) {
	    dctpoStopTimer(&(sock->featTimer));
	}
    }
}

static void mkEmptyConfirm(dctpSocket *sock, uint_t op, uint_t feat) {
     uint_t i;

     for (i = 0; i < DCTPSOCK_MAXEMPTYCONFIRMS; i += 2) {
	 if (sock->emptyConfirms[i] == 0) {
	     sock->emptyConfirms[i] = (op == DCTPOPTION_CHANGER) ?
		 DCTPOPTION_CONFIRML : DCTPOPTION_CONFIRMR;
	     sock->emptyConfirms[i + 1] = feat;
	     break;
	 }
     }
}

static int rcvdChangeCcidValid(dctpSocket *sock, uint8_t *ops, uint_t oplen, uint_t op) {
    dctpFeatures *curr, *desired;
    uint_t i, j;

    if (op == DCTPOPTION_CHANGER) {
	curr = &(sock->localFeatsCurr);
	desired = &(sock->localFeatsDesired);
    } else {
	curr = &(sock->remoteFeatsCurr);
	desired = &(sock->remoteFeatsDesired);
    }
    for (i = 0; i < oplen; ++i) {
	for (j = 0; j < DCTPFEAT_CCIDSIZE; ++j) {
	    if (desired->ccid[j] == ops[i]) {
		/* Found a match, close current ccid */
		if (sock->ccidSenderFuncs) sock->ccidSenderFuncs->destroyCcidInfo(sock);
		if (sock->ccidRcvrFuncs) sock->ccidRcvrFuncs->destroyCcidInfo(sock);
		/* Install new ccid, if time */
		curr->ccid[0] = ops[i];
		if ((sock->state == DCTPSOCK_PARTOPEN) || (sock->state == DCTPSOCK_OPEN)) {
		    dctpiOpenCcids(sock);
		}
		return(1);
	    }
	}
    }
    /* No match to our acceptable list */
    return(0);
}

static int rcvdChangeAllowShortSeqNosValid(dctpSocket *sock, uint8_t *ops,
					   uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CHANGER) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen != 1) return(0);
    if ((*ops != 0) && (!(sock->flags & DCTPSOCK_O_SHORTSEQ))) {
	return(0);
    }
    curr->allowShortSeqnos = *ops;
    return(1);
}

static int rcvdChangeSeqWindowValid(dctpSocket *sock, uint8_t *ops,
				    uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CHANGER) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen != 6) return(0);
    curr->seqWindow = (((uint64_t)dctpoNtohs(*((uint16_t *)ops))) << 32) |
	dctpoNtohl(*((uint32_t *)(ops + 2)));
    return(0);
}

static int rcvdChangeEcnIncapableValid(dctpSocket *sock, uint8_t *ops,
				       uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CHANGER) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen != 1) return(0);
    if ((*ops != 0) && (!(sock->flags & DCTPSOCK_O_ECNINCAPABLE))) {
	return(0);
    }
    curr->ecnIncapable = *ops;
    return(1);
}

static int rcvdChangeAckRatioValid(dctpSocket *sock, uint8_t *ops,
				   uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CHANGER) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen != 2) return(0);
    curr->ackRatio = dctpoNtohs(*((uint16_t *)ops));
    return(0);
}

static int rcvdChangeSendAckVectorValid(dctpSocket *sock, uint8_t *ops,
					uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CHANGER) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen != 1) return(0);
    curr->sendAckVector = *ops;
    return(1);
}

static int rcvdChangeSendNdpCountValid(dctpSocket *sock, uint8_t *ops,
				       uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CHANGER) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen != 1) return(0);
    curr->sendNDPCount = *ops;
    return(1);
}

static int rcvdChangeMinCsumCovValid(dctpSocket *sock, uint8_t *ops,
					uint_t oplen, uint_t op) {
    dctpFeatures *curr, *desired;

    if (op == DCTPOPTION_CHANGER) {
	curr = &(sock->localFeatsCurr);
	desired = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
	desired = &(sock->remoteFeatsCurr);
    }
    if (oplen != 1) return(0);
    if (*ops > 15) return(0);
    if ((*ops != 0) && ((desired->minCsumCov == 0) || (desired->minCsumCov < *ops))) {
	return(0);
    }
    curr->minCsumCov = *ops;
    return(1);
}

static int rcvdChangeCheckDataCsumValid(dctpSocket *sock, uint8_t *ops,
				       uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CHANGER) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen != 1) return(0);
    if ((*ops != 0) && (!(sock->flags & DCTPSOCK_O_CHECKDATACSUM))) {
	return(0);
    }
    curr->checkDataCsum = *ops;
    return(1);
}

static int rcvdConfirmCcidValid(dctpSocket *sock,  uint8_t *ops,
				uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CONFIRMR) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen == 0) return(1);  /* Couldn't come to agreement, no change */
    if ((*ops < 2) || (*ops > 3)) return(0);  /* Invalid value */
    if (*ops != curr->ccid[0]) {
	if (sock->ccidSenderFuncs) sock->ccidSenderFuncs->destroyCcidInfo(sock);
	if (sock->ccidRcvrFuncs) sock->ccidRcvrFuncs->destroyCcidInfo(sock);
	/* Install new ccid, if time */
	curr->ccid[0] = *ops;
	if ((sock->state == DCTPSOCK_PARTOPEN) || (sock->state == DCTPSOCK_OPEN)) {
	    dctpiOpenCcids(sock);
	}
    }
    return(1);
}

static int rcvdConfirmAllowShortSeqNosValid(dctpSocket *sock,  uint8_t *ops,
					    uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CONFIRMR) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen == 0) return(1);
    curr->allowShortSeqnos = *ops;
    return(1);
}

static int rcvdConfirmSeqWindowValid(dctpSocket *sock,  uint8_t *ops,
				     uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CONFIRMR) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen < 6) return(0);
    curr->seqWindow = (((uint64_t)dctpoNtohs(*((uint16_t *)ops))) << 32) |
	dctpoNtohl(*((uint32_t *)(ops + 2)));
    return(1);
}

static int rcvdConfirmEcnIncapableValid(dctpSocket *sock,  uint8_t *ops,
					uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CONFIRMR) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen == 0) return(1);
    curr->ecnIncapable = *ops;
    return(1);
}

static int rcvdConfirmAckRatioValid(dctpSocket *sock,  uint8_t *ops,
					uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CONFIRMR) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen < 2) return(1);
    curr->ackRatio  = dctpoNtohs(*((uint16_t *)ops));
    return(1);
}

static int rcvdConfirmSendAckVectorValid(dctpSocket *sock,  uint8_t *ops,
					uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CONFIRMR) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen == 0) return(1);
    curr->sendAckVector = *ops;
    return(1);
}

static int rcvdConfirmSendNdpCountValid(dctpSocket *sock,  uint8_t *ops,
					uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CONFIRMR) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen == 0) return(1);
    curr->sendNDPCount = *ops;
    return(1);
}

static int rcvdConfirmMinCsumCovValid(dctpSocket *sock,  uint8_t *ops,
				      uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CONFIRMR) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen == 0) return(1);
    curr->minCsumCov = *ops;
    return(1);
}

static int rcvdConfirmCheckDataCsumValid(dctpSocket *sock,  uint8_t *ops,
					 uint_t oplen, uint_t op) {
    dctpFeatures *curr;

    if (op == DCTPOPTION_CONFIRMR) {
	curr = &(sock->localFeatsCurr);
    } else {
	curr = &(sock->remoteFeatsCurr);
    }
    if (oplen == 0) return(1);
    curr->checkDataCsum = *ops;
    return(1);
}

static int (*rcvdChangeFeatValid[DCTPFEAT_MAXFEATNO + 1])
    (dctpSocket *sock, uint8_t *ops, uint_t oplen, uint_t op) = {
	NULL,
	rcvdChangeCcidValid,
	rcvdChangeAllowShortSeqNosValid,
	rcvdChangeSeqWindowValid,
	rcvdChangeEcnIncapableValid,
	rcvdChangeAckRatioValid,
	rcvdChangeSendAckVectorValid,
	rcvdChangeSendNdpCountValid,
	rcvdChangeMinCsumCovValid,
	rcvdChangeCheckDataCsumValid,
    };

static int (*rcvdConfirmFeatValid[DCTPFEAT_MAXFEATNO + 1])
    (dctpSocket *sock, uint8_t *ops, uint_t oplen, uint_t op) = {
	NULL,
	rcvdConfirmCcidValid,
	rcvdConfirmAllowShortSeqNosValid,
	rcvdConfirmSeqWindowValid,
	rcvdConfirmEcnIncapableValid,
	rcvdConfirmAckRatioValid,
	rcvdConfirmSendAckVectorValid,
	rcvdConfirmSendNdpCountValid,
	rcvdConfirmMinCsumCovValid,
	rcvdConfirmCheckDataCsumValid,
    };

int dctpiRcvdFeatureOption(dctpSocket *sock, dctpPacket *pkt, uint_t op,
			   uint8_t *ops, uint_t oplen, uint_t mandatory) {
    uint8_t *state;

    oplen -= 3;
    switch (op) {
    case DCTPOPTION_CHANGER:
    case DCTPOPTION_CONFIRMR:
	state = sock->localFeatsState;
	break;
    case DCTPOPTION_CHANGEL:
    case DCTPOPTION_CONFIRML:
	state = sock->remoteFeatsState;
	break;
    default:
	dctpoLog(DCTPLOG_ERR, "dctpiRcvdFeatureOptions: invalid option %d\n", op);
	return(mandatory ? -1 : 0);
    }
    /* Step 1: unkown features */
    if ((*ops == 0) || (*ops > DCTPFEAT_MAXFEATNO)) {
	if (mandatory) {
	    return(-1);
	} else if ((op == DCTPOPTION_CHANGER) || (op == DCTPOPTION_CHANGEL)) {
	    mkEmptyConfirm(sock, op, *ops);
	}
    }
 
   /* Step 2: check for reordering */
    if ((state[*ops] == DCTPFEAT_UNSTABLE) || (state[*ops] == DCTPFEAT_MANUNSTABLE) ||
	(dctpiCmpSeqNo(pkt->chdr.seqno, sock->fgsr) <= 0) ||
	(((op == DCTPOPTION_CONFIRMR) || (op == DCTPOPTION_CONFIRML)) &&
	 (!pkt->chdr.hasAck || (dctpiCmpSeqNo(pkt->chdr.ackno, sock->fgss) < 0)))) {
	/* Reodered, ignore */
	return(0);
    }
    /* Step 3: process Change options */
    if ((op == DCTPOPTION_CHANGER) || (op == DCTPOPTION_CHANGEL)) {
	if (rcvdChangeFeatValid[*ops](sock, ops + 1, oplen, op)) {
	    state[*ops] = DCTPFEAT_CONFIRMING;
	    sock->sendFeatureNegotiation = 1;
	} else if (mandatory) {
	    return(-1);
	} else {
	    mkEmptyConfirm(sock, op, *ops);
	}
    }
    /* Step 4; process Confirm options */
    if (((state[*ops] == DCTPFEAT_CHANGING) || (state[*ops] == DCTPFEAT_MANCHANGE)) &&
	((op == DCTPOPTION_CONFIRMR) || (op == DCTPOPTION_CONFIRML))) {
	if ((oplen > 0) && !rcvdConfirmFeatValid[*ops](sock, ops + 1, oplen, op)) {
	    return(-1);
	}
	state[*ops] = DCTPFEAT_STABLE;
    }
    return(0);
}

