diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/common/packet.h ns-2.34_dccp/common/packet.h --- ns-2.34_orig/common/packet.h 2010-02-15 16:05:14.139989795 +0100 +++ ns-2.34_dccp/common/packet.h 2010-03-08 15:09:55.787595291 +0100 @@ -182,8 +182,19 @@ // AOMDV packet static const packet_t PT_AOMDV = 61; + // DCCP packets +static const packet_t PT_DCCP = 62 ; +static const packet_t PT_DCCP_REQ = 63 ; +static const packet_t PT_DCCP_RESP = 64; +static const packet_t PT_DCCP_ACK = 65; +static const packet_t PT_DCCP_DATA = 66; +static const packet_t PT_DCCP_DATAACK = 67; +static const packet_t PT_DCCP_CLOSE = 68; +static const packet_t PT_DCCP_CLOSEREQ = 69; +static const packet_t PT_DCCP_RESET = 70; + // insert new packet types here -static packet_t PT_NTYPE = 62; // This MUST be the LAST one +static packet_t PT_NTYPE = 71; // This MUST be the LAST one enum packetClass { @@ -380,6 +391,17 @@ // Bell Labs (PackMime OL) name_[PT_BLTRACE]="BellLabsTrace"; + // DCCP + name_[PT_DCCP]="DCCP"; + name_[PT_DCCP_REQ]="DCCP_Request"; + name_[PT_DCCP_RESP]="DCCP_Response"; + name_[PT_DCCP_ACK]="DCCP_Ack"; + name_[PT_DCCP_DATA]="DCCP_Data"; + name_[PT_DCCP_DATAACK]="DCCP_DataAck"; + name_[PT_DCCP_CLOSE]="DCCP_Close"; + name_[PT_DCCP_CLOSEREQ]="DCCP_CloseReq"; + name_[PT_DCCP_RESET]="DCCP_Reset"; + // AOMDV patch name_[PT_AOMDV]= "AOMDV"; @@ -706,11 +728,34 @@ return (p); } +#include "dccp/dccp_packets.h" inline void Packet::free(Packet* p) { + hdr_dccp *dccph; if (p->fflag_) { if (p->ref_count_ == 0) { + + //free DCCP options on dropped packets + switch (HDR_CMN(p)->ptype_){ + case PT_DCCP: + case PT_DCCP_REQ: + case PT_DCCP_RESP: + case PT_DCCP_ACK: + case PT_DCCP_DATA: + case PT_DCCP_DATAACK: + case PT_DCCP_CLOSE: + case PT_DCCP_CLOSEREQ: + case PT_DCCP_RESET: + dccph = hdr_dccp::access(p); + if (dccph->options_ != NULL){ + delete (dccph->options_); + } + break; + default: + ; + } + /* * A packet's uid may be < 0 (out of a event queue), or * == 0 (newed but never gets into the event queue. @@ -733,9 +778,30 @@ inline Packet* Packet::copy() const { - + hdr_dccp *dccph, *dccph_p; Packet* p = alloc(); memcpy(p->bits(), bits_, hdrlen_); + + //copy DCCP options_, since it is a pointer + switch (HDR_CMN(this)->ptype_){ + case PT_DCCP: + case PT_DCCP_REQ: + case PT_DCCP_RESP: + case PT_DCCP_ACK: + case PT_DCCP_DATA: + case PT_DCCP_DATAACK: + case PT_DCCP_CLOSE: + case PT_DCCP_CLOSEREQ: + case PT_DCCP_RESET: + dccph = hdr_dccp::access(this); + dccph_p = hdr_dccp::access(p); + if (dccph->options_ != NULL) + dccph_p->options_ = new DCCPOptions(*dccph->options_); + break; + default: + ; + } + if (data_) p->data_ = data_->copy(); p->txinfo_.init(&txinfo_); diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/bsd_queue.h ns-2.34_dccp/dccp/bsd_queue.h --- ns-2.34_orig/dccp/bsd_queue.h 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/bsd_queue.h 2010-03-08 15:09:55.797597400 +0100 @@ -0,0 +1,541 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: /repoman/r/ncvs/src/sys/sys/queue.h,v 1.54 2002/08/05 05:18:43 alfred Exp $ + */ +//#ifndef _SYS_QUEUE_H_ +#ifndef _NS_BSD_QUEUE_H_ +#define _NS_BSD_QUEUE_H_ + +/* #include */ +#define __offsetof(type, field) ((size_t)(&((type *)0)->field)) + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - - - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_REVERSE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * + */ + +/* Undefine the ones allocated in lib/bsd-list.h */ +#undef LIST_HEAD +#undef LIST_ENTRY +#undef LIST_INIT +#undef LIST_INSERT_AFTER +#undef LIST_INSERT_BEFORE +#undef LIST_INSERT_HEAD +#undef LIST_REMOVE + +#define QUEUE_MACRO_DEBUG 0 +#if QUEUE_MACRO_DEBUG +/* Store the last 2 places the queue element or head was altered */ +struct qm_trace { + char * lastfile; + int lastline; + char * prevfile; + int prevline; +}; + +#define TRACEBUF struct qm_trace trace; +#define TRASHIT(x) do {(x) = (void *)-1;} while (0) + +#define QMD_TRACE_HEAD(head) do { \ + (head)->trace.prevline = (head)->trace.lastline; \ + (head)->trace.prevfile = (head)->trace.lastfile; \ + (head)->trace.lastline = __LINE__; \ + (head)->trace.lastfile = __FILE__; \ +} while (0) + +#define QMD_TRACE_ELEM(elem) do { \ + (elem)->trace.prevline = (elem)->trace.lastline; \ + (elem)->trace.prevfile = (elem)->trace.lastfile; \ + (elem)->trace.lastline = __LINE__; \ + (elem)->trace.lastfile = __FILE__; \ +} while (0) + +#else +#define QMD_TRACE_ELEM(elem) +#define QMD_TRACE_HEAD(head) +#define TRACEBUF +#define TRASHIT(x) +#endif /* QUEUE_MACRO_DEBUG */ + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_NEXT(curelm, field) = \ + SLIST_NEXT(SLIST_NEXT(curelm, field), field); \ + } \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *) \ + ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + if ((STAILQ_NEXT(curelm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((curelm), field);\ + } \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \ + if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ +} while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +/* + * Tail queue functions. + */ +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_HEAD(head2); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + QMD_TRACE_HEAD(head); \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + TRASHIT((elm)->field.tqe_next); \ + TRASHIT((elm)->field.tqe_prev); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + + +#ifdef _KERNEL + +/* + * XXX insque() and remque() are an old way of handling certain queues. + * They bogusly assumes that all queue heads look alike. + */ + +struct quehead { + struct quehead *qh_link; + struct quehead *qh_rlink; +}; + +#ifdef __GNUC__ + +static __inline void +insque(void *a, void *b) +{ + struct quehead *element = (struct quehead *)a, + *head = (struct quehead *)b; + + element->qh_link = head->qh_link; + element->qh_rlink = head; + head->qh_link = element; + element->qh_link->qh_rlink = element; +} + +static __inline void +remque(void *a) +{ + struct quehead *element = (struct quehead *)a; + + element->qh_link->qh_rlink = element->qh_rlink; + element->qh_rlink->qh_link = element->qh_link; + element->qh_rlink = 0; +} + +#else /* !__GNUC__ */ + +void insque(void *a, void *b); +void remque(void *a); + +#endif /* __GNUC__ */ + +#endif /* _KERNEL */ + +#endif /* !_NS_BSD_QUEUE_H_ */ +//#endif /* !_SYS_QUEUE_H_ */ diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp_ackv.cc ns-2.34_dccp/dccp/dccp_ackv.cc --- ns-2.34_orig/dccp/dccp_ackv.cc 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp_ackv.cc 2010-03-08 15:09:55.797597400 +0100 @@ -0,0 +1,920 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp_ackv.cc,v 1.21 2004/03/24 14:06:38 nilmat-8 Exp $ */ + +#include +#include "dccp_ackv.h" + +/* Static string representation of packet states */ +char* DCCPAckVector::packet_state_str_[DCCP_NUM_PACKET_STATES] = +{"Received", "ECN-marked", "Reserved", "Not Received" }; + +//private methods + +/* Add items to the head of the vector + * arg: state - packet state to set on items + * runlen - run length to set on items + * count - number of similar items to add + * ret: true if successful + * false if more space is needed and doubleMaxSize() fails. + */ +bool DCCPAckVector::addItems(dccp_packet_state state, u_int8_t runlen, int count){ + u_int8_t temp = state; + int new_head, walker; + u_int8_t item = (temp << DCCP_RUN_LENGTH_BITS | (runlen & DCCP_RUN_LENGTH_MASK)); + if (size_+count > max_size_){ + //increase size and try again + return (doubleMaxSize() && addItems(state,runlen,count)); + } + + if(size_ != 0) + seq_head_ +=(runlen+1)*count; + + new_head = (head_ - count + max_size_) % max_size_; + walker = new_head; + + while(walker != head_){ + vector_[walker] = item; + walker = (walker + 1) % max_size_; + } + + size_ += count; + head_ = new_head; + + return true; +} + +/* Find where in the vector a info about a seqnum exists + * arg: seqnum - sequence number to find + * where - resulting position in the vector + * seq_start - starting sequence number corresponding to where + * seq_end - ending sequence number corresponding to where + * ret: true if found + * false otherwise (where, seq_start, seq_end invalid) + */ +bool DCCPAckVector::findSeqNum(u_int32_t seqnum, u_int16_t *where, u_int32_t *seq_start, u_int32_t *seq_end){ + if (size_ == 0 || seqnum > seq_head_ || seqnum < seq_tail_ ) + return false; + + *where = head_; + *seq_start = seq_head_; + *seq_end = 0; + int stop = (tail_ + 1) % max_size_; + + do{ + *seq_end = *seq_start - (vector_[*where] & DCCP_RUN_LENGTH_MASK); + + if (*seq_end <= seqnum && *seq_start >= seqnum){ + return true; + } + + *where = (*where + 1) % max_size_; + *seq_start = *seq_end - 1; + } while(*where != stop); + + //should never get here!! + return false; +} + +/* Increase the maximum size of the vector by a factor two + * until DCCP_MAX_ACKV_SIZE is reached. + * ret: true if successful, false if already at DCCP_MAX_ACKV_SIZE + */ +bool DCCPAckVector::doubleMaxSize(){ + if (max_size_ == DCCP_MAX_ACKV_SIZE) + return false; + + int new_max_size = max_size_ * 2; + + if (new_max_size > DCCP_MAX_ACKV_SIZE) + new_max_size = DCCP_MAX_ACKV_SIZE; + + u_char *new_vector = new u_char[new_max_size]; + int walker = head_; + + for (int i = 0; i < size_; i++){ + new_vector[i] = vector_[walker]; + walker = (walker + 1) % max_size_; + } + max_size_ = new_max_size; + + if (size_ > 0) { + head_ = 0; + tail_ = size_-1; + } + + delete [] vector_; + vector_ = new_vector; + return true; +} + +/* Add an acknowledgment to ack history + * arg: seqnum - sequence number of the sent ack packet + */ +void DCCPAckVector::addToAckHistory(u_int32_t seqnum){ + struct dccp_ackv_hist_entry *elm = STAILQ_FIRST(&ack_hist_); + if(elm != NULL && elm->seq_sent_ >= seqnum){ + fprintf(stderr, "DCCPAckVector::addToAckHistory - Sequence number to send is old! (seqnum %d < seq_sent_ %d)\n",seqnum, elm->seq_sent_); + fflush(stdout); + abort(); + } else if (elm != NULL && elm->ack_num_ > seq_head_) { + fprintf(stderr, "DCCPAckVector::addToAckHistory - Last Ack number %d is larger than seq_head_ %d\n", elm->ack_num_, seq_head_); + fflush(stdout); + abort(); + } else { + /*if (elm != NULL && elm->ack_num_ == seq_head_) { + fprintf(stderr, "DCCPAckVector::addToAckHistory - Last Ack number %d is equal to seq_head_ %d\n", elm->ack_num_, seq_head_); + fflush(stdout); + abort(); + }*/ + struct dccp_ackv_hist_entry *new_entry = new struct dccp_ackv_hist_entry; + new_entry->seq_sent_ = seqnum; + new_entry->ack_num_ = seq_head_; + new_entry->ene_ = ene_; + STAILQ_INSERT_HEAD(&ack_hist_, new_entry, linfo_); + } +} + +/* Remove all items in the ack history */ +void DCCPAckVector::clearAckHistory(){ + struct dccp_ackv_hist_entry *elm, *elm2; + + /* Empty packet history */ + elm = STAILQ_FIRST(&ack_hist_); + while (elm != NULL) { + elm2 = STAILQ_NEXT(elm, linfo_); + delete elm; + elm = elm2; + } + + STAILQ_INIT(&ack_hist_); +} + +//public methods + +/* Return a string reprenting a packet state + * arg: state - packet state + * ret: a string representation of state + */ +const char* DCCPAckVector::packetStateAsStr(dccp_packet_state state){ + if (state < DCCP_NUM_PACKET_STATES && state >= 0) + return packet_state_str_[state]; + else + return "Unknown"; +} + +/* Constructor + * arg: initialSize - initial size for the ack vector + * ret: a new DCCPAckVector + */ +DCCPAckVector::DCCPAckVector(u_int16_t initialSize){ + vector_ = new u_char[initialSize]; + size_ = 0; + max_size_ = initialSize; + ene_ = 0; + head_ = 0; + tail_ = 0; + seq_head_ = 0; + seq_tail_ = 0; + p_walk_ = 0; + seq_p_walk_ = 0; + delta_p_walk_ = 0; + p_walking_ = false; + p_reverse_ = false; + i_walk_ = 0; + seq_i_walk_ = 0; + i_walking_ = false; + + ackv_cons_[DCCP_PACKET_RECV][DCCP_PACKET_RECV] = DCCP_PACKET_RECV; + ackv_cons_[DCCP_PACKET_RECV][DCCP_PACKET_ECN] = DCCP_PACKET_RECV; + ackv_cons_[DCCP_PACKET_RECV][DCCP_PACKET_RESERVED] = DCCP_PACKET_RESERVED; + ackv_cons_[DCCP_PACKET_RECV][DCCP_PACKET_NOT_RECV] = DCCP_PACKET_RECV; + ackv_cons_[DCCP_PACKET_ECN][DCCP_PACKET_RECV] = DCCP_PACKET_ECN; + ackv_cons_[DCCP_PACKET_ECN][DCCP_PACKET_ECN] = DCCP_PACKET_ECN; + ackv_cons_[DCCP_PACKET_ECN][DCCP_PACKET_RESERVED] = DCCP_PACKET_RESERVED; + ackv_cons_[DCCP_PACKET_ECN][DCCP_PACKET_NOT_RECV] = DCCP_PACKET_ECN; + ackv_cons_[DCCP_PACKET_RESERVED][DCCP_PACKET_RECV] = DCCP_PACKET_RESERVED; + ackv_cons_[DCCP_PACKET_RESERVED][DCCP_PACKET_ECN] = DCCP_PACKET_RESERVED; + ackv_cons_[DCCP_PACKET_RESERVED][DCCP_PACKET_RESERVED] = DCCP_PACKET_RESERVED; + ackv_cons_[DCCP_PACKET_RESERVED][DCCP_PACKET_NOT_RECV] = DCCP_PACKET_RESERVED; + ackv_cons_[DCCP_PACKET_NOT_RECV][DCCP_PACKET_RECV] = DCCP_PACKET_RECV; + ackv_cons_[DCCP_PACKET_NOT_RECV][DCCP_PACKET_ECN] = DCCP_PACKET_ECN; + ackv_cons_[DCCP_PACKET_NOT_RECV][DCCP_PACKET_RESERVED] = DCCP_PACKET_RESERVED; + ackv_cons_[DCCP_PACKET_NOT_RECV][DCCP_PACKET_NOT_RECV] = DCCP_PACKET_NOT_RECV; + + STAILQ_INIT(&ack_hist_); +} + +/* Destructor */ +DCCPAckVector::~DCCPAckVector(){ + clearAckHistory(); + delete [] vector_; +} + +/* Add a packet to the ack vector (using ackv_cons_ when needed) + * Will fill with NOT_RECV packets when packets are missing + * arg: seqnum - sequence number of packet + * state - packet state + * ret: true if successful + * false if more space is needed and doubleMaxSize() fails + * or if packet is older than the oldest in the vector + */ +bool DCCPAckVector::addPacket(u_int32_t seqnum, dccp_packet_state state){ + int dist = seqnum - seq_head_; + u_int8_t runlen = 0; + + if (size_ == 0){ + seq_head_ = seqnum; + seq_tail_ = seqnum; + head_ = 0; + tail_ = max_size_-1; + } else if (dist > 1){ //some packets are missing + int num_fulls = (dist-2) / (DCCP_RUN_LENGTH_MASK+1); + int last_runlen = (dist-2) % (DCCP_RUN_LENGTH_MASK+1); + + if (size_ + num_fulls + 1 + 1 > max_size_) + return doubleMaxSize() && addPacket(seqnum, state); + + if (num_fulls > 0) + addItems(DCCP_PACKET_NOT_RECV, DCCP_RUN_LENGTH_MASK, num_fulls); + + addItems(DCCP_PACKET_NOT_RECV,last_runlen); + + } else if (dist == 1) { //expected packet + u_int8_t last_runlen = vector_[head_] & DCCP_RUN_LENGTH_MASK; + u_int8_t last_state = vector_[head_] >> DCCP_RUN_LENGTH_BITS; + if (last_runlen < DCCP_RUN_LENGTH_MASK && last_state == (u_int8_t) state){ + vector_[head_] += 1; + seq_head_ = seqnum; + return true; + } + + } else { + dccp_packet_state curr_state; + if (getState(seqnum, &curr_state)) + if (alterState(seqnum,ackv_cons_[curr_state][state])){ + return true; + } + + return false; + } + + if (!addItems(state, runlen)){ + return false; + } + + return true; +} + +/* Add a packet to the ack vector (using ackv_cons_ when needed) + * Will fill with NOT_RECV packets when packets are missing + * arg: seqnum - sequence number of packet + * ecn - packets ecn code point + * addNonce - if true, add packet nonce to ecn echo sum + * ret: true if successful + * false if more space is needed and doubleMaxSize() fails + * or if packet is older than the oldest in the vector + */ +bool DCCPAckVector::addPacket(u_int32_t seqnum, dccp_ecn_codepoint ecn, bool addNonce){ + dccp_packet_state state = DCCP_PACKET_RECV; + if (ecn == ECN_CE) + state = DCCP_PACKET_ECN; + + bool result = addPacket(seqnum, state); + if (result && addNonce && (ecn == ECN_ECT0 || ecn == ECN_ECT1)) + ene_ = ene_ ^ ecn; + + return result; +} + +/* Remove packets older and including given seqnum from ack vector + * arg: seqnum - sequence number of the largest packet to remove + */ +void DCCPAckVector::removePackets(u_int32_t seqnum){ + if (size_ == 0 || seqnum < seq_tail_) + return; + else if (seqnum > seq_head_) { + size_ = 0; + seq_tail_ = seq_head_; + tail_ = head_; + ene_ = 0; + return; + } + + u_int16_t where; + u_int32_t seq_start; + u_int32_t seq_end; + + u_int32_t dist = 0; + + if (findSeqNum(seqnum, &where, &seq_start, &seq_end)){ + dist = seq_start-seq_end; + if (dist > 0 && seq_start != seqnum){ + vector_[where] = vector_[where] - (seqnum-seq_end+1); + size_ = ((where - head_ + max_size_) % max_size_)+1; + tail_ = where; + seq_tail_ = seqnum + 1; + } else if (where == head_){ + size_ = 0; + seq_tail_ = seq_head_; + tail_ = head_; + ene_ = 0; + } else { + where = ((where -1 + max_size_) % max_size_); + size_ = ((where - head_ + max_size_) % max_size_)+1; + tail_ = where; + seq_tail_ = seqnum +1; + } + + + } + +} + +/* Merge two ack vectors. + * Will read packet states from ackv and add them to this vector + * using ackv_cons_. + * If (ackv->seq_first_ > this->seq_first_) + * pad with DCCP_PACKET_NOT_RECV until they match + * Discards all packets older than this->seq_last_. + * If this->getSize() == 0 + * use setAckVector() + * arg: ackv - ack vector to merge with + * ret: true if successful + * false if ackv == NULL or the upper limit on size is exceeded + * Note: Adds packet in seqnum order until either successful + * or out of space. + * will NOT change ecn nonce echo as needed by the state change + */ +bool DCCPAckVector::mergeWith(DCCPAckVector *ackv){ + bool result = false; + + if (ackv == NULL) + return false; + else if (ackv->getSize() == 0) + return true; + + if (size_ == 0){ + u_int16_t size; + size = ackv->getSize(); + u_char vect[size]; + if (ackv->getAckVector(vect, &size) > 0) + if (setAckVector(vect,size,ackv->getFirstSeqNum(),ackv->getENE())) + result = true; + } else { + u_int32_t seqnum; + dccp_packet_state state; + ackv->startReversePacketWalk(); + while(ackv->prevPacket(&seqnum, &state)){ + if (!addPacket(seqnum,state) && seqnum >= seq_tail_){ + fprintf(stdout,"DCCPAckVector::mergeWith() failed\n"); + result = false; + break; + } + } + } + + + return result; +} + +/* Alter state of one packet. + * Will split items as needed. Note will not alter + * arg: seqnum - sequence number of packet to add + * newstate - packets new state + * oldstate - the packets old state + * ret: true if successful + * false if packet is not in vector or size + * if the upper limit on size is exceeded + * Note: will not alter the vector on failure. + * will NOT change ecn nonce echo as needed by the state change + */ +bool DCCPAckVector::alterState(u_int32_t seqnum, dccp_packet_state newstate, dccp_packet_state *oldstate){ + u_int16_t where; + u_int32_t seq_start; + u_int32_t seq_end; + u_char items[3]; + int num_to_add = 0; + dccp_packet_state old_state; + u_int8_t old_runlen; + bool result = false; + + if (findSeqNum(seqnum, &where, &seq_start, &seq_end)){ + old_state = (dccp_packet_state) (vector_[where] >> DCCP_RUN_LENGTH_BITS); + + if (oldstate != NULL) + *oldstate = old_state; + + if (old_state != newstate){ + old_runlen = vector_[where] & DCCP_RUN_LENGTH_MASK; + if (old_runlen == 0){ + vector_[where] = ((u_int8_t) newstate) << DCCP_RUN_LENGTH_BITS; + result = true; + } else if (seqnum == seq_start || seqnum == seq_end){ + if (size_ + 1 > max_size_){ + //increase size and try again + result = (doubleMaxSize() && alterState(seqnum, newstate)); + } else { + if(seqnum == seq_start){ + items[0] = ((u_int8_t) newstate) << DCCP_RUN_LENGTH_BITS; + items[1] = (((u_int8_t) old_state) << DCCP_RUN_LENGTH_BITS) + + (old_runlen-1); + } else { + items[0] = (((u_int8_t) old_state) << DCCP_RUN_LENGTH_BITS) + + (old_runlen-1); + items[1] = ((u_int8_t) newstate) << DCCP_RUN_LENGTH_BITS; + } + num_to_add = 2; + } + } else { + if (size_ + 2 > max_size_){ + //increase size and try again + result = (doubleMaxSize() && alterState(seqnum, newstate)); + } else { + items[0] = (((u_int8_t) old_state) << DCCP_RUN_LENGTH_BITS) + + (old_runlen-1-(seqnum-seq_end)); + items[1] = ((u_int8_t) newstate) << DCCP_RUN_LENGTH_BITS; + items[2] = (((u_int8_t) old_state) << DCCP_RUN_LENGTH_BITS) + + (old_runlen-1-(seq_start-seqnum)); + num_to_add = 3; + } + } + } else + result = true; + } + + if (num_to_add > 1){ + if(where == tail_){ + tail_ = (tail_ + (num_to_add - 1)) % max_size_; + } else if (where == head_){ + head_ = (head_ - (num_to_add - 1) + max_size_) % max_size_; + where = head_; + } else { + int from = head_; + int to = (head_ - (num_to_add - 1) + max_size_) % max_size_; + head_ = to; + while (from != where){ + vector_[to] = vector_[from]; + from = (from + 1) % max_size_; + to = (to + 1) % max_size_; + } + where = (where - (num_to_add - 1) + max_size_) % max_size_; + } + for(int i = 0; i < num_to_add; i++){ + vector_[where] = items[i]; + where = (where + 1 ) % max_size_; + } + size_ += num_to_add - 1; + result = true; + } + + return result; + +} + +/* Returns the state of a packet + * arg: seqnum - packets sequence number + * state - the packets state (if found) + * ret: true if packet is found + * false if not + */ +bool DCCPAckVector::getState(u_int32_t seqnum, dccp_packet_state *state){ + if (size_ == 0 || seqnum > seq_head_ || seqnum < seq_tail_) + return false; + bool result = false; + u_int16_t where; + u_int32_t seq_start; + u_int32_t seq_end; + + if(findSeqNum(seqnum, &where, &seq_start, &seq_end)){ + *state = (dccp_packet_state) (vector_[where] >> DCCP_RUN_LENGTH_BITS); + result = true; + } + + return result; +} + +/* Start a walk through the ack vector packet by packet, + * descending on sequence number. + */ +void DCCPAckVector::startPacketWalk(){ + p_walk_ = head_; + seq_p_walk_ = seq_head_; + delta_p_walk_ = 0; + p_walking_ = (size_ != 0); + p_reverse_ = false; +} + +/* Next packet in walk + * arg: seqnum - sequence number of next packet + * state - packet state + * ret: true if packet is found + * false if packet is not found or walk not initiated. + */ +bool DCCPAckVector::nextPacket(u_int32_t *seqnum, dccp_packet_state *state){ + if (!p_walking_ || p_reverse_) + return false; + + u_int8_t runlen = vector_[p_walk_] & DCCP_RUN_LENGTH_MASK; + + if (delta_p_walk_ > runlen){ + p_walk_ = (p_walk_+1) % max_size_; + delta_p_walk_ = 0; + } + + *seqnum = seq_p_walk_; + *state = (dccp_packet_state) (vector_[p_walk_] >> DCCP_RUN_LENGTH_BITS); + + delta_p_walk_++; + + p_walking_ = (seq_p_walk_ != seq_tail_); + + seq_p_walk_--; + + return true; +} + +/* Start a walk through the ack vector packet by packet, + * ascending on sequence number. + */ +void DCCPAckVector::startReversePacketWalk(){ + p_walk_ = tail_; + seq_p_walk_ = seq_tail_; + delta_p_walk_ = 0; + p_walking_ = (size_ != 0); + p_reverse_ = true; +} + +/* Previous packet in walk + * arg: seqnum - sequence number of previous packet + * state - packet state + * ret: true if packet is found + * false if packet is not found or walk not initiated. + */ +bool DCCPAckVector::prevPacket(u_int32_t *seqnum, dccp_packet_state *state){ + if (!p_walking_ || !p_reverse_) + return false; + + u_int8_t runlen = vector_[p_walk_] & DCCP_RUN_LENGTH_MASK; + + if (delta_p_walk_ > runlen){ + p_walk_ = (p_walk_+max_size_-1) % max_size_; + delta_p_walk_ = 0; + } + + *seqnum = seq_p_walk_; + *state = (dccp_packet_state) (vector_[p_walk_] >> DCCP_RUN_LENGTH_BITS); + + delta_p_walk_++; + + p_walking_ = (seq_p_walk_ != seq_head_); + + seq_p_walk_++; + + return true; +} + +/* Start a walk through the ack vector, item for item + * Most recent interval first. + */ +void DCCPAckVector::startIntervalWalk(){ + i_walk_ = head_; + seq_i_walk_ = seq_head_; + i_walking_ = size_ != 0; +} + +/* Next interval in walk + * arg: interval - found interval + * seq_start - the sequence number that started the interval + * seq_end - the sequence number that ended the interval + * state - the state of the packet in the interval + * ret: true if an interval is found + * false if not or walk not initiated. + */ +bool DCCPAckVector::nextInterval(u_char *interval, u_int32_t *seq_start, u_int32_t *seq_end, dccp_packet_state *state){ + if (!i_walking_) + return false; + + *interval = vector_[i_walk_]; + *seq_start = seq_i_walk_; + *seq_end = seq_i_walk_ - (vector_[i_walk_] & DCCP_RUN_LENGTH_MASK); + *state = (dccp_packet_state) (vector_[i_walk_] >> DCCP_RUN_LENGTH_BITS); + + i_walking_ = (i_walk_ != tail_); + i_walk_ = (i_walk_ +1) % max_size_; + seq_i_walk_ = *seq_end - 1; + + return true; +} + +/* Return the ECN Nonce Echo + * ret: ECN Nonce Echo + */ +u_int8_t DCCPAckVector::getENE(){ + return ene_; +} + +/* Set the ECN Nonce Echo + * arg: ene - new ene + */ +void DCCPAckVector::setENE(u_int8_t ene){ + ene_ = ene; +} + +/* Chech if ack vector is empty + * ret: true if empty, otherwise false + */ +bool DCCPAckVector::isEmpty(){ + return (size_ == 0); +} + +/* Return the first sequence number represented in the ack vector + * ret: first sequence number in the vector + * 0 if empty + */ +u_int32_t DCCPAckVector::getFirstSeqNum(){ + return seq_head_; +} + +/* Return the current size of the ack vector + * ret: current size + */ +u_int32_t DCCPAckVector::getLastSeqNum(){ + return seq_tail_; +} + +/* Return the current size of the ack vector + * ret: current size + */ +int DCCPAckVector::getSize(){ + return size_; +} + +/* Return the ack vector + * arg: vect - array to store ack vector in + * size - size of vect + * ret: The size of returned ack vector (also in size) if successful + * DCCP_ACKV_EMPTY if its empty + * DCCP_ACKV_ERR_SIZE if size is too small + * (will set size to the size needed) + */ +int DCCPAckVector::getAckVector(u_char* vect, u_int16_t *size){ + if (size_ == 0) + return DCCP_ACKV_EMPTY; + else if (size_ > *size){ + *size = size_; + return DCCP_ACKV_ERR_SIZE; + } + + int walker = head_; + + for (int i = 0; i < size_; i++){ + vect[i] = vector_[walker]; + walker = (walker + 1) % max_size_; + } + *size = size_; + + return size_; +} + +/* Import an ack vector (clearing the old one) + * arg: vect - ack vector to set + * size - size of vect + * seq_start - sequnce number of the first packet in ack vector + * ene - the current ecn nonce echo + * ret: true if successful + * false if out of space + */ +bool DCCPAckVector::setAckVector(const u_char* vect, u_int16_t size, u_int32_t seq_start, u_int8_t ene){ + if (size > max_size_) { + size_ = 0; + return (doubleMaxSize() && setAckVector(vect, size, seq_start, ene)); + } + + u_int32_t seq_end = seq_start; + + for (int i = 0; i < size; i++){ + vector_[i] = vect[i]; + if (i == size-1) + seq_end -= vect[i] & DCCP_RUN_LENGTH_MASK; + else + seq_end -= (vect[i] & DCCP_RUN_LENGTH_MASK) + 1; + } + + size_ = size; + head_ = 0; + tail_ = size_-1; + seq_head_ = seq_start; + seq_tail_ = seq_end; + ene_ = ene; + + clearAckHistory(); + return true; +} + + +/* Return and mark the ackvector as sent in history + * arg: seqnum - sequence number of packet on which ackvector is added + * acknum - acknowledgement number on that packet (== seq_first_) + * vect - ack vector to send + * size - size of ackvector + * ret: same as getAckVector() + * Note: Fails hard if seq_first_ != acknum! + */ +int DCCPAckVector::sendAckVector(u_int32_t seqnum, u_int32_t acknum, u_char* vect, u_int16_t *size){ + if (acknum != seq_head_){ + fprintf(stdout, "DCCPAckVector::sendAckVector acknum %d != seq_head %d\n",acknum,seq_head_); + fflush(stdout); + abort(); + } + + int result = getAckVector(vect, size); + + if (result > 0) { + addToAckHistory(seqnum); + } + + return result; +} + +/* Add ack vector options and mark as sent in history + * arg: seqnum - sequence number of packet on which ackvector is added + * acknum - acknowledgement number on that packet (== seq_first_) + * options - DCCPOptions object to add ackvector to + * ret: DCCP_OPT_NO_ERR on success + * similar to getAckVector and addOptions() + */ +int DCCPAckVector::sendAckVector(u_int32_t seqnum, u_int32_t acknum, DCCPOptions *options){ + if (acknum != seq_head_){ + fprintf(stdout, "DCCPAckVector::sendAckVector acknum %d != seq_head %d\n",acknum,seq_head_); + fflush(stdout); + abort(); + } else if (size_ == 0){ + fprintf(stdout, "DCCPAckVector::sendAckVector size_ == 0\n"); + fflush(stdout); + abort(); + } + u_char vect[size_]; + u_int16_t size; + size = size_; + int result = getAckVector(vect,&size); + if (result > 0){ + //for now, assume that the ackvector can be contained in one option + if (ene_ == 0) + result = options->addOption(DCCP_OPT_ACK_VECTOR_N0, vect, size); + else + result = options->addOption(DCCP_OPT_ACK_VECTOR_N1, vect, size); + if (result == DCCP_OPT_NO_ERR){ + addToAckHistory(seqnum); + } else { + fprintf(stderr, "DCCPAckVector::sendAckVector - Failed to add ack vector to option: err %d, size %d\n",result, size); + fflush(stdout); + abort(); + } + + } else { + fprintf(stderr, "DCCPAckVector::sendAckVector - Get vector returned %d\n",result); + fflush(stdout); + abort(); + } + + return result; +} + +/* Check if state may be cleared due to an received ack. + * If ackv is present, check in that to see if an ack has been acked + * otherwise check only on acknum + * arg: acknum - acknowledgement number recv + * ackv - ackvector received (if any) + */ +void DCCPAckVector::ackRecv(u_int32_t acknum, DCCPAckVector *ackv){ + struct dccp_ackv_hist_entry *elm = NULL, *next_elm = NULL; + dccp_packet_state state; + u_int8_t ene_sub = 0; + + elm = STAILQ_FIRST(&ack_hist_); + while (elm != NULL){ + if (ackv == NULL) { + if (acknum == elm->seq_sent_) + break; + } else if (ackv->getState(elm->seq_sent_, &state) && (state == DCCP_PACKET_RECV || state == DCCP_PACKET_ECN)){ + break; + } + elm = STAILQ_NEXT(elm, linfo_); + } + + if (elm != NULL){ + ene_sub = elm->ene_; + ene_ = ene_ ^ (elm->ene_); + removePackets(elm->ack_num_); + do{ + next_elm = STAILQ_NEXT(elm, linfo_); + STAILQ_REMOVE(&ack_hist_,elm,dccp_ackv_hist_entry,linfo_); + delete elm; + elm = next_elm; + } while (elm != NULL); + + //subtract ene from later acks + elm = STAILQ_FIRST(&ack_hist_); + while (elm != NULL) { + elm->ene_ = (elm->ene_) ^ ene_sub; + elm = STAILQ_NEXT(elm, linfo_); + } + } + +} + +/* Print ack vector state including intervals and ack history + * Note: Uses interval walk + */ +void DCCPAckVector::print(){ + if (isEmpty()) { + fprintf(stdout, "DCCPAckVector :: Ack vector is empty (max_size_ %d)\n",max_size_); + } else { + fprintf(stdout, "DCCPAckVector :: size_ %d, max_size_ %d, head_ %d, tail_ %d, seq_head_ %d, seq_tail_ %d, ene_ %d\n", size_, max_size_,head_,tail_,seq_head_,seq_tail_, ene_); + startIntervalWalk(); + u_char interval; + u_int32_t seq_start; + u_int32_t seq_end; + dccp_packet_state state; + + while(nextInterval(&interval, &seq_start, &seq_end, &state)){ + fprintf(stdout, "interval 0x%X (%d): state %s, runlen %d, (seq %d - %d)\n",interval, interval, packetStateAsStr(state), seq_start-seq_end,seq_start,seq_end); + } + } + printHistory(); +} + +/* Print all packets + * arg: asc - if true, print in ascending order, otherwise descending + * Note: uses packet walk + */ +void DCCPAckVector::printPackets(bool asc){ + if (isEmpty()) { + fprintf(stdout, "DCCPAckVector :: Ack vector is empty (max_size_ %d)\n",max_size_); + } else { + fprintf(stdout, "DCCPAckVector :: size_ %d, max_size_ %d, head_ %d, tail_ %d, seq_head_ %d, seq_tail_ %d, ene_ %d\n", size_, max_size_,head_,tail_,seq_head_,seq_tail_, ene_); + u_int32_t seq_num; + dccp_packet_state state; + + if (asc){ + startReversePacketWalk(); + while(prevPacket(&seq_num,&state)){ + fprintf(stdout, "Packet : seq %d state %s\n",seq_num, packetStateAsStr(state)); + } + } else { + startPacketWalk(); + while(nextPacket(&seq_num,&state)){ + fprintf(stdout, "Packet : seq %d state %s\n",seq_num, packetStateAsStr(state)); + } + } + } +} + +/* Print ack history */ +void DCCPAckVector::printHistory(){ + struct dccp_ackv_hist_entry *elm = STAILQ_FIRST(&ack_hist_); + if (elm == NULL) + fprintf(stdout, "DCCPAckVector :: Ack history is empty\n"); + else { + fprintf(stdout, "DCCPAckVector :: Ack history:\n"); + while (elm != NULL){ + fprintf(stdout, "Seq %d sent with ack %d and ene %d\n", elm->seq_sent_, elm->ack_num_,elm->ene_); + elm = STAILQ_NEXT(elm, linfo_); + } + } +} + + + + + + + + diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp_ackv.h ns-2.34_dccp/dccp/dccp_ackv.h --- ns-2.34_orig/dccp/dccp_ackv.h 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp_ackv.h 2010-03-08 15:09:55.787595291 +0100 @@ -0,0 +1,352 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp_ackv.h,v 1.14 2004/02/23 18:05:31 nilmat-8 Exp $ */ + +#ifndef ns_dccp_ackv_h +#define ns_dccp_ackv_h + +#include "config.h" + +#include "bsd_queue.h" +#include "dccp_types.h" +#include "dccp_opt.h" + +#define DCCP_RUN_LENGTH_BITS 6 +#define DCCP_RUN_LENGTH_MASK 0x3F + +#define DCCP_PACKET_STATE_BITS 2 +#define DCCP_PACKET_STATE_MASK 0xC0 + +#define DCCP_MAX_ACKV_SIZE 0xFFFF + +#define DCCP_ACKV_OK 0 +#define DCCP_ACKV_EMPTY -1 +#define DCCP_ACKV_ERR_SIZE -2 + +//ack history +STAILQ_HEAD(dccp_ackv_hist,dccp_ackv_hist_entry); + +struct dccp_ackv_hist_entry { + STAILQ_ENTRY(dccp_ackv_hist_entry) linfo_; + u_int32_t seq_sent_; + u_int32_t ack_num_; + u_int8_t ene_; +}; + +/* Class DCCPAckVector represents an ack vector. + Implemented as the October 2003 draft suggests */ +class DCCPAckVector { +private: + //packet state names + static char* packet_state_str_[DCCP_NUM_PACKET_STATES]; + + u_char* vector_; //the real ack vector as a byte array + u_int16_t size_; //current size of vector + u_int16_t max_size_; //maximum size of vector (can increase) + u_int16_t head_,tail_; //head and tail in the array + u_int32_t seq_head_, seq_tail_; //seq num of head and tail + u_int8_t ene_; //ecn nonce echo + + //packet walking variables + u_int16_t p_walk_; //current item in the vector + u_int32_t seq_p_walk_; //current seq num + int delta_p_walk_; //current location within an element + bool p_walking_; //true if a packet walk is initiated + bool p_reverse_; //true if an reverse packet walk is initiated + + //interval walking variables + u_int16_t i_walk_; //current item in the vector + u_int32_t seq_i_walk_; //current seq num + bool i_walking_; //true if an interval walk is initiated + + //history of acknowledgents (ack vectors) sent + struct dccp_ackv_hist ack_hist_; + + /* Add items to the head of the vector + * arg: state - packet state to set on items + * runlen - run length to set on items + * count - number of similar items to add + * ret: true if successful + * false if more space is needed and doubleMaxSize() fails. + */ + bool addItems(dccp_packet_state state, u_int8_t runlen, int count = 1); + + /* Find where in the vector a info about a seqnum exists + * arg: seqnum - sequence number to find + * where - resulting position in the vector + * seq_start - starting sequence number corresponding to where + * seq_end - ending sequence number corresponding to where + * ret: true if found + * false otherwise (where, seq_start, seq_end invalid) + */ + bool findSeqNum(u_int32_t seqnum, u_int16_t *where, u_int32_t *seq_start, u_int32_t *seq_end); + + /* Increase the maximum size of the vector by a factor two + * until DCCP_MAX_ACKV_SIZE is reached. + * ret: true if successful, false if already at DCCP_MAX_ACKV_SIZE + */ + bool doubleMaxSize(); + + /* Add an acknowledgment to ack history + * arg: seqnum - sequence number of the sent ack packet + */ + void addToAckHistory(u_int32_t seqnum); + + /* Remove all items in the ack history */ + void clearAckHistory(); + +public: + //ack vector conistency matrix + dccp_packet_state ackv_cons_[DCCP_NUM_PACKET_STATES][DCCP_NUM_PACKET_STATES]; + + /* Return a string reprenting a packet state + * arg: state - packet state + * ret: a string representation of state + */ + static const char* packetStateAsStr(dccp_packet_state state); + + /* Constructor + * arg: initialSize - initial size for the ack vector + * ret: a new DCCPAckVector + */ + DCCPAckVector(u_int16_t initialSize); + + /* Destructor */ + ~DCCPAckVector(); + + /* Add a packet to the ack vector (using ackv_cons_ when needed) + * Will fill with NOT_RECV packets when packets are missing + * arg: seqnum - sequence number of packet + * state - packet state + * ret: true if successful + * false if more space is needed and doubleMaxSize() fails + * or if packet is older than the oldest in the vector + */ + bool addPacket(u_int32_t seqnum, dccp_packet_state state); + + /* Add a packet to the ack vector (using ackv_cons_ when needed) + * Will fill with NOT_RECV packets when packets are missing + * arg: seqnum - sequence number of packet + * ecn - packets ecn code point + * addNonce - if true, add packet nonce to ecn echo sum + * ret: true if successful + * false if more space is needed and doubleMaxSize() fails + * or if packet is older than the oldest in the vector + */ + bool addPacket(u_int32_t seqnum, dccp_ecn_codepoint ecn, bool addNonce); + + /* Remove packets older and including given seqnum from ack vector + * arg: seqnum - sequence number of the largest packet to remove + */ + void removePackets(u_int32_t seqnum); + + /* Merge two ack vectors. + * Will read packet states from ackv and add them to this vector + * using ackv_cons_. + * If (ackv->seq_first_ > this->seq_first_) + * pad with DCCP_PACKET_NOT_RECV until they match + * Discards all packets older than this->seq_last_. + * If this->getSize() == 0 + * use setAckVector() + * arg: ackv - ack vector to merge with + * ret: true if successful + * false if ackv == NULL or the upper limit on size is exceeded + * Note: Adds packet in seqnum order until either successful + * or out of space. + * will NOT change ecn nonce echo as needed by the state change + */ + bool mergeWith(DCCPAckVector *ackv); + + /* Alter state of one packet. + * Will split items as needed. Note will not alter + * arg: seqnum - sequence number of packet to add + * newstate - packets new state + * oldstate - the packets old state + * ret: true if successful + * false if packet is not in vector or size + * if the upper limit on size is exceeded + * Note: will not alter the vector on failure. + * will NOT change ecn nonce echo as needed by the state change + */ + bool alterState(u_int32_t seqnum, dccp_packet_state newstate, dccp_packet_state *oldstate = NULL); + + /* Returns the state of a packet + * arg: seqnum - packets sequence number + * state - the packets state (if found) + * ret: true if packet is found + * false if not + */ + bool getState(u_int32_t seqnum, dccp_packet_state *state); + + + /* Packet walk functions. + * Note you can't start a descending packet walk and then + * use prevPacket(), and similiar, you can't use nextPacket + * if you started an ascending packet walk. + */ + + /* Start a walk through the ack vector packet by packet, + * descending on sequence number. + */ + void startPacketWalk(); + + /* Next packet in walk + * arg: seqnum - sequence number of next packet + * state - packet state + * ret: true if packet is found + * false if packet is not found or walk not initiated. + */ + bool nextPacket(u_int32_t *seqnum, dccp_packet_state *state); + + /* Start a walk through the ack vector packet by packet, + * ascending on sequence number. + */ + void startReversePacketWalk(); + + /* Previous packet in walk + * arg: seqnum - sequence number of previous packet + * state - packet state + * ret: true if packet is found + * false if packet is not found or walk not initiated. + */ + bool prevPacket(u_int32_t *seqnum, dccp_packet_state *state); + + /* Start a walk through the ack vector, item for item + * Most recent interval first. + */ + void startIntervalWalk(); + + /* Next interval in walk + * arg: interval - found interval + * seq_start - the sequence number that started the interval + * seq_end - the sequence number that ended the interval + * state - the state of the packet in the interval + * ret: true if an interval is found + * false if not or walk not initiated. + */ + bool nextInterval(u_char *interval, u_int32_t *seq_start, u_int32_t *seq_end, dccp_packet_state *state); + + /* Return the ECN Nonce Echo + * ret: ECN Nonce Echo + */ + u_int8_t getENE(); + + /* Set the ECN Nonce Echo + * arg: ene - new ene + */ + void setENE(u_int8_t ene); + + /* Chech if ack vector is empty + * ret: true if empty, otherwise false + */ + bool isEmpty(); + + /* Return the first sequence number represented in the ack vector + * ret: first sequence number in the vector + * 0 if empty + */ + u_int32_t getFirstSeqNum(); + + /* Return the last sequence number represented in the ack vector + * ret: last sequence number in the vector + * 0 if empty + */ + u_int32_t getLastSeqNum(); + + /* Return the current size of the ack vector + * ret: current size + */ + int getSize(); + + /* Return the ack vector + * arg: vect - array to store ack vector in + * size - size of vect + * ret: The size of returned ack vector (also in size) if successful + * DCCP_ACKV_EMPTY if its empty + * DCCP_ACKV_ERR_SIZE if size is too small + * (will set size to the size needed) + */ + int getAckVector(u_char* vect, u_int16_t *size); + + /* Import an ack vector (clearing the old one) + * arg: vect - ack vector to set + * size - size of vect + * seq_start - sequnce number of the first packet in ack vector + * ene - the current ecn nonce echo + * ret: true if successful + * false if out of space + */ + bool setAckVector(const u_char* vect, u_int16_t size, u_int32_t seq_start, u_int8_t ene); + + /* Return and mark the ackvector as sent in history + * arg: seqnum - sequence number of packet on which ackvector is added + * acknum - acknowledgement number on that packet (== seq_first_) + * vect - ack vector to send + * size - size of ackvector + * ret: same as getAckVector() + * Note: Fails hard if seq_first_ != acknum! + */ + int sendAckVector(u_int32_t seqnum, u_int32_t acknum, u_char* vect, u_int16_t *size); + + /* Add ack vector options and mark as sent in history + * arg: seqnum - sequence number of packet on which ackvector is added + * acknum - acknowledgement number on that packet (== seq_first_) + * options - DCCPOptions object to add ackvector to + * ret: DCCP_OPT_NO_ERR on success + * similar to getAckVector and addOptions() + */ + int sendAckVector(u_int32_t seqnum, u_int32_t acknum, DCCPOptions *options); + + /* Check if state may be cleared due to an received ack. + * If ackv is present, check in that to see if an ack has been acked + * otherwise check only on acknum + * arg: acknum - acknowledgement number recv + * ackv - ackvector received (if any) + */ + void ackRecv(u_int32_t acknum, DCCPAckVector *ackv = NULL); + + /* Print ack vector state including intervals and ack history + * Note: Uses interval walk + */ + void print(); + + /* Print all packets + * arg: asc - if true, print in ascending order, otherwise descending + * Note: uses packet walk + */ + void printPackets(bool asc = false); + + /* Print ack history */ + void printHistory(); +}; + +#endif + + + diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp.cc ns-2.34_dccp/dccp/dccp.cc --- ns-2.34_orig/dccp/dccp.cc 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp.cc 2010-03-08 15:09:55.797597400 +0100 @@ -0,0 +1,1979 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp.cc,v 1.54 2004/03/24 14:06:38 nilmat-8 Exp $ */ + +#include "ip.h" +#include "dccp.h" +#include "flags.h" +#include "random.h" + +//string representation of types +char* DCCPAgent::state_str_[DCCP_NUM_STATES] = +{"CLOSED", "LISTEN", "RESPOND", "REQUEST", "OPEN", "CLOSEREQ", "CLOSING"}; + +int DCCPAgent::hdr_size_[DCCP_NUM_PTYPES] = +{ DCCP_REQ_HDR_SIZE, DCCP_RESP_HDR_SIZE, DCCP_DATA_HDR_SIZE, + DCCP_ACK_HDR_SIZE, DCCP_DACK_HDR_SIZE, DCCP_CREQ_HDR_SIZE, + DCCP_CLOSE_HDR_SIZE, DCCP_RESET_HDR_SIZE }; + +char* DCCPAgent::ptype_str_[DCCP_NUM_PTYPES] = +{ "REQUEST", "RESPONSE", "DATA", "ACK", "DATAACK", "CLOSEREQ", "CLOSE", "RESET" }; + +char* DCCPAgent::reset_reason_str_[DCCP_NUM_RESET_REASONS]= +{ "Unspecified" , "Closed", "Invalid Packet", "Option Error", "Feature Error", + "Connection Refused", "Bad Service Name", "Too Busy", "Bad Init Cookie", + "Unknown", "Unanswered Challenge", "Fruitless Negotiation", + "Agression Penalty", "No Connection", "Aborted", "Extended Seqnos", + "Mandatory Failure" +}; + +char* DCCPAgent::feature_location_str_[DCCP_NUM_FEAT_LOCS] = +{ "LOCAL", "REMOTE" }; + +//OTcl linkage for DCCP agent +static class DCCPClass : public TclClass { +public: + DCCPClass() : TclClass("Agent/DCCP") {}; + TclObject* create(int argc, const char*const* argv){ + return (new DCCPAgent()); + } +} class_dccp; + +//methods for timer classes + +/* Constructor + * arg: agent - the owning agent (to notify about timeout) + * ret: A new DCCPRetransmitTimer + */ +DCCPRetransmitTimer::DCCPRetransmitTimer(DCCPAgent* agent) : TimerHandler(){ + agent_ = agent; +} + +/* Initialize the timer. + * arg: delay - initial delay + * maxToTDelay - total maximum delay + */ +void DCCPRetransmitTimer::init(double delay, double maxTotDelay){ + force_cancel(); + backoff_failed_ = false; + delay_ = delay; + tot_delay_ = 0; + max_tot_delay_ = maxTotDelay; +} + +/* Called when the timer has expired + * arg: e - The event that happened (i.e. the timer expired) + */ +void DCCPRetransmitTimer::expire(Event *e){ + agent_->timeout(DCCP_TIMER_RTX); +} + +/* Back off the timer (by a factor of 2) + * The last back off will reach the max_tot_delay. + */ +void DCCPRetransmitTimer::backOff(){ + if (tot_delay_ == 0){ //first scheduled + } else if (tot_delay_ + delay_ * 2 <= max_tot_delay_){ + //we have not reached max_tot_delay + delay_ *= 2; //exp back-off + } else { //if we double, we would go beyond max_tot_delay + delay_ = max_tot_delay_-tot_delay_; + backoff_failed_ = true; + } + tot_delay_ += delay_; + + if(delay_ > 0.0) + resched(delay_); +} + +/* Check if the back-off failed (that is if max_tot_delay is reached) + * ret: true if back-off failed, otherwise false. + */ +bool DCCPRetransmitTimer::backOffFailed(){ + return backoff_failed_; +} + +/* Constructor + * arg: agent - the owning agent (to notify about timeout) + * ret: A new DCCPSendTimer + */ +DCCPSendTimer::DCCPSendTimer(DCCPAgent* agent) : TimerHandler(){ + agent_ = agent; +} + +/* Called when the timer has expired + * arg: e - The event that happened (i.e. the timer expired) + */ +void DCCPSendTimer::expire(Event *e){ + agent_->timeout(DCCP_TIMER_SND); +} + +//private methods + +/* Creates a new packet and fills in some header fields + * ret: a new packet + */ +Packet* DCCPAgent::newPacket(){ + Packet* pkt = allocpkt(); + hdr_dccp* dccph = hdr_dccp::access(pkt); + hdr_dccpack* dccpah = hdr_dccpack::access(pkt); + hdr_flags* flagsh = hdr_flags::access(pkt); + + //fill in seq number and (possibly) ack number + dccph->seq_num_ = seq_num_; + dccpah->ack_num_ = seq_num_recv_; + + dccph->options_ = NULL; + dccph->ccval_ = 0; + dccph->cscov_ = cscov_; + + if (use_ecn_local_){ //mark packet as ecn capable + if (nonces_->uniform(-0.5,0.5) > 0){ + //set ect(1) + flagsh->ect() = 0; + flagsh->ce() = 1; + } else { + //set ect(0) + flagsh->ect() = 1; + flagsh->ce() = 0; + } + } else { + flagsh->ect() = 0; + flagsh->ce() = 0; + } + + return pkt; +} + +/* Check if an incoming packet is valid + * arg: pkt - incoming packet + * ret: true if the packet is valid, otherwise false + */ +bool DCCPAgent::checkPacket(Packet* pkt){ + bool new_pkt; + bool result = true; + hdr_cmn* cmnh = hdr_cmn::access(pkt); + hdr_dccp* dccph = hdr_dccp::access(pkt); + hdr_dccpack* dccpah = hdr_dccpack::access(pkt); + + if (cmnh->error()){ //e.g. "checksum failed" + debug("%f, DCCP(%s)::checkPacket() - Packet is corrupt (%d)\n", + now(), name(), dccph->seq_num_); + return false; + } + + bool ack_recv = (dccph->type_ != DCCP_DATA) && (dccph->type_ != DCCP_REQUEST); + if (ack_recv && packet_sent_ && dccpah->ack_num_ >= seq_num_){ + //invalid ack_num_ + debug("%f, DCCP(%s)::checkPacket() - Ack num not valid (%d)\n", + now(), name(), dccpah->ack_num_); + return false; + } else if (ack_recv && !packet_sent_){ + //if no packet has been sent, allow only resets with acknum 0 + if (!(dccph->type_ == DCCP_RESET && dccpah->ack_num_ == 0)){ + debug("%f, DCCP(%s)::checkPacket() - Ack num not valid (No packet sent!) (%d)\n", + now(), name(), dccpah->ack_num_); + return false; + } + } + + new_pkt = (dccph->seq_num_ > seq_num_recv_); + + if (dccph->type_ == DCCP_RESET) //reset is always valid + result = true; + else + switch (state_){ + case DCCP_STATE_CLOSED: + result = false; + break; + case DCCP_STATE_LISTEN: + result = (dccph->type_ == DCCP_REQUEST); + break; + case DCCP_STATE_RESPOND: + result = (new_pkt && dccph->type_ == DCCP_REQUEST || + dccph->type_ == DCCP_CLOSE || + new_pkt && dccph->type_ == DCCP_ACK || + new_pkt && dccph->type_ == DCCP_DATAACK); + break; + case DCCP_STATE_OPEN: + result = (dccph->type_ == DCCP_CLOSE || + dccph->type_ == DCCP_CLOSEREQ && !server_ || + dccph->type_ == DCCP_ACK || + dccph->type_ == DCCP_DATA || + dccph->type_ == DCCP_DATAACK); + break; + case DCCP_STATE_REQUEST: + result = (dccph->type_ == DCCP_RESPONSE + && dccpah->ack_num_ == seq_num_-1 || + dccph->type_ == DCCP_CLOSE); + break; + case DCCP_STATE_CLOSEREQ: + result = (dccph->type_ == DCCP_CLOSE || + dccph->type_ == DCCP_ACK || + dccph->type_ == DCCP_DATA || + dccph->type_ == DCCP_DATAACK); + break; + case DCCP_STATE_CLOSING: + result = (dccph->type_ == DCCP_CLOSEREQ || + dccph->type_ == DCCP_ACK || + dccph->type_ == DCCP_DATA || + dccph->type_ == DCCP_DATAACK); + break; + + default: + fprintf(stderr, "%f, DCCP(%s):checkPacket() - Illegal state (%d)!\n", + now(), name(),state_); + fflush(stdout); + abort(); + } + + if (result){ + //set highest seqnum and acknum recv so far + if (seq_num_recv_ < dccph->seq_num_) + seq_num_recv_ = dccph->seq_num_; + if (ack_recv && ack_num_recv_ < dccpah->ack_num_) + ack_num_recv_ = dccpah->ack_num_; + } + return result; +} + + +/* Add feature negotiation options on packets for features + * currently under negotiation. Will use getFeature() to obtain values. + */ +void DCCPAgent::addFeatureOptions(){ + int result; + u_int8_t type; + u_char* data = new u_char[DCCP_OPT_MAX_LENGTH]; + int i; + + for(i = 0; i < feat_list_used_; i++){ + if (!packet_recv_ || feat_list_first_[i] || feat_list_seq_num_[i] <= ack_num_recv_){ + //this is the first time, or last change for this + //feature should have been confirmed by now + + if (feat_list_loc_[i] == DCCP_FEAT_LOC_LOCAL) + type = DCCP_OPT_CHANGEL; + else + type = DCCP_OPT_CHANGER; + + result = getFeature(feat_list_num_[i], feat_list_loc_[i], data, DCCP_OPT_MAX_LENGTH); + if (result > 0){ + feat_list_seq_num_[i] = seq_num_; + debug("%f, DCCP(%s)::addFeatureOptions() - Adding option type %d for feat %d, location %s, seq %d\n", + now(), name(), type, feat_list_num_[i], featureLocationAsStr(feat_list_loc_[i]),feat_list_seq_num_[i]); + opt_->addFeatureOption(type,feat_list_num_[i], data, result); + feat_list_first_[i] = false; + } else + debug("%f, DCCP(%s)::addFeatureOptions() - getFeature() failed for feature %d, location %s. Error: %d \n", + now(), name(), feat_list_num_[i], featureLocationAsStr(feat_list_loc_[i]),result); + + } else + debug("%f, DCCP(%s)::addFeatureOptions() - Old change still pending for feat %d, location %s (seq_sent: %d ack_recv: %d)\n", + now(), name(), feat_list_num_[i], featureLocationAsStr(feat_list_loc_[i]),feat_list_seq_num_[i],ack_num_recv_); + } + + for(i = 0; i < feat_conf_used_; i++){ + if (feat_conf_loc_[i] == DCCP_FEAT_LOC_LOCAL) + type = DCCP_OPT_CONFIRML; + else + type = DCCP_OPT_CONFIRMR; + + result = getFeature(feat_conf_num_[i], feat_conf_loc_[i], data, DCCP_OPT_MAX_LENGTH); + if (result > 0){ + debug("%f, DCCP(%s)::addFeatureOptions() - Adding option type %d for feat %d, location %s\n", now(), name(), type, feat_conf_num_[i], featureLocationAsStr(feat_conf_loc_[i])); + opt_->addFeatureOption(type,feat_conf_num_[i], data, result); + } else + debug("%f, DCCP(%s)::addFeatureOptions() - getFeature() failed for feature %d, location %s. Error: %d \n", now(), name(), feat_conf_num_[i], featureLocationAsStr(feat_conf_loc_[i]),result); + } + feat_conf_used_ = 0; + delete [] data; +} + +/* Finish feature negotiation for a feature abd remove it from + * the list of ongoing feature negotiations. + * arg: num - feature number + * location - feature location + * ret: true if the feature is removed + * false if it doesn't exist in the list + */ +bool DCCPAgent::finishFeatureNegotiation(u_int8_t num, dccp_feature_location location){ + int walker = 0; + while (walker < feat_list_used_){ //walk through the list + if (feat_list_num_[walker] == num && feat_list_loc_[walker] == location){ + //we found the feature + //move the rest of the list one step up + if (walker + 1 < feat_list_used_){ + for(int i = walker; i < feat_list_used_-1; i++){ + feat_list_num_[i] = feat_list_num_[i+1]; + feat_list_loc_[i] = feat_list_loc_[i+1]; + } + } + feat_list_used_--; + return true; + + } + walker++; + } + return false; +} + +/* Add a feature to the list of features to confirm + * Will only add one entry for each (feat,loc) pair + * arg: num - feature number + * location - feature location + */ +void DCCPAgent::confirmFeature(u_int8_t num, dccp_feature_location location){ + int walker = 0; + while (walker < feat_conf_used_){ //walk through the list + if (feat_conf_num_[walker] == num && feat_conf_loc_[walker] == location){ + //we have found the feature + break; + } + walker++; + } + + if (walker == feat_conf_used_ && feat_conf_used_ < feat_size_){ + //add to the end of list + feat_conf_num_[walker] = num; + feat_conf_loc_[walker] = location; + feat_conf_used_++; + } +} + +/* Find a feature in the list of ongoing negotiations + * arg: num - feature number + * location - feature location + * ret: position in the feature list if successfull + * otherwise -1 + */ +int DCCPAgent::findFeatureInList(u_int8_t num, dccp_feature_location location){ + int walker = 0; + while (walker < feat_list_used_){ //walk through the list + if (feat_list_num_[walker] == num && feat_list_loc_[walker] == location){ + //we have found the feature + return walker; + } + walker++; + } + return -1; +} + +//protected methods + +/* OTcl binding of variables */ +void DCCPAgent::delay_bind_init_all(){ + delay_bind_init_one("packetSize_"); + delay_bind_init_one("initial_rtx_to_"); + delay_bind_init_one("max_rtx_to_"); + delay_bind_init_one("resp_to_"); + delay_bind_init_one("sb_size_"); + delay_bind_init_one("opt_size_"); + delay_bind_init_one("feat_size_"); + delay_bind_init_one("ackv_size_"); + delay_bind_init_one("ccid_"); + delay_bind_init_one("use_ecn_local_"); + delay_bind_init_one("use_ecn_remote_"); + delay_bind_init_one("ack_ratio_local_"); + delay_bind_init_one("ack_ratio_remote_"); + delay_bind_init_one("use_ackv_local_"); + delay_bind_init_one("use_ackv_remote_"); + delay_bind_init_one("q_scheme_"); + delay_bind_init_one("q_local_"); + delay_bind_init_one("q_remote_"); + delay_bind_init_one("snd_delay_"); + delay_bind_init_one("nam_tracevar_"); + delay_bind_init_one("trace_all_oneline_"); + delay_bind_init_one("allow_mult_neg_"); + delay_bind_init_one("ndp_limit_"); + delay_bind_init_one("ccval_limit_"); + delay_bind_init_one("num_data_pkt_"); + delay_bind_init_one("num_ack_pkt_"); + delay_bind_init_one("num_dataack_pkt_"); + delay_bind_init_one("cscov_"); + + Agent::delay_bind_init_all(); + + reset(); +} + +int DCCPAgent::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer){ + if (delay_bind(varName, localName, "packetSize_", &size_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "initial_rtx_to_", &initial_rtx_to_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "max_rtx_to_", &max_rtx_to_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "resp_to_", &resp_to_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "sb_size_", &sb_size_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "opt_size_", &opt_size_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "feat_size_", &feat_size_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "ackv_size_", &ackv_size_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "ccid_", &ccid_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "use_ecn_local_", &use_ecn_local_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "use_ecn_remote_", &use_ecn_remote_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "ack_ratio_local_", &ack_ratio_local_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "ack_ratio_remote_", &ack_ratio_remote_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "use_ackv_local_", &use_ackv_local_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "use_ackv_remote_", &use_ackv_remote_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "q_scheme_", &q_scheme_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "q_local_", &q_local_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "q_remote_", &q_remote_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "snd_delay_", &snd_delay_, tracer)) return TCL_OK; + if (delay_bind_bool(varName, localName, "trace_all_oneline_", &trace_all_oneline_ , tracer)) return TCL_OK; + if (delay_bind_bool(varName, localName, "nam_tracevar_", &nam_tracevar_ , tracer)) return TCL_OK; + if (delay_bind(varName, localName, "allow_mult_neg_", &allow_mult_neg_ , tracer)) return TCL_OK; + if (delay_bind(varName, localName, "ndp_limit_", &ndp_limit_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "ccval_limit_", &ccval_limit_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "num_data_pkt_", &num_data_pkt_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "num_ack_pkt_", &num_ack_pkt_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "num_dataack_pkt_", &num_dataack_pkt_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "cscov_", &cscov_, tracer)) return TCL_OK; + return Agent::delay_bind_dispatch(varName, localName, tracer); +} + +/* Return a string representation of a type */ +const char* DCCPAgent::stateAsStr(dccp_state state){ + if (state < DCCP_NUM_STATES && state >= 0) + return state_str_[state]; + else + return "UNKNOWN"; +} + +const char* DCCPAgent::packetTypeAsStr(dccp_packet_type type){ + if (type < DCCP_NUM_PTYPES && type >= 0) + return ptype_str_[type]; + else + return "UNKNOWN"; +} + +const char* DCCPAgent::resetReasonAsStr(dccp_reset_reason reason){ + if (reason < DCCP_NUM_RESET_REASONS && reason >= 0) + return reset_reason_str_[reason]; + else + return "Unknown"; +} + +const char* DCCPAgent::featureLocationAsStr(dccp_feature_location location){ + if (location < DCCP_NUM_FEAT_LOCS && location >= 0) + return feature_location_str_[location]; + else + return "UNKNOWN"; +} + +/* Return the header size (without options) of a packet type + * arg: type - packet type + * ret: header size + */ +int DCCPAgent::headerSize(dccp_packet_type type){ + if (type < DCCP_NUM_PTYPES && type >= 0) + return hdr_size_[type]; + else + return 0; +} + +/* Extract the ecn codepoint + * arg: pkt - packet + * ret: ecn codepoint of packet + */ +dccp_ecn_codepoint DCCPAgent::getECNCodePoint(Packet* pkt){ + hdr_flags* flagsh = hdr_flags::access(pkt); + if (flagsh->ect() == 1 && flagsh->ce() == 1) + return ECN_CE; + else if (flagsh->ect() == 0 && flagsh->ce() == 1) + return ECN_ECT1; + else if (flagsh->ect() == 1 && flagsh->ce() == 0) + return ECN_ECT0; + else + return ECN_NOT_ECT; +} + +/* Get the packet nonce + * arg: pkt - packet + * ret: 0 if ECT(0) is set + * 1 if ECT(1) is set + * otherwise -1 + */ +int DCCPAgent::getNonce(Packet* pkt){ + dccp_ecn_codepoint ecn = DCCPAgent::getECNCodePoint(pkt); + if (ecn == ECN_ECT0 || ecn == ECN_ECT1) + return ecn; + else + return -1; +} + +/* Changes state (and cancel/init/sched timers) + * arg: new_state - new state + */ +inline void DCCPAgent::changeState(dccp_state new_state){ + debug("%f, DCCP(%s)::changeState() - State changed from %s (%d) to %s (%d)\n", + now(), name(), stateAsStr(state_), state_, stateAsStr(new_state), new_state); + state_ = new_state; + switch (new_state){ + case DCCP_STATE_RESPOND: + timer_rtx_->force_cancel(); + timer_rtx_->sched(resp_to_); + break; + case DCCP_STATE_OPEN: + timer_rtx_->force_cancel(); + break; + case DCCP_STATE_REQUEST: + case DCCP_STATE_CLOSEREQ: + case DCCP_STATE_CLOSING: + cancelTimers(); + timer_rtx_->init(initial_rtx_to_,max_rtx_to_); + break; + default: + ; + } +} + +/* Reinitialize the agent */ +void DCCPAgent::reset(){ + debug("%f, DCCP(%s)::reset() - Reset called (seq_num_ %d)\n", now(), name(), seq_num_); + + cancelTimers(); + if (server_) + changeState(DCCP_STATE_LISTEN); + + if (state_ != DCCP_STATE_LISTEN && state_ != DCCP_STATE_CLOSED) + changeState(DCCP_STATE_CLOSED); + + delete opt_; + opt_ = new DCCPOptions(opt_size_); + delete sb_; + sb_ = new DCCPSendBuffer(sb_size_); + delete ackv_; + ackv_ = new DCCPAckVector(ackv_size_); + elapsed_time_recv_ = 0; + send_ackv_ = true; + manage_ackv_ = true; + seq_num_ = 0; + seq_num_recv_ = 0; + ack_num_recv_ = 0; + ackv_recv_ = NULL; + ack_num_ = 0; + ccval_ = 0; + send_ack_ = false; + conn_est_ = false; + output_ = false; + output_flag_ = false; + infinite_send_ = false; + close_on_empty_ = false; + server_ = false; + ndp_ = 0; + ack_ratio_local_ = DCCP_FEAT_DEF_ACK_RATIO; + ack_ratio_remote_ = DCCP_FEAT_DEF_ACK_RATIO; + q_local_ = DCCP_FEAT_DEF_Q; + q_remote_ = DCCP_FEAT_DEF_Q; + delete [] feat_list_num_; + delete [] feat_list_loc_; + delete [] feat_list_seq_num_; + delete [] feat_list_first_; + feat_list_num_ = new u_int8_t[feat_size_]; + feat_list_loc_ = new dccp_feature_location[feat_size_]; + feat_list_seq_num_ = new u_int32_t[feat_size_]; + feat_list_first_ = new bool[feat_size_]; + feat_list_used_ = 0; + + delete [] feat_conf_num_; + delete [] feat_conf_loc_; + feat_conf_num_ = new u_int8_t[feat_size_]; + feat_conf_loc_ = new dccp_feature_location[feat_size_]; + feat_conf_used_ = 0; + + seq_last_feat_neg_ = 0; + feat_first_in_pkt_ = true; + + packet_sent_ = false; + packet_recv_ = false; + ar_unacked_ = 0; + + rtt_conn_est_ = 0.0; + + num_data_pkt_ = 0; + num_ack_pkt_ = 0; + num_dataack_pkt_ = 0; +} + +/* Send a packet. + * arg: try_pure_ack - if true, try to send a pure ack if cc + * refuses to send a dataack + */ +void DCCPAgent::output(bool try_pure_ack){ + int data_size; + Packet* pkt; + hdr_dccp *dccph; + hdr_dccpack *dccpah; + hdr_cmn *cmnh; + bool moreToSend; + bool tell_cc; + bool tell_app; + + again: + data_size = 0; + moreToSend = false; + tell_cc = false; + tell_app = false; + pkt = newPacket(); + dccph = hdr_dccp::access(pkt); + dccpah = hdr_dccpack::access(pkt); + cmnh = hdr_cmn::access(pkt); + + switch(state_){ + case DCCP_STATE_REQUEST: + dccph->type_ = DCCP_REQUEST; + cmnh->ptype() = PT_DCCP_REQ; + packet_sent_ = true; + timer_rtx_->backOff(); + rtt_conn_est_ = now(); + goto send; + break; + case DCCP_STATE_RESPOND: + dccph->type_ = DCCP_RESPONSE; + cmnh->ptype() = PT_DCCP_RESP; + packet_sent_ = true; + rtt_conn_est_ = now(); + goto send; + break; + case DCCP_STATE_OPEN: + if(!conn_est_){ //the last ack in handshake + dccph->type_ = DCCP_ACK; + cmnh->ptype() = PT_DCCP_ACK; + send_ack_ = false; + conn_est_ = true; + goto send; + } + + if (infinite_send_ || !(sb_->empty())){ + //we have data to send + dccph->type_ = DCCP_DATA; + cmnh->ptype() = PT_DCCP_DATA; + if (!infinite_send_){ + cmnh->size() = sb_->top(); + } + data_size = cmnh->size(); + } + + if (data_size > 0 && send_ack_){ + dccph->type_ = DCCP_DATAACK; + cmnh->ptype() = PT_DCCP_DATAACK; + } else if (send_ack_) { + dccph->type_ = DCCP_ACK; + cmnh->ptype() = PT_DCCP_ACK; + } else if (data_size == 0) + goto free; + //ask cc of permission to send + if (!send_askPermToSend(data_size,pkt)) { + if (try_pure_ack && data_size > 0 && send_ack_){ + //we had a DCCP-DataAck packet, try to send a pure ack + data_size = 0; + cmnh->size() = 0; + dccph->type_ = DCCP_ACK; + cmnh->ptype() = PT_DCCP_ACK; + if (!send_askPermToSend(data_size,pkt)) { + debug("%f, DCCP(%s)::output() - CC refused sending a pure ack packet\n", + now(), name()); + goto free; + } + } else + goto free; + } + //remove from sendbuffer (if applicable) + if (data_size > 0 && !infinite_send_){ + sb_->remove(); + tell_app = sb_->empty(); + } + + tell_cc = true; + + if (send_ack_){ //we are going to send an ack + dccpah->ack_num_ = ack_num_; + //add ackvector if applicable + if (use_ackv_local_ && send_ackv_) { + ackv_->sendAckVector(dccph->seq_num_, dccpah->ack_num_,opt_); + } + } + dccph->ccval_ = ccval_; + send_ack_ = false; + goto send; + break; + case DCCP_STATE_CLOSEREQ: + dccph->type_ = DCCP_CLOSEREQ; + cmnh->ptype() = PT_DCCP_CLOSEREQ; + timer_rtx_->backOff(); + break; + case DCCP_STATE_CLOSING: + dccph->type_ = DCCP_CLOSE; + cmnh->ptype() = PT_DCCP_CLOSE; + timer_rtx_->backOff(); + break; + default: + ; + } + + send: + assert(opt_ != NULL); + addFeatureOptions(); + + //calculate packet size and data offset + cmnh->size() = data_size + headerSize(dccph->type_)+opt_->getSize(); + dccph->data_offset_ = (headerSize(dccph->type_)+opt_->getSize()) / 4; + + //attach options + dccph->options_ = opt_; + opt_ = new DCCPOptions(opt_size_); + + //set ndp + if (data_size == 0) + ndp_ = (ndp_ + 1) % ndp_limit_; + dccph->ndp_ = ndp_; + + debug("%f, DCCP(%s)::output() - type %s (%d), seq: %d, ack: %d, size: %d, data_size %d, data_offset_ %d, ndp: %d, ecn: %d\n", + now(), name(), packetTypeAsStr(dccph->type_), dccph->type_, dccph->seq_num_, dccpah->ack_num_, cmnh->size(), data_size, dccph->data_offset_, + dccph->ndp_,getECNCodePoint(pkt)); + + moreToSend = (state_ == DCCP_STATE_OPEN && (infinite_send_ || !sb_->empty())); + seq_num_++; + if(tell_cc) //inform cc + send_packetSent(pkt, moreToSend, data_size); + + switch(cmnh->ptype()){ + case PT_DCCP_DATA: + num_data_pkt_++; + break; + case PT_DCCP_ACK: + num_ack_pkt_++; + break; + case PT_DCCP_DATAACK: + num_dataack_pkt_++; + break; + default: + ; + } + + //send packet + send(pkt,0); + + if (tell_app){ //inform application + assert(!moreToSend); + idle(); + + if (close_on_empty_) + close(); + return; + } + + ccval_ = 0; + + if (moreToSend) //try to send more data if available + goto again; + return; + free: + Packet::free(pkt); +} + +/* Send a reset packet. + * arg: reason - reason for reset + * data - data to include on reset packet + */ +void DCCPAgent::sendReset(dccp_reset_reason reason, u_int8_t data1, + u_int8_t data2, u_int8_t data3){ + Packet *pkt = newPacket(); + hdr_dccp *dccph = hdr_dccp::access(pkt); + hdr_dccpack *dccpah = hdr_dccpack::access(pkt); + hdr_dccpreset *dccpresh = hdr_dccpreset::access(pkt); + hdr_cmn *cmnh = hdr_cmn::access(pkt); + + //fill in header info + dccph->type_ = DCCP_RESET; + cmnh->ptype() = PT_DCCP_RESET; + dccpresh->rst_reason_ = reason; + dccpresh->rst_data1_ = data1; + dccpresh->rst_data2_ = data2; + dccpresh->rst_data3_ = data3; + cmnh->size() = headerSize(dccph->type_); + dccph->data_offset_ = cmnh->size() / 4; + assert(cmnh->size() % 4 != 0); + dccph->options_ = new DCCPOptions(opt_size_); + if (state_ != DCCP_STATE_CLOSED && state_ != DCCP_STATE_LISTEN){ + //we have valid sequence number + ndp_ = (ndp_ + 1) % ndp_limit_; + dccph->ndp_ = ndp_; + seq_num_++; + } else { + dccph->ndp_ = 1; + dccph->seq_num_ = 0; + } + dccpah->ack_num_ = seq_num_recv_; + + debug("%f, DCCP(%s)::sendReset() - Sent a RESET packet (Reason %s (%d), data (%d,%d,%d), seq: %d, ack: %d, size: %d, data_offset_ %d, ndp: %d)\n", + now(), name(), resetReasonAsStr(dccpresh->rst_reason_), dccpresh->rst_reason_, dccpresh->rst_data1_, dccpresh->rst_data2_, dccpresh->rst_data3_, dccph->seq_num_, dccpah->ack_num_, cmnh->size(), dccph->data_offset_,dccph->ndp_); + send(pkt,0); +} + +/* Parse options in a packet. + * Will call parseOption() on every option found. + * Fails if parseOption() fails. + * arg: pkt - packet + * ret: true if parse is successful + * false if the parse failed and the connection should be reset + */ +bool DCCPAgent::parseOptions(Packet *pkt){ + int result = 0; + u_int8_t *type = new u_int8_t; + u_char *data = new u_char[DCCP_OPT_MAX_LENGTH]; + hdr_dccp *dccph = hdr_dccp::access(pkt); + DCCPOptions* options = dccph->options_; + + if(options == NULL){ + delete type; + delete [] data; + return false; + } + + feat_first_in_pkt_ = true; + + options->startOptionWalk(); + result = options->nextOption(type, data, DCCP_OPT_MAX_LENGTH); + + while (result >= 0){ //walk through and process opt until done or fail + if(!processOption(*type, data, result, pkt)){ + delete type; + delete [] data; + return false; + } + + result = options->nextOption(type, data, DCCP_OPT_MAX_LENGTH); + } + + delete type; + delete [] data; + return true; +} + +/* Process an incoming option. + * arg: type - option type + * data - option data + * size - size of opption data + * pkt - the packet the option was received on + * ret: true if option processing was successful + * false if it fails + */ +bool DCCPAgent::processOption(u_int8_t type, u_char* data, u_int8_t size, Packet *pkt){ + debug("%f, DCCP(%s)::processOption() - Type %d, data %d, %d, %d ... size %d\n", + now(), name(), type, (size > 0 ? data[0] : 0),(size > 1 ? data[1] : 0), + (size > 2 ? data[2] : 0), size); + int result = 0; + dccp_feature_location loc; + hdr_dccp *dccph = hdr_dccp::access(pkt); + hdr_dccpack *dccpah = hdr_dccpack::access(pkt); + + //allow only changeR(ack_ratio) when connection is established + if (state_ == DCCP_STATE_OPEN && (type == DCCP_OPT_CHANGER || type == DCCP_OPT_CONFIRML)){ + if (size > 0 && getFeatureType((u_int8_t) data[0]) == DCCP_FEAT_TYPE_SP){ + fprintf(stdout,"%f, DCCP(%s)::processOption() - Feature neg of feat %d is not allowed after connection establishment\n", + now(), name(), data[0]); + return true; + } + } else if (state_ == DCCP_STATE_OPEN && (type == DCCP_OPT_CHANGEL || type == DCCP_OPT_CONFIRMR) && size > 0 && getFeatureType((u_int8_t) data[0]) == DCCP_FEAT_TYPE_NN) { + fprintf(stdout,"%f, DCCP(%s)::processOption() - ChangeL or ConfirmR are not allowed for non-negotiable features (feat %u)\n", + now(), name(), data[0]); + return true; + } + + u_int16_t ui16 = 0; + u_int32_t ui32 = 0; + + //check that feat neg options are processed in seq num order + if (type == DCCP_OPT_CHANGEL || type == DCCP_OPT_CHANGER + || type == DCCP_OPT_CONFIRML || type == DCCP_OPT_CONFIRMR){ + if (packet_recv_ && feat_first_in_pkt_ && dccph->seq_num_ <= seq_last_feat_neg_){ + debug("%f, DCCP(%s)::processOption() - Type %d: feature neg option is out of order (last seq %u, seq now %u)\n", now(), name(), type, seq_last_feat_neg_,dccph->seq_num_); + return true; + } else { + seq_last_feat_neg_ = dccph->seq_num_; + feat_first_in_pkt_ = false; + } + } + + switch(type){ + case DCCP_OPT_PADDING: + break; + case DCCP_OPT_QUIESCENCE: + break; + case DCCP_OPT_CHANGEL: + if (size > 1){ + result = setFeature(data[0], DCCP_FEAT_LOC_REMOTE, &(data[1]), size-1); + if (result != DCCP_FEAT_OK) + goto reset; + confirmFeature(data[0], DCCP_FEAT_LOC_REMOTE); + + if (allow_mult_neg_ > 0 && feat_conf_used_ > 0 && state_ == DCCP_STATE_OPEN){ + //add an ack number to the packet with confirm options + send_ack_ = true; + ack_num_ = seq_num_recv_; + } + + } else { + result = DCCP_FEAT_ERR_SIZE; + goto reset; + } + break; + case DCCP_OPT_CHANGER: + if (size > 1){ + result = setFeature(data[0], DCCP_FEAT_LOC_LOCAL, &(data[1]), size-1); + if (result != DCCP_FEAT_OK) + goto reset; + confirmFeature(data[0], DCCP_FEAT_LOC_LOCAL); + + if (allow_mult_neg_ > 0 && feat_conf_used_ > 0 && state_ == DCCP_STATE_OPEN){ + //add an ack number to the packet with confirm options + send_ack_ = true; + ack_num_ = seq_num_recv_; + } + + } else { + result = DCCP_FEAT_ERR_SIZE; + goto reset; + } + break; + case DCCP_OPT_CONFIRML: + case DCCP_OPT_CONFIRMR: + loc = DCCP_FEAT_LOC_REMOTE; + if (type == DCCP_OPT_CONFIRMR) + loc = DCCP_FEAT_LOC_LOCAL; + + if (size > 1){ + if (featureIsChanging(data[0], loc)){ + if (allow_mult_neg_ > 0){ + if (dccph->type_ == DCCP_ACK || + dccph->type_ == DCCP_DATAACK || + dccph->type_ == DCCP_RESPONSE){ + + if (dccpah->ack_num_ < feat_list_seq_num_[findFeatureInList(data[0], loc)]){ + debug("%f, DCCP(%s)::processOption() - Confirm acknowledged an old Change. Ignoring confirm...\n", now(), name()); + return true; + } + } else { + debug("%f, DCCP(%s)::processOption() - Missing ack num together with confirm option.\n", now(), name()); + } + } + if (setFeature(data[0], loc, &(data[1]), size-1,true) != DCCP_FEAT_ERR_TEST){ + result = setFeature(data[0], loc, &(data[1]), size-1); + if (result != DCCP_FEAT_OK) + goto reset; + finishFeatureNegotiation(data[0], loc); + } else + debug("%f, DCCP(%s)::processOption() - Test of setFeature() failed for feature type %u. Ignoring confirm...\n", now(), name(), data[0]); + + } else { + //ignore + } + } else { + result = DCCP_FEAT_ERR_SIZE; + goto reset; + } + break; + case DCCP_OPT_ACK_VECTOR_N0: + case DCCP_OPT_ACK_VECTOR_N1: + debug("%f, DCCP(%s)::processOption() - Received ackvector (ene %d)\n", + now(), name(), 1-(DCCP_OPT_ACK_VECTOR_N1-type)); + if (dccph->type_ == DCCP_DATA) { + fprintf(stdout,"%f, DCCP(%s)::processOption() - Ackvector received on DCCP-Data packet!\n",now(), name()); + break; + } + + if (ackv_recv_ != NULL){ + fprintf(stderr,"%f, DCCP(%s)::processOption() - ackv_recv_ not null\n",now(), name()); + fflush(stdout); + abort(); + } + //store received ack vector + ackv_recv_ = new DCCPAckVector(ackv_size_); + if (!ackv_recv_->setAckVector(data,size,dccpah->ack_num_,1-(DCCP_OPT_ACK_VECTOR_N1-type))){ + fprintf(stdout,"%f, DCCP(%s)::processOption() - Failed to set ack vector\n",now(), name()); + delete ackv_recv_; + ackv_recv_ = NULL; + } + break; + case DCCP_OPT_ELAPSED_TIME: + if (size == 2){ + ((u_char*) &ui16)[0] = data[0]; + ((u_char*) &ui16)[1] = data[1]; + elapsed_time_recv_ = ui16; + } else if (size == 4) { + ((u_char*) &ui32)[0] = data[0]; + ((u_char*) &ui32)[1] = data[1]; + ((u_char*) &ui32)[2] = data[2]; + ((u_char*) &ui32)[3] = data[3]; + elapsed_time_recv_ = ui32; + } else { + fprintf(stdout,"%f, DCCP(%s)::processOption() - Elapsed time with wrong size %d received\n",now(), name(), size); + } + break; + default: + if (type_ < DCCP_OPT_CC_START) + debug("%f, DCCP(%s)::processOption() - Unknown option (type %d)\n", + now(), name(), type); + } + + return true; + reset: + switch (result){ + case DCCP_FEAT_NOT_PREFERED: + sendReset(DCCP_RST_FLESS_NEG,data[0],data[1],0); + break; + case DCCP_FEAT_UNKNOWN: + fprintf(stdout,"%f, DCCP(%s)::processOption() - Unknown feature (type %d)\n", + now(), name(), data[0]); + break; + case DCCP_FEAT_ERR_SIZE: + sendReset(DCCP_RST_FEATURE_ERR, + (size > 0 ? data[0] : 0), + (size > 1 ? data[1] : 0), + (size > 2 ? data[2] : 0)); + break; + } + return false; +} + +/* Change a feature (i.e. initiate a feature negotiation). + * arg: num - feature number + * location - feature location + * ret: true if the feature is added to the list of ongoing feat neg + * false if the list is full or the feature is already present + * and multiple negotiations are not allowed. + */ +bool DCCPAgent::changeFeature(u_int8_t num, dccp_feature_location location){ + if (allow_mult_neg_ > 0){ + int pos = findFeatureInList(num,location); + + if (pos >= 0){ + feat_list_seq_num_[pos] = seq_num_; + feat_list_first_[pos] = true; + return true; + } + //does not exist, check if full + if (feat_list_used_ == feat_size_) + return false; + } else { + //check if the list is full, or if this feature is already in neg + if (feat_list_used_ == feat_size_ || featureIsChanging(num, location)) + return false; + } + + feat_list_num_[feat_list_used_] = num; + feat_list_loc_[feat_list_used_] = location; + feat_list_seq_num_[feat_list_used_] = 0; + feat_list_first_[feat_list_used_] = true; + debug("%f, DCCP(%s)::changeFeature() - Added feature %d, location %s\n", + now(), name(), num, featureLocationAsStr(location)); + feat_list_used_++; + return true; +} + +/* Check if a feature is currently under negotiation. + * arg: num - feature number + * location - feature location + * ret: true if the feature is under negotiation + * false otherwise + */ +bool DCCPAgent::featureIsChanging(u_int8_t num, dccp_feature_location location){ + for (int i = 0; i< feat_list_used_; i++) + if(feat_list_num_[i] == num && feat_list_loc_[i] == location) + return true; + return false; +} + +/* Build the list of features to neg on DCCP-Request packet */ +void DCCPAgent::buildInitialFeatureList(){ + debug("%f, DCCP(%s)::buildInitialFeatureList() - values cc %d, ecnl %d, ecnr %d, ackrl %d, ackrr %d, ackvl %d, ackvr %d\n", + now(), name(), ccid_, use_ecn_local_,use_ecn_remote_, ack_ratio_local_,ack_ratio_remote_,use_ackv_local_,use_ackv_remote_); + changeFeature(DCCP_FEAT_CC, DCCP_FEAT_LOC_LOCAL); + changeFeature(DCCP_FEAT_CC, DCCP_FEAT_LOC_REMOTE); + changeFeature(DCCP_FEAT_ECN, DCCP_FEAT_LOC_LOCAL); + changeFeature(DCCP_FEAT_ECN, DCCP_FEAT_LOC_REMOTE); + changeFeature(DCCP_FEAT_ACK_RATIO, DCCP_FEAT_LOC_LOCAL); + changeFeature(DCCP_FEAT_ACK_RATIO, DCCP_FEAT_LOC_REMOTE); + changeFeature(DCCP_FEAT_ACKV, DCCP_FEAT_LOC_LOCAL); + changeFeature(DCCP_FEAT_ACKV, DCCP_FEAT_LOC_REMOTE); + changeFeature(DCCP_FEAT_Q_SCHEME, DCCP_FEAT_LOC_LOCAL); + changeFeature(DCCP_FEAT_Q_SCHEME, DCCP_FEAT_LOC_REMOTE); + changeFeature(DCCP_FEAT_Q, DCCP_FEAT_LOC_LOCAL); + changeFeature(DCCP_FEAT_Q, DCCP_FEAT_LOC_REMOTE); +} + +/* Set a value for a specific feature. + * arg: num - feature number + * location - feature location + * data - feature data + * size - size of data + * testSet - if true, only check value for correctness, don't set + * ret: DCCP_FEAT_OK if successful + * DCCP_FEAT_UNKNOWN if the feature number is unknown + * DCCP_FEAT_NOT_PREFERED if the value is not prefered + * DCCP_FEAT_ERR_SIZE if the size does not match the feature + * DCCP_FEAT_ERR_TEST if a non-neg feature failed the test. + * a negotiable feature that fails returns NOT_PREFERED + * Note: DCCP_FEAT_NOT_PREFERED is not applicable for non neg features. + * NOT_PREFERED and ERR_SIZE will trigger a reset + */ +int DCCPAgent::setFeature(u_int8_t num, dccp_feature_location location, + u_char* data, u_int8_t size, bool testSet){ + u_int16_t ui16; + + switch(num){ + case DCCP_FEAT_CC: + if (size == 1){ + if (ccid_ == data[0]) + return DCCP_FEAT_OK; + else + return DCCP_FEAT_NOT_PREFERED; + } else + return DCCP_FEAT_ERR_SIZE; + break; + case DCCP_FEAT_ECN: + if (size == 1){ + if (location == DCCP_FEAT_LOC_LOCAL){ + if (use_ecn_local_ && data[0] || + !(use_ecn_local_ || data[0])) + return DCCP_FEAT_OK; + else + return DCCP_FEAT_NOT_PREFERED; + + } else { + if (use_ecn_remote_ && data[0] || + !(use_ecn_remote_ || data[0])) + return DCCP_FEAT_OK; + else + return DCCP_FEAT_NOT_PREFERED; + } + } else + return DCCP_FEAT_ERR_SIZE; + break; + case DCCP_FEAT_ACK_RATIO: + if (size == 2){ + ((u_char*) &ui16)[0] = data[0]; + ((u_char*) &ui16)[1] = data[1]; + if (testSet){ + if (location == DCCP_FEAT_LOC_LOCAL){ + return ((ack_ratio_local_ != (u_int16_t) ui16) ? DCCP_FEAT_ERR_TEST : DCCP_FEAT_OK); + } else { + return ((ack_ratio_remote_ != (u_int16_t) ui16) ? DCCP_FEAT_ERR_TEST : DCCP_FEAT_OK); + } + } else { + if (location == DCCP_FEAT_LOC_LOCAL){ + ack_ratio_local_ = (u_int16_t) ui16; + + } else + ack_ratio_remote_ = (u_int16_t) ui16; + } + return DCCP_FEAT_OK; + } else + return DCCP_FEAT_ERR_SIZE; + break; + case DCCP_FEAT_ACKV: + if (size == 1){ + if (location == DCCP_FEAT_LOC_LOCAL){ + if (use_ackv_local_ && data[0] || + !(use_ackv_local_ || data[0])) + return DCCP_FEAT_OK; + else + return DCCP_FEAT_NOT_PREFERED; + + } else { + if (use_ackv_remote_ == data[0]|| + !(use_ackv_remote_ || data[0])) + return DCCP_FEAT_OK; + else + return DCCP_FEAT_NOT_PREFERED; + } + } else + return DCCP_FEAT_ERR_SIZE; + break; + case DCCP_FEAT_Q_SCHEME: + if (size == 1){ + if (q_scheme_ == data[0]) + return DCCP_FEAT_OK; + else + return DCCP_FEAT_NOT_PREFERED; + } else + return DCCP_FEAT_ERR_SIZE; + break; + case DCCP_FEAT_Q: + if (size == 1){ + if (location == DCCP_FEAT_LOC_LOCAL){ + q_local_ = (data[0] > 0 ? 1 : 0); + } else { + q_remote_ = (data[0] > 0 ? 1 : 0); + } + return DCCP_FEAT_OK; + } else + return DCCP_FEAT_ERR_SIZE; + break; + default: + return DCCP_FEAT_UNKNOWN; + } +} + +/* Obtain a value of a feature. + * Used in addFeatureOptions() to get the feature value. + * arg: num - feature number + * location - feature location + * data - feature data + * size - maximum size of data + * ret: DCCP_FEAT_OK if successful + * DCCP_FEAT_UNKNOWN if the feature number is unknown + * DCCP_FEAT_ERR_SIZE if the size is too small + */ +int DCCPAgent::getFeature(u_int8_t num, dccp_feature_location location, + u_char* data, u_int8_t maxSize){ + u_int16_t ui16; + + switch(num){ + case DCCP_FEAT_CC: + if (maxSize > 0){ + data[0] = (u_int8_t) ccid_; + return 1; + } else + return DCCP_FEAT_ERR_SIZE; + break; + case DCCP_FEAT_ECN: + if (maxSize > 0){ + if (location == DCCP_FEAT_LOC_LOCAL) + data[0] = (u_int8_t) use_ecn_local_; + else + data[0] = (u_int8_t) use_ecn_remote_; + + return 1; + } else + return DCCP_FEAT_ERR_SIZE; + break; + case DCCP_FEAT_ACK_RATIO: + if (maxSize > 1){ + if (location == DCCP_FEAT_LOC_LOCAL) + ui16 = (u_int16_t) ack_ratio_local_; + else + ui16 = (u_int16_t) ack_ratio_remote_; + data[0] = ((u_char*) &ui16)[0]; + data[1] = ((u_char*) &ui16)[1]; + + return 2; + } else + return DCCP_FEAT_ERR_SIZE; + break; + case DCCP_FEAT_ACKV: + if (maxSize > 0){ + if (location == DCCP_FEAT_LOC_LOCAL) + data[0] = (u_int8_t) use_ackv_local_; + else + data[0] = (u_int8_t) use_ackv_remote_; + + return 1; + } else + return DCCP_FEAT_ERR_SIZE; + break; + case DCCP_FEAT_Q_SCHEME: + if (maxSize > 0){ + data[0] = (u_int8_t) q_scheme_; + return 1; + } else + return DCCP_FEAT_ERR_SIZE; + break; + case DCCP_FEAT_Q: + if (maxSize > 0){ + if (location == DCCP_FEAT_LOC_LOCAL) + data[0] = (u_int8_t) q_local_; + else + data[0] = (u_int8_t) q_remote_; + + return 1; + } else + return DCCP_FEAT_ERR_SIZE; + break; + default: + return DCCP_FEAT_UNKNOWN; + } +} + +/* Obtain the feature type + * arg: num - feature number + * ret: the feature type, or DCCP_FEAT_TYPE_UKNOWN if unknown + */ +dccp_feature_type DCCPAgent::getFeatureType(u_int8_t num){ + switch(num){ + case DCCP_FEAT_CC: + case DCCP_FEAT_ECN: + case DCCP_FEAT_ACKV: + case DCCP_FEAT_Q_SCHEME: + return DCCP_FEAT_TYPE_SP; + break; + case DCCP_FEAT_ACK_RATIO: + case DCCP_FEAT_Q: + return DCCP_FEAT_TYPE_NN; + default: + return DCCP_FEAT_TYPE_UNKNOWN; + } +} + +/* Cancel all timers */ +void DCCPAgent::cancelTimers(){ + timer_snd_->force_cancel(); + timer_rtx_->force_cancel(); +} + + +/* Ask sender permission to send a packet + * arg: dataSize - size of data in packet (0 = pure ACK) + * pkt - the packet to send + * ret: true if permission is granted, otherwise false + * Note: Packet should not be used for other than manipulating + * ecn marks! + */ +bool DCCPAgent::send_askPermToSend(int dataSize, Packet *pkt){ + return (dataSize == 0 || timer_snd_->status() != TIMER_PENDING); +} + +/* A(n) ACK/DATA/DATAACK packet has been sent (sender) + * arg: pkt - packet sent + * moreToSend - true if there exist more data to send + * dataSize - size of data sent + */ +void DCCPAgent::send_packetSent(Packet *pkt, bool moreToSend, int dataSize) { + if (snd_delay_ > 0) + timer_snd_->resched(snd_delay_); + else { + fprintf(stderr,"%f, DCCP(%s)::send_packetSend() - snd_delay_ is 0!\n", now(), name()); + fflush(stdout); + abort(); + } +}; + +/* A(n) ACK/DATA/DATAACK packet has been received (sender) + * arg: pkt - packet received + * dataSize - size of data in packet + * If this function would like to send a packet, set output_ = true + * and output_flag_ if appropriate. + */ +void DCCPAgent::send_packetRecv(Packet *pkt, int dataSize) { +}; + +/* A ACK/DATA/DATAACK packet has been received (receiver) + * arg: pkt - packet received + * dataSize - size of data in packet + * If this function would like to send a packet, set output_ = true + * and output_flag_ if appropriate. + */ +void DCCPAgent::recv_packetRecv(Packet *pkt, int dataSize) { + hdr_dccp *dccph = hdr_dccp::access(pkt); + + if (dccph->type_ != DCCP_DATA && dccph->type_ != DCCP_DATAACK + && dccph->type_ != DCCP_ACK){ + fprintf(stderr,"%f, DCCP(%s)::recv_packetRecv() - Got a packet of type %s!\n", now(), name(), packetTypeAsStr(dccph->type_)); + fflush(stdout); + abort(); + } + + //ack ackording to ack ratio + if(dccph->type_ == DCCP_DATA || dccph->type_ == DCCP_DATAACK){ + ar_unacked_++; + if (ar_unacked_ >= ack_ratio_local_){ + send_ack_ = true; + ack_num_ = seq_num_recv_; + ar_unacked_= 0; + output_ = true; + output_flag_ = true; + } + } +}; + +/* Tracing functions */ +void DCCPAgent::traceAll() { +} + +void DCCPAgent::traceVar(TracedVar* v) { +} + + +//public methods + +/* Constructor + * ret: A new DCCPAgent + */ +DCCPAgent::DCCPAgent() : Agent(PT_DCCP){ + sb_size_ = DCCP_SB_SIZE; + opt_size_ = DCCP_OPT_SIZE; + feat_size_ = DCCP_FEAT_SIZE; + ackv_size_ = DCCP_ACKV_SIZE; + + ndp_limit_ = DCCP_NDP_LIMIT; + ccval_limit_ = DCCP_CCVAL_LIMIT; + + server_ = false; + seq_num_ = 0; + seq_num_recv_ = 0; + ack_num_recv_ = 0; + ackv_recv_ = NULL; + state_ = DCCP_STATE_CLOSED; + ack_num_ = 0; + ccval_ = 0; + cscov_ = DCCP_CSCOV_ALL; + send_ack_ = 0; + conn_est_ = false; + output_ = false; + output_flag_ = false; + infinite_send_ = false; + close_on_empty_ = false; + ndp_ = 0; + ccid_ = DCCP_CCID; + use_ecn_local_ = DCCP_FEAT_DEF_ECN; + use_ecn_remote_ = DCCP_FEAT_DEF_ECN; + ack_ratio_local_ = DCCP_FEAT_DEF_ACK_RATIO; + ack_ratio_remote_ = DCCP_FEAT_DEF_ACK_RATIO; + use_ackv_local_ = DCCP_FEAT_DEF_ACKV; + use_ackv_remote_ = DCCP_FEAT_DEF_ACKV; + q_scheme_ = DCCP_FEAT_DEF_Q_SCHEME; + q_local_ = DCCP_FEAT_DEF_Q; + q_remote_ = DCCP_FEAT_DEF_Q; + + feat_list_num_ = new u_int8_t[feat_size_]; + feat_list_loc_ = new dccp_feature_location[feat_size_]; + feat_list_seq_num_ = new u_int32_t[feat_size_]; + feat_list_first_ = new bool[feat_size_]; + feat_list_used_ = 0; + + feat_conf_num_ = new u_int8_t[feat_size_]; + feat_conf_loc_ = new dccp_feature_location[feat_size_]; + feat_conf_used_ = 0; + + seq_last_feat_neg_ = 0; + feat_first_in_pkt_ = true; + + allow_mult_neg_ = 0; + + packet_sent_ = false; + packet_recv_ = false; + + sb_ = new DCCPSendBuffer(sb_size_); + opt_ = new DCCPOptions(opt_size_); + ackv_ = new DCCPAckVector(ackv_size_); + + send_ackv_ = true; + manage_ackv_ = true; + + elapsed_time_recv_ = 0; + nonces_ = new RNG(); + nonces_->set_seed((long int) 0); + + timer_rtx_ = new DCCPRetransmitTimer(this); + timer_snd_ = new DCCPSendTimer(this); + + snd_delay_ = DCCP_SND_DELAY; + + nam_tracevar_ = false; + trace_all_oneline_ = false; + + ar_unacked_ = 0; + + rtt_conn_est_ = 0.0; + + initial_rtx_to_ = DCCP_INITIAL_RTX_TO; + max_rtx_to_ = DCCP_MAX_RTX_TO; + resp_to_ = DCCP_RESP_TO; + + num_data_pkt_ = 0; + num_ack_pkt_ = 0; + num_dataack_pkt_ = 0; +} + +/* Destructor */ +DCCPAgent::~DCCPAgent(){ + delete timer_rtx_; + delete timer_snd_; + delete sb_; + delete opt_; + delete nonces_; + delete [] feat_list_num_; + delete [] feat_list_loc_; + delete [] feat_list_seq_num_; + delete [] feat_list_first_; + delete [] feat_conf_num_; + delete [] feat_conf_loc_; +} + +/* Process a "function call" from OTCl + * arg: argc - number of arguments + * argv - arguments + * ret: TCL_OK if successful, TCL_ERROR otherwise + */ +int DCCPAgent::command(int argc, const char*const* argv){ + if (argc == 3) { + if (strcmp(argv[1], "advance") == 0) { + advanceby(atoi(argv[2])); + return (TCL_OK); + } + if (strcmp(argv[1], "advanceby") == 0) { + advanceby(atoi(argv[2])); + return (TCL_OK); + } + } + return (Agent::command(argc, argv)); +} + +/* Receive a packet + * arg: pkt - Packet received + * handler - handler + */ +void DCCPAgent::recv(Packet* pkt, Handler* handler){ + int data_size = 0; + bool ack_received = false; + bool data_or_ack_pkt = false; + bool processOptions = true; + bool tell_cc = false; + bool tell_app = false; + hdr_dccp *dccph = hdr_dccp::access(pkt); + hdr_dccpack *dccpah = hdr_dccpack::access(pkt); + hdr_dccpreset *dccpresh = hdr_dccpreset::access(pkt); + hdr_cmn *cmnh = hdr_cmn::access(pkt); + output_ = false; + output_flag_ = false; + + //check if packet is valid + if (!checkPacket(pkt)) + goto free; + + //preprocess packet (if reset, reset the agent) + switch (dccph->type_){ + case DCCP_REQUEST: + case DCCP_RESPONSE: + break; + case DCCP_DATA: + case DCCP_DATAACK: + data_or_ack_pkt = true; + data_size = cmnh->size() - headerSize(dccph->type_) + - dccph->options_->getSize(); + ack_received = (dccph->type_ == DCCP_DATAACK); + break; + case DCCP_ACK: + data_or_ack_pkt = true; + ack_received = true; + break; + case DCCP_CLOSEREQ: + case DCCP_CLOSE: + processOptions = false; + break; + case DCCP_RESET: + debug("%f, DCCP(%s)::recv() - Received a reset packet, reason %s (%d), data (%d,%d,%d))\n", + now(), name(), resetReasonAsStr(dccpresh->rst_reason_), dccpresh->rst_reason_, + dccpresh->rst_data1_,dccpresh->rst_data2_,dccpresh->rst_data3_); + ack_received = true; + reset(); + goto free; + break; + default: + fprintf(stdout, "%f, DCCP(%s)::recv() - Received an unknown packet (type %d) in state %s (%d)!\n", + now(), name(), dccph->type_, stateAsStr(state_), state_); + goto free; + } + debug("%f, DCCP(%s)::recv() - type %s, seq %d, ack %d, packet size %d, data_size is %d, cscov_ %d\n",now(), name(), packetTypeAsStr(dccph->type_),dccph->seq_num_, dccpah->ack_num_,cmnh->size(),data_size, dccph->cscov_); + + //process options (not on close packets) + if (processOptions && !parseOptions(pkt)){ + debug("%f, DCCP(%s)::recv() - Parse option failed!)\n", now(), name()); + reset(); + goto free; + } + + switch (state_){ + case DCCP_STATE_CLOSED: + fprintf(stdout, "%f, DCCP(%s)::recv() - Received a %s (%d) packet in state %s!\n", + now(), name(), packetTypeAsStr(dccph->type_), + dccph->type_, stateAsStr(state_)); + if (dccph->type_ != DCCP_RESET){ + //ignore + } + goto free; + break; + case DCCP_STATE_LISTEN: + if(dccph->type_ == DCCP_REQUEST){ + packet_recv_ = true; + changeState(DCCP_STATE_RESPOND); + //ack_num_ = seq_num_recv_; + output_ = true; + goto free_send; + } else if (dccph->type_ != DCCP_RESET){ + //ignore + } + goto free; + break; + case DCCP_STATE_RESPOND: + if(dccph->type_ == DCCP_REQUEST){ + //resend the RESPONSE + output_ = true; + goto free_send; + } else if (dccph->type_ == DCCP_CLOSE) { + sendReset(DCCP_RST_CLOSED,0,0,0); + reset(); + } else if (ack_received && dccpah->ack_num_ == seq_num_-1) { + rtt_conn_est_ = now() - rtt_conn_est_; + debug("%f, DCCP(%s)::recv() - RTT estimated to %f s (server)!\n",now(), name(), rtt_conn_est_); + if (data_size > 0){ + //the last ack in handshake contained data + tell_cc = true; + dccph->type_ = DCCP_DATA; + if (use_ackv_local_ && manage_ackv_) + ackv_->addPacket(dccph->seq_num_, getECNCodePoint(pkt), true); + } else if (use_ackv_local_ && manage_ackv_) + ackv_->addPacket(dccph->seq_num_+1, DCCP_PACKET_NOT_RECV); + tell_app = (!infinite_send_) && sb_->empty(); + conn_est_ = true; + changeState(DCCP_STATE_OPEN); + goto data; + } else if (ack_received) + debug("%f, DCCP(%s)::recv() - Invalid ack num %d to finalise the handshake\n",now(),name(), dccpah->ack_num_); + goto free; + break; + case DCCP_STATE_OPEN: + if (dccph->type_ == DCCP_CLOSE){ + sendReset(DCCP_RST_CLOSED, 0 ,0 ,0); + reset(); + } else if (dccph->type_ == DCCP_CLOSEREQ){ + changeState(DCCP_STATE_CLOSING); + output_ = true; + goto free_send; + } else if (data_or_ack_pkt) { + if (use_ackv_local_ && manage_ackv_){ + //add packet to local ackvector + if(!(ackv_->addPacket(dccph->seq_num_, getECNCodePoint(pkt), dccph->type_ != DCCP_ACK))) + fprintf(stdout, "%f, DCCP(%s)::recv() - Add packet to ack vector failed!\n",now(), name()); + + if (ack_received){ + ackv_->ackRecv(dccpah->ack_num_, ackv_recv_); + } + } + tell_cc = true; + goto data; + } + goto free; + break; + case DCCP_STATE_REQUEST: + if (dccph->type_ == DCCP_RESPONSE){ + packet_recv_ = true; + changeState(DCCP_STATE_OPEN); + send_ack_ = true; + ack_num_ = seq_num_recv_; + output_ = true; + rtt_conn_est_ = now() - rtt_conn_est_; + debug("%f, DCCP(%s)::recv() - RTT estimated to %f s (client)!\n",now(), name(), rtt_conn_est_); + if (use_ackv_local_ && manage_ackv_) + ackv_->addPacket(dccph->seq_num_+1,DCCP_PACKET_NOT_RECV); + goto free_send; + } else if (dccph->type_ == DCCP_CLOSE){ + sendReset(DCCP_RST_CLOSED, 0 ,0 ,0); + reset(); + } else if (dccph->type_ != DCCP_RESET){ + //ignore + } + goto free; + break; + case DCCP_STATE_CLOSEREQ: + if (dccph->type_ == DCCP_CLOSE) { + sendReset(DCCP_RST_CLOSED,0,0,0); + reset(); + } else if (data_or_ack_pkt) + goto data; + goto free; + break; + case DCCP_STATE_CLOSING: + if (dccph->type_ == DCCP_CLOSEREQ){ + changeState(DCCP_STATE_CLOSING); //i.e. reset timer + output_ = true; + goto free_send; + } else if (data_or_ack_pkt) + goto data; + goto free; + break; + default: + fprintf(stderr, "%f, DCCP(%s):recv() - Illegal state (%d)!\n", + now(), name(),state_); + fflush(stdout); + abort(); + } + + data: + if (data_or_ack_pkt) { + if (tell_cc){ + recv_packetRecv(pkt,data_size); + send_packetRecv(pkt,data_size); + } + if(data_size > 0){ + recvBytes(data_size); //inform the application + } + } + + if (tell_app) + idle(); + free_send: + if (output_) + output(output_flag_); + free: + //remove received options + if (ackv_recv_ != NULL){ + delete ackv_recv_; + ackv_recv_ = NULL; + } + elapsed_time_recv_ = 0; + + delete (dccph->options_); + dccph->options_ = NULL; + + Packet::free(pkt); +} + +/* Send a packet, + * If this is the first packet to send, initiate a connection. + * arg: nbytes - number of bytes in packet (-1 -> infinite send) + * flags - Flags: + * "MSG_EOF" will close the connection when all data + * has been flag + */ +void DCCPAgent::sendmsg(int nbytes, const char *flags){ + if (infinite_send_) + return; + + switch (state_){ + case DCCP_STATE_CLOSED: + case DCCP_STATE_RESPOND: + case DCCP_STATE_REQUEST: + case DCCP_STATE_OPEN: + if (nbytes > 0) { + if (!(sb_->add(nbytes))) + debug("%f, DCCP(%s)::sendmsg() - Sendbuffer is full\n", now(), name()); + } else if (nbytes == 0) { + fprintf(stdout, "%f, DCCP(%s)::sendmsg() - Application tries to send 0 byte of data\n", + now(), name()); + return; + } else { + debug("%f, DCCP(%s)::sendmsg() - Infinite send activated\n", + now(), name()); + infinite_send_ = true; + } + + if (flags != NULL && strcmp(flags, "MSG_EOF") == 0) + close_on_empty_ = TRUE; + + if (state_ == DCCP_STATE_CLOSED) { + //initiate a connection + buildInitialFeatureList(); + changeState(DCCP_STATE_REQUEST); + output(); + } else if (state_ == DCCP_STATE_OPEN) + output(); + break; + case DCCP_STATE_LISTEN: + case DCCP_STATE_CLOSEREQ: + case DCCP_STATE_CLOSING: + break; + default: + fprintf(stderr, "%f, DCCP(%s)::sendmsg() - Illegal state (%d)!\n", + now(), name(),state_); + fflush(stdout); + abort(); + } +} + +/* Close the connection */ +void DCCPAgent::close(){ + debug("%f, DCCP(%s)::close() - The application wants to close the connection (state %s)\n", now(), name(),stateAsStr(state_)); + switch(state_){ + case DCCP_STATE_CLOSED: + fprintf(stdout, "%f, DCCP(%s)::close() - Already closed!\n", + now(), name()); + break; + case DCCP_STATE_RESPOND: + changeState(DCCP_STATE_CLOSEREQ); + output(); + break; + case DCCP_STATE_REQUEST: + sendReset(DCCP_RST_CLOSED,0,0,0); + reset(); + break; + case DCCP_STATE_OPEN: + if (server_) + changeState(DCCP_STATE_CLOSEREQ); + else + changeState(DCCP_STATE_CLOSING); + + output(); + break; + case DCCP_STATE_LISTEN: + reset(); + changeState(DCCP_STATE_CLOSED); + break; + case DCCP_STATE_CLOSEREQ: + case DCCP_STATE_CLOSING: + fprintf(stdout, "%f, DCCP(%s)::close() - Application tried to close when in state %s\n", + now(), name(), stateAsStr(state_)); + break; + default: + fprintf(stderr, "%f, DCCP(%s)::sendmsg() - Illegal state (%d)!\n", + now(), name(),state_); + fflush(stdout); + abort(); + } + +} + +/* Listen for incoming connections + * Use close() to stop listen + */ +void DCCPAgent::listen(){ + if (state_ == DCCP_STATE_CLOSED){ + server_ = true; + changeState(DCCP_STATE_LISTEN); + } else + debug("%f, DCCP(%s)::listen() - Listen called while in state %s (%d)\n", + now(), name(), stateAsStr(state_),state_); + +} + +/* A timeout has occured + * arg: tno - id of timeout event + */ +void DCCPAgent::timeout(int tno){ + debug("%f, DCCP(%s)::timeout() - Timeout %d occured!\n", + now(), name(), tno); + switch (tno){ + case DCCP_TIMER_RTX: + switch(state_){ + case DCCP_STATE_RESPOND: + sendReset(DCCP_RST_CLOSED,0,0,0); + reset(); + break; + case DCCP_STATE_REQUEST: + if (timer_rtx_->backOffFailed()){ + sendReset(DCCP_RST_CLOSED,0,0,0); + reset(); + } else + output(); + break; + case DCCP_STATE_CLOSING: + if (timer_rtx_->backOffFailed()){ + reset(); + } else + output(); + break; + case DCCP_STATE_CLOSEREQ: + if (timer_rtx_->backOffFailed()){ + sendReset(DCCP_RST_CLOSED,0,0,0); + reset(); + } else + output(); + break; + default: + fprintf(stderr,"%f, DCCP(%s)::timeout() - Timer (%d) should not be running in state %s!\n", + now(), name(), tno,stateAsStr(state_)); + fflush(stdout); + abort(); + } + break; + case DCCP_TIMER_SND: + switch(state_){ + case DCCP_STATE_OPEN: + output(); + break; + default: + fprintf(stderr,"%f, DCCP(%s)::timeout() - Timer (%d) should not be running in state %s!\n", + now(), name(), tno,stateAsStr(state_)); + fflush(stdout); + abort(); + } + break; + default: + fprintf(stdout,"%f, DCCP(%s)::timeout() - Unknown timeout occured (%d)!\n", + now(), name(), tno); + } +} + +/* Trace a variable. + * arg: v - traced variable + */ +void DCCPAgent::trace(TracedVar* v) { + if (nam_tracevar_) { + Agent::trace(v); + } else if (trace_all_oneline_){ + traceAll(); + }else{ + traceVar(v); + } +} + +/* Send delta packets of packetSize_ size + * arg: delta - number of packets to send + */ +void DCCPAgent::advanceby(int delta){ + if (delta <= 0) + return; + + if (!infinite_send_){ + + switch (state_){ + case DCCP_STATE_CLOSED: + case DCCP_STATE_RESPOND: + case DCCP_STATE_REQUEST: + case DCCP_STATE_OPEN: + + while (!sb_->full() && delta > 0){ + sb_->add(size_); + delta--; + } + if (sb_->full()){ + fprintf(stdout,"%f, DCCP(%s)::advanceby() - Sendbuffer is full (%d packets dropped)\n", + now(), name(), delta); + } + if (state_ == DCCP_STATE_CLOSED) { + //initiate a connection + buildInitialFeatureList(); + changeState(DCCP_STATE_REQUEST); + output(); + } else if (state_ == DCCP_STATE_OPEN) + output(); + case DCCP_STATE_LISTEN: + case DCCP_STATE_CLOSEREQ: + case DCCP_STATE_CLOSING: + break; + default: + fprintf(stderr, "%f, DCCP(%s)::advanceby() - Illegal state (%d)!\n", + now(), name(),state_); + fflush(stdout); + abort(); + + } + } +} + + diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp.h ns-2.34_dccp/dccp/dccp.h --- ns-2.34_orig/dccp/dccp.h 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp.h 2010-03-08 15:09:55.787595291 +0100 @@ -0,0 +1,560 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp.h,v 1.43 2004/03/14 16:32:53 nilmat-8 Exp $ */ + +#ifndef ns_dccp_h +#define ns_dccp_h + +#include "agent.h" +#include "dccp_types.h" +#include "dccp_packets.h" +#include "packet.h" +#include "dccp_sb.h" +#include "dccp_opt.h" +#include "dccp_ackv.h" +#include "rng.h" + +//ccid for DCCP agent (non-standard!) +#define DCCP_CCID 254 + +//limits for ndp and ccval +#define DCCP_NDP_LIMIT 8 +#define DCCP_CCVAL_LIMIT 16 + +//checksum coverage +#define DCCP_CSCOV_ALL 0 +#define DCCP_CSCOV_HEADER_ONLY 1 + +//timer identifiers +#define DCCP_TIMER_RTX 0 +#define DCCP_TIMER_SND 1 + +//retransmission parameters +#define DCCP_INITIAL_RTX_TO 3.0 +#define DCCP_MAX_RTX_TO 75.0 +#define DCCP_RESP_TO 75.0 + +//send delay for send timer +#define DCCP_SND_DELAY 0.0001 + +#define DCCP_SB_SIZE 1000 //maximum number of packets in the send buffer +#define DCCP_OPT_SIZE 512 //maximum size of options per packet +#define DCCP_FEAT_SIZE 24 //maximum size of ongoing feat neg +#define DCCP_ACKV_SIZE 20 //initial maximum size of ackvector + +//features +#define DCCP_FEAT_CC 1 +#define DCCP_FEAT_ECN 2 +#define DCCP_FEAT_ACK_RATIO 3 +#define DCCP_FEAT_ACKV 4 + +//Quiescence features (experimental) +#define DCCP_FEAT_Q_SCHEME 126 //quiescence scheme to use +#define DCCP_FEAT_Q 127 //quiescence feature + +/* Quiescence scheme */ +#define DCCP_Q_SCHEME_NORMAL 0 +#define DCCP_Q_SCHEME_Q_FEAT 1 +#define DCCP_Q_SCHEME_Q_OPT 2 + +//default values for features +#define DCCP_FEAT_DEF_CCID 2 +#define DCCP_FEAT_DEF_ECN 1 +#define DCCP_FEAT_DEF_ACK_RATIO 2 +#define DCCP_FEAT_DEF_ACKV 0 +#define DCCP_FEAT_DEF_Q_SCHEME DCCP_Q_SCHEME_NORMAL +#define DCCP_FEAT_DEF_Q 0 + +//error values for features +#define DCCP_FEAT_OK 0 +#define DCCP_FEAT_UNKNOWN -1 +#define DCCP_FEAT_NOT_PREFERED -2 +#define DCCP_FEAT_ERR_SIZE -3 +#define DCCP_FEAT_ERR_TEST -4 + +#define DCCP_NUM_DUP_ACKS 3 + +//forward decleration of class DCCPAgent +class DCCPAgent; + +/* The DCCPRetransmitTimer is used for retransmission of packet + * or as a timeout while waiting for packets. + */ +class DCCPRetransmitTimer : public TimerHandler { +protected: + DCCPAgent *agent_; //the owning agent + double delay_; //the scheduled delay + double tot_delay_; //total delayed time so far + double max_tot_delay_; //maximum delayed time + bool backoff_failed_; //true if max_tot_dealay_ is reached +public: + /* Constructor + * arg: agent - the owning agent (to notify about timeout) + * ret: A new DCCPRetransmitTimer + */ + DCCPRetransmitTimer(DCCPAgent* agent); + + /* Initialize the timer. + * arg: delay - initial delay + * maxToTDelay - total maximum delay + */ + void init(double delay, double maxTotDelay); + + /* Called when the timer has expired + * arg: e - The event that happened (i.e. the timer expired) + */ + virtual void expire(Event *e); + + /* Back off the timer (by a factor of 2) + * The last back off will reach the max_tot_delay. + */ + void backOff(); + + /* Check if the back-off failed (that is if max_tot_delay is reached) + * ret: true if back-off failed, otherwise false. + */ + bool backOffFailed(); +}; + +/* The DCCPSendTimer models the time it takes to send a packet */ +class DCCPSendTimer : public TimerHandler { +protected: + DCCPAgent *agent_; //the owning agent +public: + /* Constructor + * arg: agent - the owning agent (to notify about timeout) + * ret: A new DCCPSendTimer + */ + DCCPSendTimer(DCCPAgent* agent); + + /* Called when the timer has expired + * arg: e - The event that happened (i.e. the timer expired) + */ + virtual void expire(Event *e); +}; + +/* Class DCCPAgent implements a two-way agent using the DCCP protocol. + * Conforms to the October 2003 drafts. + * If changes are made to tcl variables, the agent should be reset + * to enable all changes. + * It provides a "cc" that simulates that it takes send_delay_ seconds + * to send a packet. + * Acks ackording to ack ratio. + * Can use ecn and/or ack vectors (no ecn verification) */ +class DCCPAgent : public Agent { +private: + RNG* nonces_; //random number generator + + //string representation of types + static char* state_str_[DCCP_NUM_STATES]; + static int hdr_size_[DCCP_NUM_PTYPES]; + static char* ptype_str_[DCCP_NUM_PTYPES]; + static char* reset_reason_str_[DCCP_NUM_RESET_REASONS]; + static char* feature_location_str_[DCCP_NUM_FEAT_LOCS]; + + bool server_; //true if this agent i a server + dccp_state state_; //current protocol state + bool packet_sent_; //true after the first packet has been sent + bool packet_recv_; //true after the first packet has been received + + bool close_on_empty_; //if true, the connection is closed when the sendbuffer is empty + + //list of feature currently under negotiation + //consider these variables as one list + u_int8_t *feat_list_num_; //feature number + dccp_feature_location *feat_list_loc_; //feature location + u_int32_t *feat_list_seq_num_; //sequence number of the packet the feature was last negotiated on + bool *feat_list_first_; //true if this feat has not been neg before + u_int8_t feat_list_used_; //number of entries in the list + + //list over features to confirm + u_int8_t *feat_conf_num_; //feature number + dccp_feature_location *feat_conf_loc_; //feature location + u_int8_t feat_conf_used_; //number of entries in the list + + int allow_mult_neg_; //allow multiple nn feature negotiations at one time + + //sequence number for the packet with last neg option processed + u_int32_t seq_last_feat_neg_; + bool feat_first_in_pkt_; //is this the first feat neg opt in pkt? + + DCCPRetransmitTimer* timer_rtx_; //retransmit timer + + DCCPSendTimer* timer_snd_; //send timer + double snd_delay_; //the delay of the send timer + + /* Creates a new packet and fills in some header fields + * ret: a new packet + */ + Packet* newPacket(); + + /* Check if an incoming packet is valid + * arg: pkt - incoming packet + * ret: true if the packet is valid, otherwise false + */ + bool checkPacket(Packet* pkt); + + /* Add feature negotiation options on packets for features + * currently under negotiation. Will use getFeature() to obtain values. + */ + void addFeatureOptions(); + + /* Finish feature negotiation for a feature abd remove it from + * the list of ongoing feature negotiations. + * arg: num - feature number + * location - feature location + * ret: true if the feature is removed + * false if it doesn't exist in the list + */ + bool finishFeatureNegotiation(u_int8_t num, dccp_feature_location location); + + /* Add a feature to the list of features to confirm + * Will only add one entry for each (feat,loc) pair + * arg: num - feature number + * location - feature location + */ + void confirmFeature(u_int8_t num, dccp_feature_location location); + + /* Find a feature in the list of ongoing negotiations + * arg: num - feature number + * location - feature location + * ret: position in the feature list if successfull + * otherwise -1 + */ + int findFeatureInList(u_int8_t num, dccp_feature_location location); + +protected: + int ndp_limit_; //ndp limit + int ccval_limit_; //ccval limit + + DCCPSendBuffer* sb_; //send buffer. Contains packet sizes to send. + bool infinite_send_; //send an unlimited amount of data + + u_int8_t ndp_; //ndp of last packet sent + u_int32_t seq_num_; //next sequence number to send + + u_int32_t seq_num_recv_; //highest sequence number received so far + u_int32_t ack_num_recv_; //highest ack received so far + + DCCPAckVector *ackv_recv_; //the received ack vector (if any) + u_int32_t elapsed_time_recv_; //the received time elapsed (if any) + + u_int32_t ack_num_; //sequence number to ack + bool send_ack_; //send an ack + u_int8_t ccval_; //cc value to send + int cscov_; //checksum coverage value to send + + bool conn_est_; //true if the connection is established + + bool output_; //run output() after packet has been received + bool output_flag_; //flag to hand to output. i.e. output(output_flag_) + + DCCPOptions *opt_; //options to attach to the next outgoing packet + + //feature variables (name_location_) + int ccid_; //ccid is the same on both locations + int use_ecn_local_; + int use_ecn_remote_; + int ack_ratio_local_; + int ack_ratio_remote_; + int use_ackv_local_; + int use_ackv_remote_; + int q_scheme_; //quiescence scheme is the same on both locations + int q_local_; //specifies if the remote sender is quiescent + int q_remote_; //specifies if I am quiescent + + DCCPAckVector *ackv_; //local ack_vector of all incoming packets + bool send_ackv_; //if true, send ack vector on all acks + bool manage_ackv_; //if true, add incoming packets to ackv_ and clear state on ack-of-ack + + int nam_tracevar_; + int trace_all_oneline_; + + double initial_rtx_to_; //inital value for rtx timer delay + double max_rtx_to_; //maximum total rtx delay (then back-off fails) + double resp_to_; //response timer delay + + int sb_size_; //send buffer size + int opt_size_; //maximum size of options on packets + int feat_size_; //maximum number of ongoing feat neg + int ackv_size_; //initial ackv_ size + + int ar_unacked_; //unacked data packets received + + double rtt_conn_est_; //rtt measured during connection establishment + + /* Return the current (simulated) time + * ret: current time + */ + inline double now() { return (&Scheduler::instance() == NULL ? 0 : Scheduler::instance().clock()); } + + /* OTcl binding of variables */ + virtual void delay_bind_init_all(); + virtual int delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer); + + /* Return a string representation of a type */ + const char* stateAsStr(dccp_state state); + const char* packetTypeAsStr(dccp_packet_type type); + const char* resetReasonAsStr(dccp_reset_reason reason); + const char* featureLocationAsStr(dccp_feature_location location); + /* Return the header size (without options) of a packet type + * arg: type - packet type + * ret: header size + */ + int headerSize(dccp_packet_type type); + + /* Extract the ecn codepoint + * arg: pkt - packet + * ret: ecn codepoint of packet + */ + static dccp_ecn_codepoint getECNCodePoint(Packet* pkt); + + /* Get the packet nonce + * arg: pkt - packet + * ret: 0 if ECT(0) is set + * 1 if ECT(1) is set + * otherwise -1 + */ + static int getNonce(Packet* pkt); + + /* Changes state (and cancel/init/sched timers) + * arg: new_state - new state + */ + void changeState(dccp_state new_state); + + /* Reinitialize the agent */ + virtual void reset(); + + /* Send a packet. + * arg: try_pure_ack - if true, try to send a pure ack if cc + * refuses to send a dataack + */ + void output(bool try_pure_ack = false); + + /* Send a reset packet. + * arg: reason - reason for reset + * data - data to include on reset packet + */ + void sendReset(dccp_reset_reason reason, u_int8_t data1, + u_int8_t data2, u_int8_t data3); + + /* Parse options in a packet. + * Will call parseOption() on every option found. + * Fails if parseOption() fails. + * arg: pkt - packet + * ret: true if parse is successful + * false if the parse failed and the connection should be reset + */ + virtual bool parseOptions(Packet *pkt); + + /* Process an incoming option. + * arg: type - option type + * data - option data + * size - size of opption data + * pkt - the packet the option was received on + * ret: true if option processing was successful + * false if it fails + * This function will process: + * feature negotiation options + * ack vectors (extraction only) + * elapsed time (extraction only) + */ + virtual bool processOption(u_int8_t type, u_char* data, u_int8_t size, Packet *pkt); + + /* Change a feature (i.e. initiate a feature negotiation). + * arg: num - feature number + * location - feature location + * ret: true if the feature is added to the list of ongoing feat neg + * false if the list is full or the feature is already present + */ + bool changeFeature(u_int8_t num, dccp_feature_location location); + + /* Check if a feature is currently under negotiation. + * arg: num - feature number + * location - feature location + * ret: true if the feature is under negotiation + * false otherwise + */ + bool featureIsChanging(u_int8_t num, dccp_feature_location location); + + /* Build the list of features to neg on DCCP-Request packet */ + virtual void buildInitialFeatureList(); + + /* Set a value for a specific feature. + * arg: num - feature number + * location - feature location + * data - feature data + * size - size of data + * testSet - if true, only check value for correctness, don't set + * ret: DCCP_FEAT_OK if successful + * DCCP_FEAT_UNKNOWN if the feature number is unknown + * DCCP_FEAT_NOT_PREFERED if the value is not prefered + * DCCP_FEAT_ERR_SIZE if the size does not match the feature + * DCCP_FEAT_ERR_TEST if a non-neg feature failed the test. + * a negotiable feature that fails returns NOT_PREFERED + * Note: DCCP_FEAT_NOT_PREFERED is not applicable for non neg features. + * NOT_PREFERED and ERR_SIZE will trigger a reset + */ + virtual int setFeature(u_int8_t num, dccp_feature_location location, + u_char* data, u_int8_t size, bool testSet = false); + + /* Obtain a value of a feature. + * Used in addFeatureOptions() to get the feature value. + * arg: num - feature number + * location - feature location + * data - feature data + * size - maximum size of data + * ret: DCCP_FEAT_OK if successful + * DCCP_FEAT_UNKNOWN if the feature number is unknown + * DCCP_FEAT_ERR_SIZE if the size is too small + */ + virtual int getFeature(u_int8_t num, dccp_feature_location location, + u_char* data, u_int8_t maxSize); + + /* Obtain the feature type + * arg: num - feature number + * ret: the feature type, or DCCP_FEAT_TYPE_UKNOWN if unknown + */ + virtual dccp_feature_type getFeatureType(u_int8_t num); + + /* Cancel all timers */ + virtual void cancelTimers(); + + //Interface to cc + + /* Ask sender permission to send a packet + * arg: dataSize - size of data in packet (0 = pure ACK) + * pkt - the packet to send + * ret: true if permission is granted, otherwise false + * Note: Packet should not be used for other than manipulating + * ecn marks! + */ + virtual bool send_askPermToSend(int dataSize, Packet *pkt); + + /* A(n) ACK/DATA/DATAACK packet has been sent (sender) + * arg: pkt - packet sent + * moreToSend - true if there exist more data to send + * dataSize - size of data sent + */ + virtual void send_packetSent(Packet *pkt, bool moreToSend, int dataSize); + /* A(n) ACK/DATA/DATAACK packet has been received (sender) + * arg: pkt - packet received + * dataSize - size of data in packet + * If this function would like to send a packet, set output_ = true + * and output_flag_ if appropriate. + */ + virtual void send_packetRecv(Packet *pkt, int dataSize); + + /* A ACK/DATA/DATAACK packet has been received (receiver) + * arg: pkt - packet received + * dataSize - size of data in packet + * If this function would like to send a packet, set output_ = true + * and output_flag_ if appropriate. + */ + virtual void recv_packetRecv(Packet *pkt, int dataSize); + + /* Tracing functions */ + virtual void traceAll(); + virtual void traceVar(TracedVar* v); +public: + + //statistics on sent packets + + TracedInt num_data_pkt_; + TracedInt num_ack_pkt_; + TracedInt num_dataack_pkt_; + + /* Constructor + * ret: A new DCCPAgent + */ + DCCPAgent(); + + /* Destructor */ + virtual ~DCCPAgent(); + + + /* Process a "function call" from OTCl + * arg: argc - number of arguments + * argv - arguments + * ret: TCL_OK if successful, TCL_ERROR otherwise + * + * Supported function call handled by this agent: + * advanceby x - advances num packets + * advance x - = advanceby x + */ + int command(int argc, const char*const* argv); + + /* Receive a packet + * arg: pkt - Packet received + * handler - handler + */ + virtual void recv(Packet* pkt, Handler* handler); + + /* Send a packet, + * If this is the first packet to send, initiate a connection. + * arg: nbytes - number of bytes in packet (-1 -> infinite send) + * flags - Flags: + * "MSG_EOF" will close the connection when all data + * has been flag + */ + virtual void sendmsg(int nbytes, const char *flags = 0); + + /* Close the connection */ + virtual void close(); + + /* Listen for incoming connections + * Use close() to stop listen + */ + virtual void listen(); + + /* A timeout has occured + * arg: tno - id of timeout event + * Handles: Retransmit timer - DCCP_TIMER_RTX + * Send timer - DCCP_TIMER_SND + */ + virtual void timeout(int tno); + + /* Trace a variable. + * arg: v - traced variable + */ + void trace(TracedVar* v); + + /* Send delta packets of packetSize_ size + * arg: delta - number of packets to send + */ + void advanceby(int delta); +}; + +#endif + + + + + + diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp_opt.cc ns-2.34_dccp/dccp/dccp_opt.cc --- ns-2.34_orig/dccp/dccp_opt.cc 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp_opt.cc 2010-03-08 15:09:55.787595291 +0100 @@ -0,0 +1,215 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp_opt.cc,v 1.7 2004/02/23 18:05:31 nilmat-8 Exp $ */ + +#include +#include "dccp_opt.h" + +/* Constructor + * arg: size - Maximum size for stored options + * returns: a new DCCPOptions object + */ +DCCPOptions::DCCPOptions(int size): size_(0), max_size_(size), current_(DCCP_OPT_ERR_WALK){ + options_ = new u_char[max_size_]; +} + +/* Copy constructor */ +DCCPOptions::DCCPOptions(const DCCPOptions& orig){ + options_ = new u_char[orig.max_size_]; + memcpy (options_, orig.options_, orig.size_); + size_ = orig.size_; + max_size_ = orig.max_size_; + current_ = orig.current_; +} + + +/* Destructor */ +DCCPOptions::~DCCPOptions(){ + delete [] options_; +} + +/* Add an option + * arg: type - Option type + * data - Option data + * dataSize - Size of option data + * ret: DCCP_OPT_NO_ERR if successful + * DCCP_OPT_ERR_SIZE if the size of the option is wrong + * DCCP_OPT_ERR_FULL if max_size_ is reached + */ +int DCCPOptions::addOption(u_int8_t type, const u_char* data, u_int8_t dataSize){ + int length = ((dataSize == 0) ? 1 : dataSize+2); + if ((type < DCCP_OPT_SINGLE_BYTE_LIMIT && dataSize > 0) || + (type >= DCCP_OPT_SINGLE_BYTE_LIMIT && dataSize == 0) || + length > DCCP_OPT_MAX_LENGTH) + return DCCP_OPT_ERR_SIZE; + + if (size_ + length > max_size_) //out of space + return DCCP_OPT_ERR_FULL; + + options_[size_++] = type; + if (dataSize > 0){ + options_[size_++] = length; + for(int i = 0; i < dataSize; i++) + options_[size_++] = data[i]; + } + + return DCCP_OPT_NO_ERR; +} + +/* Add a feature option + * arg: type - Option type + * feat_num - Feature number + * data - Feature data + * dataSize - Size of feature data + * ret: like addOption() but also + * DCCP_OPT_ERR_TYPE if type is not ChangeR/L or ConfirmR/L + */ +int DCCPOptions::addFeatureOption(u_int8_t type, u_int8_t feat_num, const u_char *data, + u_int8_t dataSize){ + int length = dataSize + 3; + + if (dataSize == 0 || length > DCCP_OPT_MAX_LENGTH) //must include some data, but not too much + return DCCP_OPT_ERR_SIZE; + + if (type < DCCP_OPT_CHANGEL || type > DCCP_OPT_CONFIRMR) + return DCCP_OPT_ERR_TYPE; //not an feature option + + if (size_ + length > max_size_) //out of space + return DCCP_OPT_ERR_FULL; + + options_[size_++] = type; + options_[size_++] = length; + options_[size_++] = feat_num; + + for(int i = 0; i < dataSize; i++) + options_[size_++] = data[i]; + + return DCCP_OPT_NO_ERR; +} + +/* Extract an option + * arg: type - Option type + * data - Array to copy option data to + * maxSize - Maximum size of data array + * ret: DCCP_OPT_NO_ERR if successful (for single byte option) + * size of data obtained if successful for other options + * DCCP_OPT_ERR_SIZE if the size of the data array is to small + * DCCP_OPT_ERR_NOT_FOUND if the option is not found + */ +int DCCPOptions::getOption(u_int8_t type, u_char* data, u_int8_t maxSize){ + int walker = 0, dataSize = 0; + + while (walker < size_){ + if (options_[walker] < DCCP_OPT_SINGLE_BYTE_LIMIT) { //single byte option + if (options_[walker] == type) + return DCCP_OPT_NO_ERR; + walker++; + } else { //multi byte option + dataSize = options_[walker+1]-2; + walker += 2; + if (options_[walker-2] == type) { + if (maxSize >= dataSize){ + for(int i = walker; i < walker+dataSize; i++) + data[i-walker] = options_[i]; + return dataSize; + } else + return DCCP_OPT_ERR_SIZE; + //not reached + } + + walker += dataSize; + } + + } + return DCCP_OPT_ERR_NOT_FOUND; +} + +/* Get the total size of options, including necessary padding. + * ret: size of options + */ +u_int16_t DCCPOptions::getSize(){ + u_int16_t result = size_; + if ((size_ % DCCP_OPT_ALIGNMENT) > 0) //add padding + result += DCCP_OPT_ALIGNMENT - (size_ % DCCP_OPT_ALIGNMENT); + return result; +} + +/* Start a walk through the options. */ +void DCCPOptions::startOptionWalk(){ + current_ = 0; +} + +/* Return the next option in the option walk + * arg: type - Option type found + * data - Array to copy option data to + * maxSize - Maximum size of data array + * ret: like getOption() but also + * DCCP_OPT_ERR_WALK if option walk is not initiated. + */ +int DCCPOptions::nextOption(u_int8_t* type, u_char* data, u_int8_t maxSize){ + if (current_ == DCCP_OPT_ERR_WALK) + return DCCP_OPT_ERR_WALK; + if (current_ >= size_) + return DCCP_OPT_ERR_NOT_FOUND; + + if (options_[current_] < DCCP_OPT_SINGLE_BYTE_LIMIT) { //single byte option + *type = options_[current_]; + current_++; + } else { //multi byte option + int dataSize = options_[current_+1]-2; + current_ += 2; + if (maxSize >= dataSize){ + *type = options_[current_-2]; + for(int i = current_; i < current_+dataSize; i++) + data[i-current_] = options_[i]; + current_ += dataSize; + return dataSize; + } else { + current_ -= 2; + return DCCP_OPT_ERR_SIZE; + } + } + return DCCP_OPT_NO_ERR; +} + +/* Print all options and state (for debug) */ +void DCCPOptions::print(){ + fprintf(stdout, "DCCPOptions :: size %d, max_size %d, current %d\n", size_, max_size_, current_); + if (size_ == 0) + fprintf(stdout, "No option is present\n"); + else { + fprintf(stdout, "Option buffer:\n"); + for(int i = 0; i < size_; i++){ + fprintf(stdout, "%d ", options_[i]); + if ( (i != 0 && (i+1) % DCCP_OPT_PRINT_ALIGNMENT == 0) || i == size_-1) + fprintf(stdout, "\n"); + } + } + fflush(stdout); +} diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp_opt.h ns-2.34_dccp/dccp/dccp_opt.h --- ns-2.34_orig/dccp/dccp_opt.h 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp_opt.h 2010-03-08 15:09:55.797597400 +0100 @@ -0,0 +1,141 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp_opt.h,v 1.8 2004/02/23 18:05:31 nilmat-8 Exp $ */ + +#ifndef ns_dccp_opt_h +#define ns_dccp_opt_h + +#include "config.h" +#include "dccp_types.h" + +//options types less than this limit consist of a single byte +#define DCCP_OPT_SINGLE_BYTE_LIMIT 32 +#define DCCP_OPT_CC_START 128 //CCID specific options starts here + +//total size is padded to this byte boundary +#define DCCP_OPT_ALIGNMENT 4 +#define DCCP_OPT_MAX_LENGTH 255 //maximum length of one option + +#define DCCP_OPT_PRINT_ALIGNMENT 16 //print this number of option data/row + +//options errors +#define DCCP_OPT_NO_ERR 0 +#define DCCP_OPT_ERR_SIZE -1 +#define DCCP_OPT_ERR_FULL -2 +#define DCCP_OPT_ERR_NOT_FOUND -3 +#define DCCP_OPT_ERR_TYPE -4 +#define DCCP_OPT_ERR_WALK -5 + +//options +#define DCCP_OPT_PADDING 0 +#define DCCP_OPT_QUIESCENCE 30 //experimental!!! +#define DCCP_OPT_CHANGEL 33 +#define DCCP_OPT_CONFIRML 34 +#define DCCP_OPT_CHANGER 35 +#define DCCP_OPT_CONFIRMR 36 +#define DCCP_OPT_ACK_VECTOR_N0 37 +#define DCCP_OPT_ACK_VECTOR_N1 38 +#define DCCP_OPT_ELAPSED_TIME 46 + +//units +#define DCCP_OPT_ELAPSED_TIME_UNIT 0.0001 + +/* Class DCCPOptions models dccp options in packets */ +class DCCPOptions { +private: + u_char* options_; //array with options + u_int16_t size_; //size used in the array + u_int16_t max_size_; //the maximum size of the array + int current_; //current position in walk +public: + /* Constructor + * arg: size - Maximum size for stored options + * returns: a new DCCPOptions object + */ + DCCPOptions(int size); + + /* Copy constructor */ + DCCPOptions(const DCCPOptions&); + + /* Destructor */ + ~DCCPOptions(); + + /* Add an option + * arg: type - Option type + * data - Option data + * dataSize - Size of option data + * ret: DCCP_OPT_NO_ERR if successful + * DCCP_OPT_ERR_SIZE if the size of the option is wrong + * DCCP_OPT_ERR_FULL if max_size_ is reached + */ + int addOption(u_int8_t type, const u_char* data, u_int8_t dataSize); + + /* Add a feature option + * arg: type - Option type + * feat_num - Feature number + * data - Feature data + * dataSize - Size of feature data + * ret: like addOption() but also + * DCCP_OPT_ERR_TYPE if type is not ChangeR/L or ConfirmR/L + */ + int addFeatureOption(u_int8_t type, u_int8_t feat_num, const u_char *data, u_int8_t dataSize); + + /* Extract an option + * arg: type - Option type + * data - Array to copy option data to + * maxSize - Maximum size of data array + * ret: DCCP_OPT_NO_ERR if successful (for single byte option) + * size of data obtained if successful for other options + * DCCP_OPT_ERR_SIZE if the size of the data array is to small + * DCCP_OPT_ERR_NOT_FOUND if the option is not found + */ + int getOption(u_int8_t type, u_char* data, u_int8_t maxSize); + + /* Get the total size of options, including necessary padding. + * ret: size of options + */ + u_int16_t getSize(); + + /* Start a walk through the options. */ + void startOptionWalk(); + + /* Return the next option in the option walk + * arg: type - Option type found + * data - Array to copy option data to + * maxSize - Maximum size of data array + * ret: like getOption() but also + * DCCP_OPT_ERR_WALK if option walk is not initiated. + */ + int nextOption(u_int8_t* type, u_char* data, u_int8_t maxSize); + + /* Print all options and state (for debug) */ + void print(); +}; + +#endif diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp_packets.cc ns-2.34_dccp/dccp/dccp_packets.cc --- ns-2.34_orig/dccp/dccp_packets.cc 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp_packets.cc 2010-03-08 15:09:55.797597400 +0100 @@ -0,0 +1,116 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp_packets.cc,v 1.2 2004/02/23 18:05:31 nilmat-8 Exp $ */ + +#include "packet.h" +#include "dccp_packets.h" + +int hdr_dccp::offset_; +int hdr_dccpack::offset_; +int hdr_dccpreq::offset_; +int hdr_dccpresp::offset_; +int hdr_dccpdata::offset_; +int hdr_dccpdataack::offset_; +int hdr_dccpclose::offset_; +int hdr_dccpclosereq::offset_; +int hdr_dccpreset::offset_; + +//OTcl linkage for packet headers +static class DCCPHeaderClass : public PacketHeaderClass { +public: + DCCPHeaderClass() : PacketHeaderClass("PacketHeader/DCCP", + sizeof(hdr_dccp)){ + bind_offset(&hdr_dccp::offset_); + } +} class_dccphdr; + +static class DCCPResetHeaderClass : public PacketHeaderClass { +public: + DCCPResetHeaderClass() : PacketHeaderClass("PacketHeader/DCCP_RESET", + sizeof(hdr_dccpreset)){ + bind_offset(&hdr_dccpreset::offset_); + } +} class_dccpresethdr; + +static class DCCPResponseHeaderClass : public PacketHeaderClass { +public: + DCCPResponseHeaderClass() : PacketHeaderClass("PacketHeader/DCCP_RESP", + sizeof(hdr_dccpresp)){ + bind_offset(&hdr_dccpresp::offset_); + } +} class_dccpresphdr; + +static class DCCPRequestHeaderClass : public PacketHeaderClass { +public: + DCCPRequestHeaderClass() : PacketHeaderClass("PacketHeader/DCCP_REQ", + sizeof(hdr_dccpreq)){ + bind_offset(&hdr_dccpreq::offset_); + } +} class_dccpreqhdr; + +static class DCCPAckHeaderClass : public PacketHeaderClass { +public: + DCCPAckHeaderClass() : PacketHeaderClass("PacketHeader/DCCP_ACK", + sizeof(hdr_dccpack)){ + bind_offset(&hdr_dccpack::offset_); + } +} class_dccpackhdr; + +static class DCCPDataAckHeaderClass : public PacketHeaderClass { +public: + DCCPDataAckHeaderClass() : PacketHeaderClass("PacketHeader/DCCP_DATAACK", + sizeof(hdr_dccpdataack)){ + bind_offset(&hdr_dccpdataack::offset_); + } +} class_dccpdataackhdr; + +static class DCCPDataHeaderClass : public PacketHeaderClass { +public: + DCCPDataHeaderClass() : PacketHeaderClass("PacketHeader/DCCP_DATA", + sizeof(hdr_dccpdata)){ + bind_offset(&hdr_dccpdata::offset_); + } +} class_dccpdatahdr; + +static class DCCPCloseHeaderClass : public PacketHeaderClass { +public: + DCCPCloseHeaderClass() : PacketHeaderClass("PacketHeader/DCCP_CLOSE", + sizeof(hdr_dccpclose)){ + bind_offset(&hdr_dccpclose::offset_); + } +} class_dccpclosehdr; + +static class DCCPCloseReqHeaderClass : public PacketHeaderClass { +public: + DCCPCloseReqHeaderClass() : PacketHeaderClass("PacketHeader/DCCP_CLOSEREQ", + sizeof(hdr_dccpclosereq)){ + bind_offset(&hdr_dccpclosereq::offset_); + } +} class_dccpclosereqhdr; + diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp_packets.h ns-2.34_dccp/dccp/dccp_packets.h --- ns-2.34_orig/dccp/dccp_packets.h 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp_packets.h 2010-03-08 15:09:55.797597400 +0100 @@ -0,0 +1,137 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp_packets.h,v 1.3 2004/03/14 16:32:53 nilmat-8 Exp $ */ + +#ifndef ns_dccp_packets_h +#define ns_dccp_packets_h + +#include "dccp_types.h" +#include "dccp_opt.h" + +struct hdr_dccp { + //generic dccp header + dccp_packet_type type_; + u_int8_t ccval_; + u_int8_t ndp_; + u_int32_t seq_num_; + u_int8_t data_offset_; + u_int8_t cscov_; + + //options + DCCPOptions *options_; + + //ns-2 header offset + static int offset_; + inline static int& offset() { return offset_; } + inline static hdr_dccp* access(const Packet* p) { + return (hdr_dccp*) p->access(offset_); + } +}; + +struct hdr_dccpack { + u_int32_t ack_num_; + + //ns-2 header offset + static int offset_; + inline static int& offset() { return offset_; } + inline static hdr_dccpack* access(const Packet* p) { + return (hdr_dccpack*) p->access(offset_); + } +}; + +struct hdr_dccpreset { + //reset packet info + dccp_reset_reason rst_reason_; + u_int8_t rst_data1_; + u_int8_t rst_data2_; + u_int8_t rst_data3_; + + //ns-2 header offset + static int offset_; + inline static int& offset() { return offset_; } + inline static hdr_dccpreset* access(const Packet* p) { + return (hdr_dccpreset*) p->access(offset_); + } +}; + +struct hdr_dccpreq { + //ns-2 header offset + static int offset_; + inline static int& offset() { return offset_; } + inline static hdr_dccpreq* access(const Packet* p) { + return (hdr_dccpreq*) p->access(offset_); + } +}; + +struct hdr_dccpresp { + //ns-2 header offset + static int offset_; + inline static int& offset() { return offset_; } + inline static hdr_dccpresp* access(const Packet* p) { + return (hdr_dccpresp*) p->access(offset_); + } +}; + +struct hdr_dccpdata { + //ns-2 header offset + static int offset_; + inline static int& offset() { return offset_; } + inline static hdr_dccpdata* access(const Packet* p) { + return (hdr_dccpdata*) p->access(offset_); + } +}; + +struct hdr_dccpdataack { + //ns-2 header offset + static int offset_; + inline static int& offset() { return offset_; } + inline static hdr_dccpdataack* access(const Packet* p) { + return (hdr_dccpdataack*) p->access(offset_); + } +}; + +struct hdr_dccpclose { + //ns-2 header offset + static int offset_; + inline static int& offset() { return offset_; } + inline static hdr_dccpclose* access(const Packet* p) { + return (hdr_dccpclose*) p->access(offset_); + } +}; + +struct hdr_dccpclosereq { + //ns-2 header offset + static int offset_; + inline static int& offset() { return offset_; } + inline static hdr_dccpclosereq* access(const Packet* p) { + return (hdr_dccpclosereq*) p->access(offset_); + } +}; + +#endif diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp_sb.cc ns-2.34_dccp/dccp/dccp_sb.cc --- ns-2.34_orig/dccp/dccp_sb.cc 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp_sb.cc 2010-03-08 15:09:55.797597400 +0100 @@ -0,0 +1,123 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp_sb.cc,v 1.7 2004/02/23 18:05:31 nilmat-8 Exp $ */ + +#include +#include "dccp_sb.h" + +/* Constructor + * arg: size - Maximum size of buffer + * ret: a new DCCPSendBuffer + */ +DCCPSendBuffer::DCCPSendBuffer(int size): head_(0), tail_(0), + size_(size), empty_(true) { + if (size_ <= 0) + size_ = 1; + + buf_ = new int[size_]; +} + +/* Destructor */ +DCCPSendBuffer::~DCCPSendBuffer(){ + delete [] buf_; +} + +/* Add an element to the buffer + * arg: size - size of element + * ret: true if successful, false if full + */ +bool DCCPSendBuffer::add(int packet_size){ + if (empty_) { + buf_[head_] = packet_size; + empty_ = false; + } else if ( (tail_+1) % size_ == head_) //buffer is full + return false; + else { + tail_ = (tail_ + 1) % size_; + buf_[tail_] = packet_size; + } + return true; +} + +/* Check if the buffer is full + * ret: true if full, otherwise false + */ +bool DCCPSendBuffer::full(){ + return (!empty_ && ((tail_+1) % size_ == head_)); +} + +/* Check if the buffer is empty + * ret: true if empty, otherwise false + */ +bool DCCPSendBuffer::empty(){ + return empty_; +} + +/* Return the top element (without removing it) + * ret: the top element. If empty, return 0. + */ +int DCCPSendBuffer::top(){ + int result = 0; + if (!empty_) + result = buf_[head_]; + return result; +} + +/* Return and remove the top element + * ret: the top element. If empty, return 0. + */ +int DCCPSendBuffer::remove(){ + int result = 0; + if (!empty_){ + result = buf_[head_]; + //remove element + if (head_ != tail_) + head_ = (head_+1) % size_; + else + empty_ = true; + } + return result; +} + +/* Print send buffer contents and state (for debug) */ +void DCCPSendBuffer::print(){ + int walker_ = head_; + if (empty_) + fprintf(stdout, "DCCPSendBuffer :: Buffer is empty (size %d, head %d, tail %d)\n", size_, head_, tail_); + else { + fprintf(stdout, "DCCPSendBuffer :: Buffer (size %d, head %d, tail %d): ", size_, head_, tail_); + while(walker_ != tail_){ + fprintf(stdout, "%d ",buf_[walker_]); + walker_ = (walker_ + 1) % size_; + } + fprintf(stdout, "%d\n",buf_[walker_]); + } + fflush(stdout); +} + diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp_sb.h ns-2.34_dccp/dccp/dccp_sb.h --- ns-2.34_orig/dccp/dccp_sb.h 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp_sb.h 2010-03-08 15:09:55.787595291 +0100 @@ -0,0 +1,88 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp_sb.h,v 1.5 2004/02/23 18:05:31 nilmat-8 Exp $ */ + +#ifndef ns_dccp_sb_h +#define ns_dccp_sb_h + +/* A (circular) send buffer consisting of packet sizes */ +class DCCPSendBuffer { +private: + int* buf_; //buffer + int head_,tail_; //head and tail of circular buffer + int size_; //maximum size of buffer + bool empty_; //if its empty or not +public: + /* Constructor + * arg: size - Maximum size of buffer + * ret: a new DCCPSendBuffer + */ + DCCPSendBuffer(int size); + + /* Destructor */ + ~DCCPSendBuffer(); + + /* Add an element to the buffer + * arg: size - size of element + * ret: true if successful, false if full + */ + bool add(int size); + + /* Check if the buffer is full + * ret: true if full, otherwise false + */ + bool full(); + + /* Check if the buffer is empty + * ret: true if empty, otherwise false + */ + bool empty(); + + /* Return the top element (without removing it) + * ret: the top element. If empty, return 0. + */ + int top(); + + /* Return and remove the top element + * ret: the top element. If empty, return 0. + */ + int remove(); + + /* Print send buffer contents and state (for debug) */ + void print(); +}; + +#endif + + + + + + + diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp_tcplike.cc ns-2.34_dccp/dccp/dccp_tcplike.cc --- ns-2.34_orig/dccp/dccp_tcplike.cc 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp_tcplike.cc 2010-03-08 15:09:55.797597400 +0100 @@ -0,0 +1,1232 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp_tcplike.cc,v 1.44 2004/03/24 14:05:23 nilmat-8 Exp $ */ + +#include "ip.h" +#include "dccp_tcplike.h" +#include "flags.h" + +#define DCCP_TCPLIKE_IN_WINDOW 1 +#define DCCP_TCPLIKE_AFTER_WINDOW 0 + +//OTcl linkage for DCCP TCPlike agent +static class DCCPTCPlikeClass : public TclClass { +public: + DCCPTCPlikeClass() : TclClass("Agent/DCCP/TCPlike") {}; + TclObject* create(int argc, const char*const* argv){ + return (new DCCPTCPlikeAgent()); + } +} class_dccptcplike; + +//methods for timer classes + +/* Constructor + * arg: agent - the owning agent (to notify about timeout) + * ret: A new DCCPTCPlikeTOTimer + */ +DCCPTCPlikeTOTimer::DCCPTCPlikeTOTimer(DCCPTCPlikeAgent* agent) : TimerHandler(){ + agent_ = agent; +} + +/* Called when the timer has expired + * arg: e - The event that happened (i.e. the timer expired) + */ +void DCCPTCPlikeTOTimer::expire(Event *e){ + agent_->timeout(DCCP_TCPLIKE_TIMER_TO); +} + +/* Constructor + * arg: agent - the owning agent (to notify about timeout) + * ret: A new DCCPTCPlikeDelayedAckTimer + */ +DCCPTCPlikeDelayedAckTimer::DCCPTCPlikeDelayedAckTimer(DCCPTCPlikeAgent* agent) : TimerHandler(){ + agent_ = agent; +} + +/* Called when the timer has expired + * arg: e - The event that happened (i.e. the timer expired) + */ +void DCCPTCPlikeDelayedAckTimer::expire(Event *e){ + agent_->timeout(DCCP_TCPLIKE_TIMER_DACK); +} + +//private methods + + +/* Clear the send history */ +inline void DCCPTCPlikeAgent::clearSendHistory(){ + struct dccp_tcplike_send_hist_entry *elm, *elm2; + + /* Empty packet history */ + elm = STAILQ_FIRST(&send_hist_); + while (elm != NULL) { + elm2 = STAILQ_NEXT(elm, linfo_); + delete elm; + elm = elm2; + } + + STAILQ_INIT(&send_hist_); +} + +/* Find a specific packet in the send history + * arg: seq_num - sequence number of packet to find + * start_elm - start searching from this element + * ret: pointer to the correct element if the element is found + * otherwise the closest packet with lower seq num is returned + * which can be NULL. + */ +inline struct dccp_tcplike_send_hist_entry* DCCPTCPlikeAgent::findPacketInSendHistory(u_int32_t seq_num, dccp_tcplike_send_hist_entry* start_elm){ + struct dccp_tcplike_send_hist_entry *elm = start_elm; + while (elm != NULL && elm->seq_num_ > seq_num) + elm = STAILQ_NEXT(elm, linfo_); + return elm; +} + +/* Remove packets from send history + * arg: trim_to - remove packets with lower sequence numbers than this + * Note: if trim_to does not exist in the history, trim_to is set to the next + * lower sequence number found before trimming. + */ +void DCCPTCPlikeAgent::trimSendHistory(u_int32_t trim_to){ + if (trim_to > hist_last_seq_){ + struct dccp_tcplike_send_hist_entry *elm, *elm2; + //find packet corresponding to trim_to + elm = findPacketInSendHistory(trim_to, STAILQ_FIRST(&send_hist_)); + if (elm != NULL){ + //remove older packets + elm2 = STAILQ_NEXT(elm, linfo_); + while (elm2 != NULL){ + STAILQ_REMOVE(&send_hist_,elm2,dccp_tcplike_send_hist_entry,linfo_); + delete elm2; + elm2 = STAILQ_NEXT(elm, linfo_); + } + } + hist_last_seq_ = trim_to; + } +} + +/* Prints the contents of the send history */ +void DCCPTCPlikeAgent::printSendHistory(){ + struct dccp_tcplike_send_hist_entry *elm = STAILQ_FIRST(&send_hist_); + if (elm == NULL) + fprintf(stdout, "Packet history is empty (send)\n"); + else { + fprintf(stdout, "Packet history (send):\n"); + while(elm != NULL){ + fprintf(stdout,"Packet: seq %d, t_sent %f, ecn %d\n", elm->seq_num_, elm->t_sent_, elm->ecn_); + elm = STAILQ_NEXT(elm, linfo_); + } + } +} + +/* Clear the history of received packets */ +inline void DCCPTCPlikeAgent::clearSendRecvHistory(){ + struct dccp_tcplike_send_recv_hist_entry *elm, *elm2; + + /* Empty packet history */ + elm = STAILQ_FIRST(&send_recv_hist_); + while (elm != NULL) { + elm2 = STAILQ_NEXT(elm, linfo_); + delete elm; + elm = elm2; + } + + STAILQ_INIT(&send_recv_hist_); +} + +/* Insert a packet in the receive history + * Will not insert "lost" packets or duplicates. + * arg: packet - entry representing the packet to insert + * ret: true if packet was inserted, otherwise false. + */ +bool DCCPTCPlikeAgent::insertInSendRecvHistory(dccp_tcplike_send_recv_hist_entry *packet){ + struct dccp_tcplike_send_recv_hist_entry *elm, *elm2; + int num_later = 0; + + if (STAILQ_EMPTY(&send_recv_hist_)){ //history is empty + STAILQ_INSERT_HEAD(&send_recv_hist_, packet, linfo_); + } else { //history contains at least one entry + elm = STAILQ_FIRST(&send_recv_hist_); + if (packet->seq_num_ > elm->seq_num_) //insert first in history + STAILQ_INSERT_HEAD(&send_recv_hist_, packet, linfo_); + else if (packet->seq_num_ == elm->seq_num_) //duplicate + return false; + else { //packet should be inserted somewhere after the head + num_later = 1; + + //walk through the history to find the correct place + elm2 = STAILQ_NEXT(elm,linfo_); + while(elm2 != NULL){ + if (packet->seq_num_ > elm2->seq_num_){ + STAILQ_INSERT_AFTER(&send_recv_hist_, elm, packet, linfo_); + break; + } else if (packet->seq_num_ == elm2->seq_num_) { + return false; //duplicate + } + + elm = elm2; + elm2 = STAILQ_NEXT(elm,linfo_); + + num_later++; + + if (num_later == num_dup_acks_){ //packet is "lost" + return false; + } + } + + if(elm2 == NULL && num_later < num_dup_acks_){ + STAILQ_INSERT_TAIL(&send_recv_hist_, packet, linfo_); + } + + } + } + return true; +} + +/* Prints the contents of the receive history */ +void DCCPTCPlikeAgent::printSendRecvHistory(){ + struct dccp_tcplike_send_recv_hist_entry *elm = STAILQ_FIRST(&send_recv_hist_); + if (elm == NULL) + fprintf(stdout, "Packet history is empty (send recv)\n"); + else { + fprintf(stdout, "Packet history (send recv):\n"); + while(elm != NULL){ + fprintf(stdout,"Packet: seq %d, type %s, ndp %d\n", elm->seq_num_, packetTypeAsStr(elm->type_), elm->ndp_); + elm = STAILQ_NEXT(elm, linfo_); + } + } +} + +/* Detects packet loss in recv history. + * Trims the history to num_dup_acks_+1 length. + * arg: seq_start, seq_end - the lost packet is in [start,end] + * ret: true if a packet loss was detected, otherwise false. + */ +bool DCCPTCPlikeAgent::detectLossSendRecv(u_int32_t *seq_start, u_int32_t *seq_end){ + struct dccp_tcplike_send_recv_hist_entry *before = STAILQ_FIRST(&send_recv_hist_); + int num_later = 1; + bool result = false; + //find the packet before the num_dup_acks_ limit + while (before != NULL && num_later < num_dup_acks_){ + num_later++; + before = STAILQ_NEXT(before, linfo_); + } + + struct dccp_tcplike_send_recv_hist_entry *after = NULL; + //find the packet after the limit + if (before != NULL) + after = STAILQ_NEXT(before, linfo_); + + if (before == NULL || after == NULL) + return false; + + if (before->seq_num_ - after->seq_num_ != 1){ + //we have loss, check if it includes ack packet by comparing ndp values + result = (before->type_ == DCCP_ACK ? ((before->ndp_ - 1 + ndp_limit_) % ndp_limit_ != after->ndp_) : ( before->ndp_ != after->ndp_)); + + if (result){ //we found a loss of an ack packet + *seq_start = before->seq_num_ - 1; + *seq_end = after->seq_num_ + 1; + } + } + + //trim history + if (after != NULL){ + before = after; + after = STAILQ_NEXT(before, linfo_); + while(after != NULL){ + STAILQ_REMOVE(&send_recv_hist_,after,dccp_tcplike_send_recv_hist_entry,linfo_); + delete after; + after = STAILQ_NEXT(before, linfo_); + } + } + return result; +} + +/* Check the constraints on the ack ratio (recv_ack_ratio_) + * ret: true if the ack ratio was changed due to constraints + * false otherwise + */ +bool DCCPTCPlikeAgent::checkAckRatio(){ + bool result = false; + int temp = cwnd_ / 2; + if (cwnd_ % 2 > 0) + temp++; + + //ackratio is never greater than half cwnd (rounded up) + if(recv_ack_ratio_ > temp){ + recv_ack_ratio_ = temp; + result = true; + } + //ack ratio always >= 2 for cwnd >= 4 + if (cwnd_ >= 4 && recv_ack_ratio_ < 2){ + recv_ack_ratio_ = 2; + result = true; + } else if (recv_ack_ratio_ == 0){ //always an integer > 0 + recv_ack_ratio_ = 1; + result = true; + } + + return result; +} + +/* Compares recv_ack_ratio_ with ack_ratio_remote_ and + * tries to change the feauture if they differ. + */ +void DCCPTCPlikeAgent::changeRemoteAckRatio(){ + if (recv_ack_ratio_ != ack_ratio_remote_ && changeFeature(DCCP_FEAT_ACK_RATIO,DCCP_FEAT_LOC_REMOTE)){ + debug("%f, DCCP/TCPlike(%s)::changeRemoteAckRatio() - Changed Remote ack ratio: before %d, after %d\n",now(),name(),ack_ratio_remote_,recv_ack_ratio_); + ack_ratio_remote_ = recv_ack_ratio_; + } +} + +/* Update the ack ratio + * arg: num_ack - number of newly acknowledged packets + */ +void DCCPTCPlikeAgent::updateAckRatio(int num_ack){ + debug("%f, DCCP/TCPlike(%s)::updateAckRatio() - Before: num_ack %d, in_win %d, wins %d, cwnd %d, ack_ratio %d\n", now(), name(), num_ack, num_ack_in_win_, num_win_acked_, (int) cwnd_, recv_ack_ratio_); + if (recv_ack_ratio_ == 1){ //if ack ration is 1, we can't decrease it more + num_ack_in_win_ = 0; + num_win_acked_ = 0; + } else { + num_ack_in_win_ += num_ack; + //only check limit when a whole window has been acked + if (num_ack > 0 && num_ack_in_win_ >= cwnd_){ + num_win_acked_++; + num_ack_in_win_ -= cwnd_; + + double ack_ratio_dec = ((double) cwnd_)/((double) (recv_ack_ratio_*(recv_ack_ratio_ - 1))); + + if (num_win_acked_ > ack_ratio_dec){ + debug("%f, DCCP/TCPlike(%s)::updateAckRatio() - Ack ratio decreased since num_win_acked_ > ack_ratio_dec\n", now(), name()); + recv_ack_ratio_--; + checkAckRatio(); + num_win_acked_ = 0; + } + } + } + debug("%f, DCCP/TCPlike(%s)::updateAckRatio() - After: num_ack %d, in_win %d, wins %d, cwnd %d, ack_ratio %d\n", now(), name(), num_ack, num_ack_in_win_, num_win_acked_, (int) cwnd_, recv_ack_ratio_); +} + +/* Take action on lost or marked ack packet */ +void DCCPTCPlikeAgent::lostOrMarkedAck(){ + ack_win_start_ = seq_num_; + num_win_acked_ = 0; + num_ack_in_win_ = 0; + skip_ack_loss_ = true; + recv_ack_ratio_ *= 2; + checkAckRatio(); +} + +/* Update the congestion window + * arg: num_inc_cwnd - number of newly acknowledged packets + */ +void DCCPTCPlikeAgent::updateCwnd(int num_inc_cwnd){ + debug("%f, DCCP/TCPlike(%s)::updateCwnd() - Before: num_inc_cwnd %d, cwnd_ %d, cwnd_frac_ %d, ssthresh_ %d, ack_ratio_remote_ %d\n", now(),name(), num_inc_cwnd, (int) cwnd_, (int) cwnd_frac_,(int) ssthresh_, ack_ratio_remote_); + int old_cwnd = cwnd_; + + if (cwnd_ < ssthresh_){ //slow start + if (num_inc_cwnd > ack_ratio_remote_){ + cwnd_ += ack_ratio_remote_; + num_inc_cwnd -= ack_ratio_remote_; + } else { + cwnd_ += num_inc_cwnd; + num_inc_cwnd = 0; + } + + if (cwnd_ < ssthresh_) + num_inc_cwnd = 0; + else { + num_inc_cwnd += (cwnd_ - ssthresh_); + cwnd_ = ssthresh_; + } + if (cwnd_ == ssthresh_){ //we passed ssthresh_ + cwnd_frac_ = 0; + //check quiescence and send ack of ack if needed + if (detectQuiescence()){ + //output_ = true; + send_ack_ = true; + ack_num_ = seq_num_recv_; + } + } + } + + if (num_inc_cwnd > 0 && cwnd_ >= ssthresh_){ //congestion avoidance + cwnd_frac_ += num_inc_cwnd; + while (cwnd_frac_ >= cwnd_){ + cwnd_frac_ -= cwnd_; + cwnd_++; + } + + //check quiescence and send ack of ack if needed + if (cwnd_ != old_cwnd && detectQuiescence()){ + //output_ = true; + send_ack_ = true; + ack_num_ = seq_num_recv_; + } + } + + if (old_cwnd < cwnd_) + if (checkAckRatio()) + debug("%f, DCCP/TCPlike(%s)::updateCwnd() - Ack ratio changed! old_cwnd %d cwnd_ %d\n", now(), name(), old_cwnd, (int) cwnd_); + + debug("%f, DCCP/TCPlike(%s)::updateCwnd() - After: num_inc_cwnd %d, cwnd_ %d, cwnd_frac_ %d, ssthresh_ %d, ack_ratio_remote_ %d\n", now(), name(), num_inc_cwnd, (int) cwnd_, (int) cwnd_frac_, (int) ssthresh_, ack_ratio_remote_); +} + +/* Take action on lost or marked data packet */ +void DCCPTCPlikeAgent::lostOrMarkedData(){ + if (cwnd_ >= 2) //i.e . cwnd_ != 1 + cwnd_ = cwnd_ / 2; + cwnd_frac_ = 0; + ssthresh_ = cwnd_; + seq_win_start_ = seq_num_; + debug("%f, DCCP/TCPlike(%s)::lostOrMarkedData() - updated cwnd and ssthresh to: cwnd_ %d, ssthresh_ %d, seq_win_start %d\n", now(), name(), (int) cwnd_, (int) ssthresh_, seq_win_start_); +} + +/* Detects quiescence of the corresponding sender + * ret: true if sender is quiescent + * false otherwise + */ +bool DCCPTCPlikeAgent::detectQuiescence(){ + debug("%f, DCCP/TCPlike(%s)::detectQuiescence() - scheme %d, seq high data %d, last in vect %d, time now %f, time high data %f, srtt %f\n", now(), name(), q_scheme_, q_high_data_recv_, ackv_->getLastSeqNum(),now(),q_t_data_,(double) srtt_); + + if (q_scheme_ == DCCP_Q_SCHEME_Q_OPT){ + if(sender_quiescent_) + debug("%f, DCCP/TCPlike(%s)::detectQuiescence() - Receiver detected that the corresponding sender is quiescent (Q_OPT)\n", now(),name()); + return sender_quiescent_; + } else if (q_scheme_ == DCCP_Q_SCHEME_Q_FEAT){ + if (q_local_) + debug("%f, DCCP/TCPlike(%s)::detectQuiescence() - Receiver detected that the corresponding sender is quiescent (Q_FEAT)\n", now(),name()); + return q_local_; + } + + if (srtt_ <= 0.0) + return false; + + double t = q_min_t_; + if (t < 2*srtt_) + t = 2*srtt_; + if (ackv_->getLastSeqNum() > q_high_data_recv_ && now()-q_t_data_ >= t) + debug("%f, DCCP/TCPlike(%s)::detectQuiescence() - Receiver detected that the corresponding sender is quiescent (NORMAL)\n", now(), name()); + return (ackv_->getLastSeqNum() > q_high_data_recv_ && now()-q_t_data_ >= t); +} + +//protected methods + +/* Methods inherited from DCCPAgent. + * See dccp.h For detailed information regarding arguments etc. + */ + +void DCCPTCPlikeAgent::delay_bind_init_all(){ + delay_bind_init_one("initial_cwnd_"); + delay_bind_init_one("cwnd_timeout_"); + delay_bind_init_one("initial_ssthresh_"); + delay_bind_init_one("cwnd_"); + delay_bind_init_one("cwnd_frac_"); + delay_bind_init_one("ssthresh_"); + delay_bind_init_one("pipe_"); + delay_bind_init_one("initial_rto_"); + delay_bind_init_one("min_rto_"); + delay_bind_init_one("rto_"); + delay_bind_init_one("srtt_"); + delay_bind_init_one("rttvar_"); + delay_bind_init_one("rtt_sample_"); + delay_bind_init_one("alpha_"); + delay_bind_init_one("beta_"); + delay_bind_init_one("k_"); + delay_bind_init_one("g_"); + delay_bind_init_one("num_dup_acks_"); + delay_bind_init_one("q_min_t_"); + delay_bind_init_one("dack_delay_"); + delay_bind_init_one("q_opt_ratio_"); + delay_bind_init_one("ackv_size_lim_"); + DCCPAgent::delay_bind_init_all(); +} + +int DCCPTCPlikeAgent::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer){ + if (delay_bind(varName, localName, "initial_cwnd_", &initial_cwnd_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "cwnd_timeout_", &cwnd_timeout_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "initial_ssthresh_", &initial_ssthresh_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "cwnd_", &cwnd_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "cwnd_frac_", &cwnd_frac_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "ssthresh_", &ssthresh_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "pipe_", &pipe_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "initial_rto_", &initial_rto_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "min_rto_", &min_rto_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "rto_", &rto_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "srtt_", &srtt_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "rttvar_", &rttvar_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "rtt_sample_", &rtt_sample_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "alpha_", &alpha_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "beta_", &beta_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "k_", &k_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "g_", &g_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "num_dup_acks_", &num_dup_acks_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "q_min_t_", &q_min_t_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "dack_delay_", &dack_delay_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "q_opt_ratio_", &q_opt_ratio_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "ackv_size_lim_", &ackv_size_lim_, tracer)) return TCL_OK; + return DCCPAgent::delay_bind_dispatch(varName, localName, tracer); +} + + +void DCCPTCPlikeAgent::reset(){ + DCCPAgent::reset(); + + cwnd_ = initial_cwnd_; + cwnd_frac_ = 0; + ssthresh_ = initial_ssthresh_; + pipe_ = 0; + clearSendHistory(); + clearSendRecvHistory(); + + t_last_data_sent_ = (double) (-q_min_t_)*2; + q_packets_wo_opt_ = 0; + + rto_ = initial_rto_; + srtt_ = -1.0; + rttvar_ = 0.0; + rtt_sample_ = 0.0; + + hist_last_seq_ = 0; + high_ack_recv_ = 0; + + high_seq_recv_ = 0; + high_ndp_recv_ = -1; + unacked_ = 0; + + delete stored_ackv_; + stored_ackv_ = new DCCPAckVector(ackv_size_); + seq_all_done_ = 0; + seq_win_start_ = 0; + seq_pipe_start_ = 0; + num_ack_in_win_ = 0; + num_win_acked_ = 0; + ack_win_start_ = 0; + skip_ack_loss_ = false; + recv_ack_ratio_ = ack_ratio_remote_; + + q_high_data_recv_ = 0; + q_t_data_ = 0.0; + ackv_lim_seq_ = 0; + + sender_quiescent_ = false; + t_high_recv_ = 0.0; +} + +void DCCPTCPlikeAgent::cancelTimers(){ + timer_to_->force_cancel(); + timer_dack_->force_cancel(); + DCCPAgent::cancelTimers(); +} + +/* Process incoming options. + * This function will process: + * ack vectors (verify ECN Nonce Echo) + * quiescence option + */ +bool DCCPTCPlikeAgent::processOption(u_int8_t type, u_char* data, u_int8_t size, Packet *pkt){ + bool result = DCCPAgent::processOption(type, data, size, pkt); + if (result){ + switch(type){ + case DCCP_OPT_ACK_VECTOR_N0: + case DCCP_OPT_ACK_VECTOR_N1: + //check if we received a new ack vector + if (ackv_recv_ != NULL && ackv_recv_->getFirstSeqNum() >= high_ack_recv_){ + //check if we got all needed packets in the history + if(hist_last_seq_ <= ackv_recv_->getLastSeqNum()){ + + u_int8_t ene = 0; + + u_int32_t seqnum; + dccp_packet_state state; + + struct dccp_tcplike_send_hist_entry *elm = STAILQ_FIRST(&send_hist_); + //walk through the ack vector and compute the ECN Nonce Echo + ackv_recv_->startPacketWalk(); + while(ackv_recv_->nextPacket(&seqnum, &state)){ + if (state == DCCP_PACKET_RECV){ + elm = findPacketInSendHistory(seqnum,elm); + if (elm != NULL && elm->seq_num_ == seqnum){ + ene = ene ^ elm->ecn_; + } else if (elm == NULL) + break; + } + } + //compare with the received echo + if (ene != ackv_recv_->getENE()){ + fprintf(stdout,"%f, DCCP/TCPlike(%s)::processOption() - ECN check failed! \n", now(), name()); + sendReset(DCCP_RST_AGG_PEN,0,0,0); + result = false; + } + } else + debug("%f, DCCP/TCPlike(%s)::processOption() - Ack vector includes packets not in history. Skipping ecn check for now...\n", now(), name()); + } else if (ackv_recv_ != NULL) + debug("%f, DCCP/TCPlike(%s)::processOption() - Old ack detected. Skipping ecn check\n", now(), name()); + break; + case DCCP_OPT_QUIESCENCE: + sender_quiescent_ = true; + break; + } + } + return result; +} + + +bool DCCPTCPlikeAgent::send_askPermToSend(int dataSize, Packet *pkt){ + ack_num_ = seq_num_recv_; + bool result = ( pipe_ < cwnd_ || dataSize == 0); + u_int32_t el_time32 = (u_int32_t) ((now() - t_high_recv_)/DCCP_OPT_ELAPSED_TIME_UNIT); + double t; + if (q_scheme_ == DCCP_Q_SCHEME_Q_OPT && !infinite_send_ && dataSize == 0){ + t = q_min_t_; + if (t < 2*srtt_) + t = 2*srtt_; + if (now() - t_last_data_sent_ > t && sb_->empty()){ + q_packets_wo_opt_++; + if (q_packets_wo_opt_ >= q_opt_ratio_){ + opt_->addOption(DCCP_OPT_QUIESCENCE,NULL,0); + q_packets_wo_opt_ = 0; + } + } + + } else if (q_scheme_ == DCCP_Q_SCHEME_Q_FEAT && !infinite_send_){ + if(dataSize > 0 && q_remote_){ + if (changeFeature(DCCP_FEAT_Q,DCCP_FEAT_LOC_REMOTE)) + q_remote_ = 0; + } else if (dataSize == 0) { + t = q_min_t_; + if (t < 2*srtt_) + t = 2*srtt_; + if (now() - t_last_data_sent_ > t && sb_->empty() && !q_remote_){ + if (changeFeature(DCCP_FEAT_Q,DCCP_FEAT_LOC_REMOTE)) + q_remote_ = 1; + + } + } + + } + + //add elapsed time on acknowledgments if needed + if(result && send_ack_ && el_time32 > 0){ + debug("%f, DCCP/TCPlike(%s)::send_askPermToSend() - Ack is delayed by %d (t_high_recv_ %f)\n", now(), name(),el_time32,t_high_recv_); + if (el_time32 > 0 && el_time32 <= 0xFFFF){ + u_int16_t el_time16 = (u_int16_t) el_time32; + opt_->addOption(DCCP_OPT_ELAPSED_TIME,((u_char*) &el_time16), 2); + } else if (el_time32 > 0) + opt_->addOption(DCCP_OPT_ELAPSED_TIME,((u_char*) &el_time32), 4); + } + + return (result); +} + +void DCCPTCPlikeAgent::send_packetSent(Packet *pkt, bool moreToSend, int dataSize){ + struct dccp_tcplike_send_hist_entry *new_packet; + int result; + hdr_dccp *dccph = hdr_dccp::access(pkt); + + switch(dccph->type_){ + case DCCP_DATA: + case DCCP_DATAACK: + if (q_scheme_ == DCCP_Q_SCHEME_Q_OPT) + q_packets_wo_opt_ = 0; + t_last_data_sent_ = now(); + //add data packets to send history + new_packet = new struct dccp_tcplike_send_hist_entry; + new_packet->t_sent_ = now(); + new_packet->seq_num_ = dccph->seq_num_; + result = getNonce(pkt); + new_packet->ecn_ = (result >= 0 ? result : 0); + STAILQ_INSERT_HEAD(&(send_hist_), new_packet, linfo_); + pipe_++; + if (timer_to_->status() != TIMER_PENDING) + timer_to_->resched(rto_); + break; + default: + ; + } +} + +void DCCPTCPlikeAgent::send_packetRecv(Packet *pkt, int dataSize){ + hdr_dccp *dccph = hdr_dccp::access(pkt); + hdr_dccpack *dccpah = hdr_dccpack::access(pkt); + bool lost_ack = false; + struct dccp_tcplike_send_recv_hist_entry *packet; + packet = new struct dccp_tcplike_send_recv_hist_entry; + packet->type_ = dccph->type_; + packet->seq_num_ = dccph->seq_num_; + + packet->ndp_ = dccph->ndp_; + + //insert in the history over received packets + if(!insertInSendRecvHistory(packet)){ + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - Insertion in send recv history failed for seqnum %d\n", now(), name(), packet->seq_num_); + delete packet; + } else { + u_int32_t seq_start; + u_int32_t seq_end; + //detect loss + if(detectLossSendRecv(&seq_start, &seq_end)){ + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - Ack loss detected (send recv) in %d - %d\n" , now(), name(), seq_start, seq_end); + lost_ack = true; + } + } + + switch(dccph->type_){ + case DCCP_DATA: + if (!skip_ack_loss_ && lost_ack) + lostOrMarkedAck(); + break; + case DCCP_ACK: + case DCCP_DATAACK: + skip_ack_loss_ = (dccpah->ack_num_ < ack_win_start_); + + //check if ack is marked + if(!lost_ack && getECNCodePoint(pkt) == ECN_CE){ + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - Ack packet is marked %d\n", now(), name(), dccph->seq_num_); + lost_ack = true; + } + + //check if we got an ack vector (if not, ignore packet) + if (ackv_recv_ != NULL && ackv_recv_->getSize() > 0){ + + struct dccp_tcplike_send_hist_entry *elm = findPacketInSendHistory(dccpah->ack_num_,STAILQ_FIRST(&send_hist_)); + if (elm != NULL && elm->seq_num_ == dccpah->ack_num_) { + //the ack regards a data packet + //update rtt + rtt_sample_ = now() - elm->t_sent_ - elapsed_time_recv_*DCCP_OPT_ELAPSED_TIME_UNIT; + + if (srtt_ < 0){ + srtt_ = rtt_sample_; + rttvar_ = rtt_sample_/2.0; + } else { + rttvar_ = (1 - beta_) * rttvar_ + + beta_ * fabs(srtt_ - rtt_sample_); + srtt_ = (1- alpha_) * srtt_ + alpha_*rtt_sample_; + } + + rto_ = srtt_ + (k_*rttvar_ > g_ ? k_*rttvar_ : g_); + if(rto_ < min_rto_) + rto_ = min_rto_; + + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - RTT measurement updated: elapsed time %d sample %f, rto %f, srtt %f, rttvar %f\n", now(), name(), elapsed_time_recv_, (double) rtt_sample_, (double) rto_, (double) srtt_, (double) rttvar_); + } + + if (dccpah->ack_num_ > high_ack_recv_) + high_ack_recv_ = dccpah->ack_num_; + else if (dccpah->ack_num_ == high_ack_recv_) + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - Duplicate acknowledgment received ack_num_ %d\n", now(), name(), dccpah->ack_num_); + else if (!skip_ack_loss_ && lost_ack){ + lostOrMarkedAck(); + break; + } else + break; + + if (dccpah->ack_num_ < high_ack_recv_){ + fprintf(stderr,"%f, DCCP/TCPlike(%s)::send_packetRecv() - ack_num %d is less than high_ack_recv_ %d!\n", now(), name(), dccpah->ack_num_, high_ack_recv_); + fflush(stdout); + abort(); + } + + //init the stored ack vector if it is empty + if (stored_ackv_->getSize() == 0){ + stored_ackv_->addPacket(ackv_recv_->getLastSeqNum(), DCCP_PACKET_NOT_RECV); + stored_ackv_->addPacket(ackv_recv_->getFirstSeqNum(), DCCP_PACKET_NOT_RECV); + if (seq_all_done_ == 0) //first ack vector recv + seq_all_done_ = ackv_recv_->getLastSeqNum()-1; + else //stored ack vector is zero because of a timeout + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - seq_all_done_ %d is not zero (stored ackv == 0)\n",now(), name(),seq_all_done_); + } + + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - high_ack_recv_ %d, hist_last_seq_ %d, seq_all_done_ %d, seq_win_start_ %d\n",now(),name(),high_ack_recv_, hist_last_seq_, seq_all_done_, seq_win_start_); + + int num_lost[2]; + int num_marked[2]; + int num_ok[2]; + int num_ok_mark[2]; + int num_ack[2]; + int num_pipe[2]; + for (int i = 0; i < 2; i++){ + num_lost[i] = 0; + num_marked[i] = 0; + num_ok[i] = 0; + num_ok_mark[i] = 0; + num_ack[i] = 0; + num_pipe[i] = 0; + } + + int num_packets = 0; + u_int32_t new_seq_all_done = 0; + + bool ackv_recv_empty = false; + u_int32_t first_stored = stored_ackv_->getFirstSeqNum(); + u_int32_t seqnum_old; + u_int32_t seqnum_new; + dccp_packet_state state_old; + dccp_packet_state state_new; + + if (hist_last_seq_ > seq_all_done_){ + fprintf(stderr,"%f, DCCP/TCPlike(%s)::send_packetRecv() - Last packet in history %d is newer than seq_all_done %d\n", now(), name(), hist_last_seq_, seq_all_done_); + fflush(stdout); + abort(); + } + + //process the ack vector information + + ackv_recv_->startPacketWalk(); + stored_ackv_->startPacketWalk(); + + if (!ackv_recv_->nextPacket(&seqnum_new, &state_new)){ + fprintf(stderr,"%f, DCCP/TCPlike(%s)::send_packetRecv() - ackv_recv_ is empty!\n", now(), name()); + fflush(stdout); + abort(); + } + + if (seqnum_new > first_stored){ + state_old = DCCP_PACKET_NOT_RECV; + seqnum_old = seqnum_new; + } else if (!(stored_ackv_->nextPacket(&seqnum_old, &state_old)) || + seqnum_old != seqnum_new){ + fprintf(stderr,"%f, DCCP/TCPlike(%s)::send_packetRecv() - Stored ackv is empty or seqnum_old != seqnum_new!\n", now(), name()); + fflush(stdout); + abort(); + } + + do { + + if (seqnum_new <= seq_all_done_) + break; + + if (new_seq_all_done == 0 && num_packets == num_dup_acks_) + new_seq_all_done = seqnum_new; + + elm = findPacketInSendHistory(seqnum_new,elm); + state_new = stored_ackv_->ackv_cons_[state_old][state_new]; + + if ((elm != NULL) && (elm->seq_num_ == seqnum_new)){ //if data packet + + if (state_new != state_old){ + if (state_old == DCCP_PACKET_NOT_RECV && + state_new == DCCP_PACKET_RECV){ + num_ok[(int) (seqnum_new >= seq_win_start_)]++; + num_ack[(int) (seqnum_new >= ack_win_start_)]++; + num_pipe[(int) (seqnum_new >= seq_pipe_start_)]++; + } else if (state_old == DCCP_PACKET_NOT_RECV && + state_new == DCCP_PACKET_ECN){ + num_marked[(int) (seqnum_new >= seq_win_start_)]++; + num_ack[(int) (seqnum_new >= ack_win_start_)]++; + num_pipe[(int) (seqnum_new >= seq_pipe_start_)]++; + } else if (state_old == DCCP_PACKET_RECV && + state_new == DCCP_PACKET_ECN) { + num_marked[(int) (seqnum_new >= seq_win_start_)]++; + num_ok_mark[(int) (seqnum_new >= seq_win_start_)]++; + } + + } else if (state_new == DCCP_PACKET_NOT_RECV){ + if (num_packets >= num_dup_acks_){ + num_lost[(int) (seqnum_new >= seq_win_start_)]++; + num_ack[(int) (seqnum_new >= ack_win_start_)]++; + num_pipe[(int) (seqnum_new >= seq_pipe_start_)]++; + } + } + } + + if (state_new != DCCP_PACKET_NOT_RECV) + num_packets++; + + if (!ackv_recv_->nextPacket(&seqnum_new, &state_new)){ + seqnum_new--; + ackv_recv_empty = true; + } + + if (seqnum_new == seq_all_done_) + break; + else if (seqnum_new > first_stored){ + + state_old = DCCP_PACKET_NOT_RECV; + seqnum_old = seqnum_new; + } else if (!(stored_ackv_->nextPacket(&seqnum_old, &state_old))){ + fprintf(stderr,"%f, DCCP/TCPlike(%s)::send_packetRecv() - Stored ack vector is empty when it shouldn't be!\n", now(), name()); + fflush(stdout); + abort(); + } + + if (ackv_recv_empty){ + state_new = state_old; + } + + } while (true); + + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - in window: ok %d, marked %d, lost %d, ok_mark %d\n",now(), name(), num_ok[1],num_marked[1],num_lost[1],num_ok_mark[1]); + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - after window: ok %d, marked %d, lost %d, ok_mark %d\n",now(), name(), num_ok[0],num_marked[0],num_lost[0],num_ok_mark[0]); + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - new_seq_all_done %d, seq_all_done_ %d\n",now(), name(), new_seq_all_done,seq_all_done_); + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - (ack) in window: %d, not in window %d\n",now(), name(), num_ack[1],num_ack[0]); + + if (new_seq_all_done > seq_all_done_) + seq_all_done_ = new_seq_all_done; + + stored_ackv_->mergeWith(ackv_recv_); + stored_ackv_->removePackets(seq_all_done_); + + + //trim history to seq = min(last in recv ackvector, seq_all_done); + u_int32_t trim_to = ackv_recv_->getLastSeqNum(); + if (seq_all_done_ < trim_to) + trim_to = seq_all_done_; + + trimSendHistory(trim_to); + + int num_inc_cwnd = + num_ok[DCCP_TCPLIKE_IN_WINDOW] + num_marked[DCCP_TCPLIKE_IN_WINDOW] - + num_ok_mark[DCCP_TCPLIKE_IN_WINDOW] + + num_ok[DCCP_TCPLIKE_AFTER_WINDOW] + num_marked[DCCP_TCPLIKE_AFTER_WINDOW] - + num_ok_mark[DCCP_TCPLIKE_AFTER_WINDOW]; + + + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - Update pipe (before): pipe_ %d\n", now(), name(), (int) pipe_); + pipe_ = pipe_ - num_pipe[DCCP_TCPLIKE_IN_WINDOW]; + + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - Update pipe (after): pipe_ %d\n", now(), name(), (int) pipe_); + + if (pipe_ < 0) { + fprintf(stderr,"%f, DCCP/TCPlike(%s)::send_packetRecv() - Pipe is less than zero! (%i)\n", now(), name(), (int) pipe_); + fflush(stdout); + abort(); + } else if (pipe_ == 0) { + timer_to_->force_cancel(); + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - Pipe is zero, cancels timer\n", now(), name()); + } else if (num_inc_cwnd > 0){ + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - Newly acked packets, rescheduling timer. rto %f\n", now(), name(),(double) rto_); + timer_to_->resched(rto_); + } + + updateCwnd(num_inc_cwnd); + + if (num_lost[DCCP_TCPLIKE_IN_WINDOW] > 0 || + num_marked[DCCP_TCPLIKE_IN_WINDOW] > 0){ + lostOrMarkedData(); + num_ack_in_win_ = 0; + if (checkAckRatio()){ + debug("%f, DCCP/TCPlike(%s)::send_packetRecv() - Ackratio changed because of data loss\n", now(), name()); + num_win_acked_ = 0; + ack_win_start_ = seq_num_; + num_ack[DCCP_TCPLIKE_IN_WINDOW] = 0; + } + + if (detectQuiescence()){ + //output_ = true; + send_ack_ = true; + ack_num_ = seq_num_recv_; + } + } + + if (!skip_ack_loss_ && lost_ack){ + lostOrMarkedAck(); + } else { + updateAckRatio(num_ack[DCCP_TCPLIKE_IN_WINDOW]); + } + + if (pipe_ < cwnd_) + output_ = true; + + //check if ackv_size_lim_ is reached + if (ackv_size_lim_ > 0 + && ackv_recv_->getSize() > ackv_size_lim_ + && ackv_lim_seq_ < dccpah->ack_num_ + && detectQuiescence()){ + debug("%f, DCCP/TCPlike(%s)::send_packet_recv() - Ack vector size limit reached during quiescence. Sending ack of ack. (lim %u, size %u, lim_seq %u, seq_num_ %u\n", now(),name(),ackv_size_lim_,ackv_recv_->getSize(),ackv_lim_seq_,seq_num_); + send_ack_ = true; + ack_num_ = seq_num_recv_; + ackv_lim_seq_ = seq_num_; + } + + + } else { + fprintf(stdout,"%f, DCCP/TCPlike(%s)::send_packetRecv() - No ack vector on acknowledgment!\n", now(), name()); + if (lost_ack) + lostOrMarkedAck(); + } + break; + default: + ; + } + + changeRemoteAckRatio(); + +} + +void DCCPTCPlikeAgent::recv_packetRecv(Packet *pkt, int dataSize){ + hdr_dccp *dccph = hdr_dccp::access(pkt); + + if (q_t_data_ == 0.0) + q_t_data_ = now(); + + if(dccph->type_ == DCCP_DATA || dccph->type_ == DCCP_DATAACK){ + //start dack timer if idle + if (timer_dack_->status() == TIMER_IDLE){ + //printf("TIMER is IDLE\n"); + timer_dack_->sched(dack_delay_); + } + unacked_++; + q_t_data_ = now(); + if (dccph->seq_num_ > q_high_data_recv_) + q_high_data_recv_ = dccph->seq_num_; + sender_quiescent_ = false; + } else if (high_ndp_recv_ >= 0 && (int) (dccph->seq_num_ - high_seq_recv_) > 1){ + //some packet is missing + if ( (int) (dccph->seq_num_ - high_seq_recv_) > + ((dccph->ndp_ - high_ndp_recv_ + ndp_limit_) % ndp_limit_) + ){ //data packet is "lost" + debug("%f, DCCP/TCPlike(%s)::recv_packetRecv() - Data loss detected by receiver seq_num %d - ndp %d, high_seq_recv %d - ndp %d\n",now(),name(), dccph->seq_num_, dccph->ndp_,high_seq_recv_, high_ndp_recv_); + q_t_data_ = now(); + if (dccph->seq_num_-1 > q_high_data_recv_) + q_high_data_recv_ = dccph->seq_num_-1; + sender_quiescent_ = false; + } + } + + if (dccph->seq_num_ > high_seq_recv_){ + high_seq_recv_ = dccph->seq_num_; + high_ndp_recv_ = dccph->ndp_; + t_high_recv_ = now(); + } + + if (unacked_ >= ack_ratio_local_){ + timer_dack_->force_cancel(); + send_ack_ = true; + ack_num_ = seq_num_recv_; + unacked_= 0; + output_ = true; + output_flag_ = true; + } + +} + +/* Variable tracing */ + +void DCCPTCPlikeAgent::traceAll() { + char wrk[500]; + + sprintf(wrk,"%f %d %d %d %d %f %f %f %f\n", + now(), (int) cwnd_, (int) cwnd_frac_, (int) ssthresh_, + (int) pipe_, (double) rto_, (double) srtt_, (double) rttvar_, + (double) rtt_sample_); + + if (channel_) + Tcl_Write(channel_, wrk, strlen(wrk)); +} + + +void DCCPTCPlikeAgent::traceVar(TracedVar* v) +{ + char wrk[500]; + + if (!strcmp(v->name(), "cwnd_") || + !strcmp(v->name(), "cwnd_frac_") || + !strcmp(v->name(), "ssthresh_") || + !strcmp(v->name(), "pipe_")){ + sprintf(wrk,"%f %d %s\n", now(), int(*((TracedInt*) v)), v->name()); + if (channel_) + Tcl_Write(channel_, wrk, strlen(wrk)); + } else if (!strcmp(v->name(), "rto_") || + !strcmp(v->name(), "srtt_") || + !strcmp(v->name(), "rttvar_") || + !strcmp(v->name(), "rtt_sample_")){ + sprintf(wrk,"%f %f %s\n", now(), double(*((TracedDouble*) v)), v->name()); + if (channel_) + Tcl_Write(channel_, wrk, strlen(wrk)); + } else + DCCPAgent::traceVar(v); +} + +//public methods + +/* Constructor + * ret: a new DCCPTCPlikeAgent + */ +DCCPTCPlikeAgent::DCCPTCPlikeAgent() : DCCPAgent() { + ccid_ = DCCP_TCPLIKE_CCID; + + initial_cwnd_ = DCCP_TCPLIKE_INIT_CWND; + initial_ssthresh_ = DCCP_TCPLIKE_INIT_SSTHRESH; + initial_rto_ = DCCP_TCPLIKE_INIT_RTO; + cwnd_timeout_ = DCCP_TCPLIKE_CWND_TO; + + cwnd_ = initial_cwnd_; + cwnd_frac_ = 0; + ssthresh_ = initial_ssthresh_; + pipe_ = 0; + + t_last_data_sent_ = (double) (-DCCP_TCPLIKE_MIN_T)*2; + q_opt_ratio_ = DCCP_TCPLIKE_QUIESCENCE_OPT_RATIO; + q_packets_wo_opt_ = 0; + + rto_ = initial_rto_; + srtt_ = -1.0; + rttvar_ = 0.0; + rtt_sample_ = 0.0; + k_ = DCCP_TCPLIKE_K; + g_ = DCCP_TCPLIKE_G; + alpha_ = DCCP_TCPLIKE_ALPHA; + beta_ = DCCP_TCPLIKE_BETA; + + min_rto_ = DCCP_TCPLIKE_MIN_RTO; + + num_dup_acks_ = DCCP_NUM_DUP_ACKS; + + STAILQ_INIT(&send_hist_); + STAILQ_INIT(&send_recv_hist_); + + timer_to_ = new DCCPTCPlikeTOTimer(this); + timer_dack_ = new DCCPTCPlikeDelayedAckTimer(this); + + hist_last_seq_ = 0; + high_ack_recv_ = 0; + + high_seq_recv_ = 0; + high_ndp_recv_ = -1; + unacked_ = 0; + + stored_ackv_ = new DCCPAckVector(ackv_size_); + stored_ackv_->ackv_cons_[DCCP_PACKET_RECV][DCCP_PACKET_RECV] = DCCP_PACKET_RECV; + stored_ackv_->ackv_cons_[DCCP_PACKET_RECV][DCCP_PACKET_ECN] = DCCP_PACKET_ECN; + stored_ackv_->ackv_cons_[DCCP_PACKET_RECV][DCCP_PACKET_RESERVED] = DCCP_PACKET_RESERVED; + stored_ackv_->ackv_cons_[DCCP_PACKET_RECV][DCCP_PACKET_NOT_RECV] = DCCP_PACKET_RECV; + stored_ackv_->ackv_cons_[DCCP_PACKET_ECN][DCCP_PACKET_RECV] = DCCP_PACKET_ECN; + stored_ackv_->ackv_cons_[DCCP_PACKET_ECN][DCCP_PACKET_ECN] = DCCP_PACKET_ECN; + stored_ackv_->ackv_cons_[DCCP_PACKET_ECN][DCCP_PACKET_RESERVED] = DCCP_PACKET_RESERVED; + stored_ackv_->ackv_cons_[DCCP_PACKET_ECN][DCCP_PACKET_NOT_RECV] = DCCP_PACKET_ECN; + stored_ackv_->ackv_cons_[DCCP_PACKET_RESERVED][DCCP_PACKET_RECV] = DCCP_PACKET_RESERVED; + stored_ackv_->ackv_cons_[DCCP_PACKET_RESERVED][DCCP_PACKET_ECN] = DCCP_PACKET_RESERVED; + stored_ackv_->ackv_cons_[DCCP_PACKET_RESERVED][DCCP_PACKET_RESERVED] = DCCP_PACKET_RESERVED; + stored_ackv_->ackv_cons_[DCCP_PACKET_RESERVED][DCCP_PACKET_NOT_RECV] = DCCP_PACKET_RESERVED; + stored_ackv_->ackv_cons_[DCCP_PACKET_NOT_RECV][DCCP_PACKET_RECV] = DCCP_PACKET_RECV; + stored_ackv_->ackv_cons_[DCCP_PACKET_NOT_RECV][DCCP_PACKET_ECN] = DCCP_PACKET_ECN; + stored_ackv_->ackv_cons_[DCCP_PACKET_NOT_RECV][DCCP_PACKET_RESERVED] = DCCP_PACKET_RESERVED; + stored_ackv_->ackv_cons_[DCCP_PACKET_NOT_RECV][DCCP_PACKET_NOT_RECV] = DCCP_PACKET_NOT_RECV; + seq_all_done_ = 0; + seq_win_start_ = 0; + seq_pipe_start_ = 0; + + num_win_acked_ = 0; + num_ack_in_win_ = 0; + ack_win_start_ = 0; + skip_ack_loss_ = false; + recv_ack_ratio_ = ack_ratio_remote_; + + q_high_data_recv_ = 0; + q_t_data_ = 0.0; + q_min_t_ = DCCP_TCPLIKE_MIN_T; + + ackv_size_lim_ = DCCP_TCPLIKE_ACKV_SIZE_LIM; + ackv_lim_seq_ = 0; + + sender_quiescent_ = false; + + dack_delay_ = DCCP_TCPLIKE_DACK_DELAY; + t_high_recv_ = 0.0; + + //test conversion from bool to int + if (!((int) (cwnd_ >= cwnd_frac_) == 0 || (int) (cwnd_ >= cwnd_frac_) == 1)){ + fprintf(stderr,"DCCP/TCPlike::DCCPTCPlikeAgent() - The conversion between bool and int is not as expected ((int) (%d >= %d) = %d)!\n", (int) cwnd_, (int) cwnd_frac_, (int) (cwnd_ >= cwnd_frac_)); + fflush(stdout); + abort(); + } else if (!((int) (cwnd_ < cwnd_frac_) == 0 || (int) (cwnd_ < cwnd_frac_) == 1)){ + fprintf(stderr,"DCCP/TCPlike::DCCPTCPlikeAgent() - The conversion between bool and int is not as expected ((int) (%d < %d) = %d)!\n", (int) cwnd_, (int) cwnd_frac_, (int) (cwnd_ < cwnd_frac_)); + fflush(stdout); + abort(); + } + +} + +/* Destructor */ +DCCPTCPlikeAgent::~DCCPTCPlikeAgent(){ + clearSendHistory(); + clearSendRecvHistory(); + delete stored_ackv_; + delete timer_to_; + delete timer_dack_; +} + +int DCCPTCPlikeAgent::command(int argc, const char*const* argv){ + return DCCPAgent::command(argc,argv); +} + +/* A timeout has occured + * arg: tno - id of timeout event + * Handles: Delayed acknowledgement timer - DCCP_TCPLIKE_TIMER_DACK + * Timeout timer - DCCP_TCPLIKE_TIMER_TO + */ +void DCCPTCPlikeAgent::timeout(int tno){ + switch (tno){ + case DCCP_TCPLIKE_TIMER_TO: + debug("%f, DCCP/TCPlike(%s)::timeout() - Timeout\n", now(), name()); + ssthresh_ = cwnd_ / 2; + if (ssthresh_ == 0) + ssthresh_ = 1; + + cwnd_ = cwnd_timeout_; + pipe_ = 0; + + seq_pipe_start_ = seq_num_; + + seq_win_start_ = seq_num_; + cwnd_frac_ = 0; + + num_ack_in_win_ = 0; + num_win_acked_ = 0; + ack_win_start_ = seq_num_; + recv_ack_ratio_ = 1; + checkAckRatio(); + changeRemoteAckRatio(); + rto_ = rto_ * 2; + output(); + break; + case DCCP_TCPLIKE_TIMER_DACK: + debug("%f, DCCP/TCPlike(%s)::timeout() - Delayed ack timer expired\n", now(), name()); + send_ack_ = true; + ack_num_ = seq_num_recv_; + unacked_ = 0; + output(true); + break; + default: + DCCPAgent::timeout(tno); + } +} + + + + + + + + + + + diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp_tcplike.h ns-2.34_dccp/dccp/dccp_tcplike.h --- ns-2.34_orig/dccp/dccp_tcplike.h 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp_tcplike.h 2010-03-08 15:09:55.797597400 +0100 @@ -0,0 +1,347 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp_tcplike.h,v 1.32 2004/02/23 18:05:31 nilmat-8 Exp $ */ + +#ifndef ns_dccp_tcplike_h +#define ns_dccp_tcplike_h + +#include "dccp.h" +#include "bsd_queue.h" + +// Use tracevars for window and rtt variables +#define DCCP_TCPLIKE_USE_TRACED_VARS + +#define DCCP_TCPLIKE_CCID 2 + +//initial values +#define DCCP_TCPLIKE_INIT_CWND 3 //rfc2581 says 2 but rfc3390 allows more +#define DCCP_TCPLIKE_CWND_TO 1 //cwnd is set to this value on timeout +#define DCCP_TCPLIKE_INIT_SSTHRESH 0xFFFF + +//RTO parameters - rfc 2988 +#define DCCP_TCPLIKE_INIT_RTO 3 +#define DCCP_TCPLIKE_ALPHA 0.125 +#define DCCP_TCPLIKE_BETA 0.25 +#define DCCP_TCPLIKE_K 4 +#define DCCP_TCPLIKE_G 0.010 +#define DCCP_TCPLIKE_MIN_RTO 1.0 + +//Timers +#define DCCP_TCPLIKE_TIMER_TO 128 +#define DCCP_TCPLIKE_TIMER_DACK 129 + +#define DCCP_TCPLIKE_DACK_DELAY 0.2 //delay for delayed acknowledgments +#define DCCP_TCPLIKE_MIN_T 0.2 //T value for quiescence detection + +//Quiescence +#define DCCP_TCPLIKE_QUIESCENCE_OPT_RATIO 1 +#define DCCP_TCPLIKE_ACKV_SIZE_LIM 10 //ack an ack when recv ackv > this + +//forward decleration of class DCCPTCPlikeAgent +class DCCPTCPlikeAgent; + +//The DCCPTCPlikeTOTimer is similar to TCP's retransmission timeouts +class DCCPTCPlikeTOTimer : public TimerHandler { +protected: + DCCPTCPlikeAgent *agent_; //the owning agent +public: + /* Constructor + * arg: agent - the owning agent (to notify about timeout) + * ret: A new DCCPTCPlikeTOTimer + */ + DCCPTCPlikeTOTimer(DCCPTCPlikeAgent* agent); + + /* Called when the timer has expired + * arg: e - The event that happened (i.e. the timer expired) + */ + virtual void expire(Event *e); +}; + +/* The DCCPTCPlikeDelayedAckTimer expires when a data packet has not been + * acknowledged within dack_delay_ s of its arrival */ +class DCCPTCPlikeDelayedAckTimer : public TimerHandler { +protected: + DCCPTCPlikeAgent *agent_; //the owning agent +public: + /* Constructor + * arg: agent - the owning agent (to notify about timeout) + * ret: A new DCCPTCPlikeDelayedAckTimer + */ + DCCPTCPlikeDelayedAckTimer(DCCPTCPlikeAgent* agent); + + /* Called when the timer has expired + * arg: e - The event that happened (i.e. the timer expired) + */ + virtual void expire(Event *e); +}; + +//packet history of sent data packets +STAILQ_HEAD(dccp_tcplike_send_hist,dccp_tcplike_send_hist_entry); + + +struct dccp_tcplike_send_hist_entry { + STAILQ_ENTRY(dccp_tcplike_send_hist_entry) linfo_; + u_int32_t seq_num_; //sequence number + double t_sent_; //timestamp of sent packet + u_int8_t ecn_; //ecn nonce (0 or 1) sent +}; + +//packet history over received packets +STAILQ_HEAD(dccp_tcplike_send_recv_hist,dccp_tcplike_send_recv_hist_entry); + +struct dccp_tcplike_send_recv_hist_entry { + STAILQ_ENTRY(dccp_tcplike_send_recv_hist_entry) linfo_; + u_int32_t seq_num_; //sequence number + dccp_packet_type type_; //packet type + u_int8_t ndp_; //ndp value +}; + +/* The class DCCPTCPlikeAgent implements a two way DCCP agent using + * TCP-like (CCID 2) congestion control mechanism. + * If changes are made to tcl variables, the agent should be reset + * to enable all changes. + * Conforms to the October 2003 drafts. + */ +class DCCPTCPlikeAgent : public DCCPAgent { +private: + //sender + + int initial_cwnd_; //initial congestion window + int cwnd_timeout_; //initial value for the cwnd after a timeout + int initial_ssthresh_; //initial value for slow start threshold + double initial_rto_; //initial rto value + +#ifdef DCCP_TCPLIKE_USE_TRACED_VARS + TracedInt cwnd_; //congestion window + TracedInt cwnd_frac_; //fractional part of cwnd (in unit of 1/cwnd) + TracedInt ssthresh_; //slow start threshold + TracedInt pipe_; //outstanding packets in the network + TracedDouble rto_; //"retransmission" timeout value + TracedDouble srtt_; //smoothed rtt + TracedDouble rttvar_; //rtt variance + TracedDouble rtt_sample_; //latest rtt sample +#else + int cwnd_; + int cwnd_frac_; + int ssthresh_; + int pipe_; + double rto_; + double srtt_; + double rttvar_; + double rtt_sample_; +#endif + double alpha_; //smoothing factor for rtt/rto calculations + double beta_; //smoothing factor for rtt/rto calculations + double g_; //timer granularity + int k_; //k value + + double min_rto_; //minimum rto allowed + + /* The number of packets with higher sequence number needed before + * a delayed packet is considered lost (similiar to NUMDUPACKS) */ + int num_dup_acks_; + + //congestion control on data packets related attributes + + struct dccp_tcplike_send_hist send_hist_; //history over sent packets + u_int32_t hist_last_seq_; //last sequence number in history + + DCCPAckVector *stored_ackv_; //state information about recv packets + + u_int32_t high_ack_recv_; //highest ack received so far + u_int32_t seq_all_done_; //seq nums <= this number have been processed + + /* The sequence number that started the new window after the + * last congestion event */ + u_int32_t seq_win_start_; + + /* Decrease pipe for all packets acknowledged after and including + * this sequence number */ + u_int32_t seq_pipe_start_; + + //ack ratio related attributes + int num_ack_in_win_; //number of packets acknowledged + int num_win_acked_; //number of cwnd acked without lost/marked acks + /* The sequence number that started the new window after the + * last congestion event regarding acks */ + u_int32_t ack_win_start_; + + bool skip_ack_loss_; //if true, ack loss is ignored + u_int16_t recv_ack_ratio_;//the ackratio to report to the receiver + + //history over received packets + struct dccp_tcplike_send_recv_hist send_recv_hist_; + + DCCPTCPlikeTOTimer *timer_to_; //timeout timer + + //quiescence + double t_last_data_sent_; //timestamp of when last data pkt was sent + int q_opt_ratio_; //add Q opt on each opt ratio acks sent + int q_packets_wo_opt_; //ack packets sent without Q opt + + int ackv_size_lim_; //send an ack of ack if recv ackv >= this + u_int32_t ackv_lim_seq_; //sequencenumber of last ack of ack sent due to size limit + + /* Clear the send history */ + void clearSendHistory(); + + /* Find a specific packet in the send history + * arg: seq_num - sequence number of packet to find + * start_elm - start searching from this element + * ret: pointer to the correct element if the element is found + * otherwise the closest packet with lower seq num is returned + * which can be NULL. + */ + struct dccp_tcplike_send_hist_entry* findPacketInSendHistory(u_int32_t seq_num, dccp_tcplike_send_hist_entry* start_elm); + + /* Remove packets from send history + * arg: trim_to - remove packets with lower sequence numbers than this + * Note: if trim_to does not exist in the history, trim_to is set + * to the next lower sequence number found before trimming. + */ + void trimSendHistory(u_int32_t trim_to); + + /* Prints the contents of the send history */ + void printSendHistory(); + + /* Clear the history of received packets */ + void clearSendRecvHistory(); + + /* Insert a packet in the receive history + * Will not insert "lost" packets or duplicates. + * arg: packet - entry representing the packet to insert + * ret: true if packet was inserted, otherwise false. + */ + bool insertInSendRecvHistory(dccp_tcplike_send_recv_hist_entry *packet); + /* Prints the contents of the receive history */ + void printSendRecvHistory(); + + /* Detects packet loss in recv history. + * Trims the history to num_dup_acks_+1 length. + * arg: seq_start, seq_end - the lost packet is in [start,end] + * ret: true if a packet loss was detected, otherwise false. + */ + bool detectLossSendRecv(u_int32_t *seq_start, u_int32_t *seq_end); + + /* Check the constraints on the ack ratio (recv_ack_ratio_) + * ret: true if the ack ratio was changed due to constraints + * false otherwise + */ + bool checkAckRatio(); + + /* Compares recv_ack_ratio_ with ack_ratio_remote_ and + * tries to change the feauture if they differ. + */ + void changeRemoteAckRatio(); + + /* Update the ack ratio + * arg: num_ack - number of newly acknowledged packets + */ + void updateAckRatio(int num_ack); + + /* Take action on lost or marked ack packet */ + void lostOrMarkedAck(); + + /* Update the congestion window + * arg: num_inc_cwnd - number of newly acknowledged packets + */ + void updateCwnd(int num_inc_cwnd); + + /* Take action on lost or marked data packet */ + void lostOrMarkedData(); + + //receiver + + u_int32_t high_seq_recv_; //highest sequence number received + int high_ndp_recv_; //ndp value of high_seq_recv_ + double t_high_recv_; //timestamp of high_seq_recv_ + int unacked_; //number of unacknowledged data packets + + //quiescence + u_int32_t q_high_data_recv_; //highest data packet received + double q_min_t_; //T value for quiescence detection + double q_t_data_; //timestamp of last data pkt observed + + bool sender_quiescent_; //the sender is quiescent (q opt recv) + + double dack_delay_; //delay for delayed acknowledgments + DCCPTCPlikeDelayedAckTimer *timer_dack_; //delayed acknowledgments timer + /* Detects quiescence of the corresponding sender + * ret: true if sender is quiescent + * false otherwise + */ + bool detectQuiescence(); +protected: + + /* Methods inherited from DCCPAgent. + * See dccp.h For detailed information regarding arguments etc. + */ + + virtual void delay_bind_init_all(); + virtual int delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer); + + virtual void reset(); + virtual void cancelTimers(); + + /* Process incoming options. + * This function will process: + * ack vectors (verify ECN Nonce Echo) + * quiescence option + */ + virtual bool processOption(u_int8_t type, u_char* data, u_int8_t size, Packet *pkt); + + virtual bool send_askPermToSend(int dataSize, Packet *pkt); + virtual void send_packetSent(Packet *pkt, bool moreToSend, int dataSize); + virtual void send_packetRecv(Packet *pkt, int dataSize); + virtual void recv_packetRecv(Packet *pkt, int dataSize); + + virtual void traceAll(); + virtual void traceVar(TracedVar* v); +public: + /* Constructor + * ret: a new DCCPTCPlikeAgent + */ + DCCPTCPlikeAgent(); + + /* Destructor */ + virtual ~DCCPTCPlikeAgent(); + + int command(int argc, const char*const* argv); + + /* A timeout has occured + * arg: tno - id of timeout event + * Handles: Delayed acknowledgement timer - DCCP_TCPLIKE_TIMER_DACK + * Timeout timer - DCCP_TCPLIKE_TIMER_TO + */ + virtual void timeout(int tno); + +}; + +#endif + diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp_tfrc.cc ns-2.34_dccp/dccp/dccp_tfrc.cc --- ns-2.34_orig/dccp/dccp_tfrc.cc 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp_tfrc.cc 2010-03-08 15:09:55.787595291 +0100 @@ -0,0 +1,2194 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp_tfrc.cc,v 1.35 2004/03/24 14:06:38 nilmat-8 Exp $ */ + +#include "ip.h" +#include "dccp_tfrc.h" +#include "flags.h" + + +//OTcl linkage for DCCPTFRC agent +static class DCCPTFRCClass : public TclClass { +public: + DCCPTFRCClass() : TclClass("Agent/DCCP/TFRC") {}; + TclObject* create(int argc, const char*const* argv){ + return (new DCCPTFRCAgent()); + } +} class_dccptfrc; + +//methods for timer classes + +/* Constructor + * arg: agent - the owning agent (to notify about timeout) + * ret: A new DCCPTFRCSendTimer + */ +DCCPTFRCSendTimer::DCCPTFRCSendTimer(DCCPTFRCAgent* agent) : TimerHandler(){ + agent_ = agent; +} + +/* Called when the timer has expired + * arg: e - The event that happened (i.e. the timer expired) + */ +void DCCPTFRCSendTimer::expire(Event *e){ + agent_->timeout(DCCP_TFRC_TIMER_SEND); +} + +/* Constructor + * arg: agent - the owning agent (to notify about timeout) + * ret: A new DCCPTFRCNoFeedbackTimer + */ +DCCPTFRCNoFeedbackTimer::DCCPTFRCNoFeedbackTimer(DCCPTFRCAgent* agent) : TimerHandler(){ + agent_ = agent; +} + +/* Called when the timer has expired + * arg: e - The event that happened (i.e. the timer expired) + */ +void DCCPTFRCNoFeedbackTimer::expire(Event *e){ + agent_->timeout(DCCP_TFRC_TIMER_NO_FEEDBACK); +} + +//private methods + +/* Clear the send history */ +inline void DCCPTFRCAgent::clearSendHistory(){ + struct s_hist_entry *elm, *elm2; + + /* Empty packet history */ + elm = STAILQ_FIRST(&s_hist_); + while (elm != NULL) { + elm2 = STAILQ_NEXT(elm, linfo_); + delete elm; + elm = elm2; + } + + STAILQ_INIT(&s_hist_); +} + +/* Find a specific packet in the send history + * arg: seq_num - sequence number of packet to find + * start_elm - start searching from this element + * ret: pointer to the correct element if the element is found + * otherwise the closest packet with lower seq num is returned + * which can be NULL. + */ +inline struct s_hist_entry* DCCPTFRCAgent::findPacketInSendHistory(u_int32_t seq_num, s_hist_entry* start_elm){ + struct s_hist_entry *elm = start_elm; + while (elm != NULL && elm->seq_num_ > seq_num) + elm = STAILQ_NEXT(elm, linfo_); + return elm; +} + +/* Remove packets from send history + * arg: trim_to - remove packets with lower sequence numbers than this + * Note: if trim_to does not exist in the history, trim_to is set to the next + * lower sequence number found before trimming. + */ +void DCCPTFRCAgent::trimSendHistory(u_int32_t trim_to){ + if (trim_to > s_hist_last_seq_){ + struct s_hist_entry *elm, *elm2; + //find packet corresponding to trim_to + elm = findPacketInSendHistory(trim_to, STAILQ_FIRST(&s_hist_)); + if (elm != NULL){ + //remove older packets + elm2 = STAILQ_NEXT(elm, linfo_); + while (elm2 != NULL){ + STAILQ_REMOVE(&s_hist_,elm2,s_hist_entry,linfo_); + delete elm2; + elm2 = STAILQ_NEXT(elm, linfo_); + } + } + s_hist_last_seq_ = trim_to; + } +} + +/* Prints the contents of the send history */ +void DCCPTFRCAgent::printSendHistory(){ + struct s_hist_entry *elm = STAILQ_FIRST(&s_hist_); + if (elm == NULL) + fprintf(stdout, "Packet history is empty (send)\n"); + else { + fprintf(stdout, "Packet history (send):\n"); + while(elm != NULL){ + fprintf(stdout,"Packet: seq %d, t_sent %f, ecn %d, wincount %u\n", elm->seq_num_, elm->t_sent_, elm->ecn_, elm->win_count_); + elm = STAILQ_NEXT(elm, linfo_); + } + } +} + +/* Clear the history of received packets */ +inline void DCCPTFRCAgent::clearRecvHistory(){ + struct r_hist_entry *elm, *elm2; + + /* Empty packet history */ + elm = STAILQ_FIRST(&r_hist_); + while (elm != NULL) { + elm2 = STAILQ_NEXT(elm, linfo_); + delete elm; + elm = elm2; + } + + STAILQ_INIT(&r_hist_); +} + +/* Insert a packet in the receive history + * Will not insert "lost" packets or duplicates. + * arg: packet - entry representing the packet to insert + * ret: true if packet was inserted, otherwise false. + */ +bool DCCPTFRCAgent::insertInRecvHistory(r_hist_entry *packet){ + struct r_hist_entry *elm, *elm2; + int num_later = 0; + fflush(stdout); + if (STAILQ_EMPTY(&r_hist_)){ //history is empty + STAILQ_INSERT_HEAD(&r_hist_, packet, linfo_); + if (packet->type_ != DCCP_ACK) + r_last_data_pkt_ = packet; + } else { //history contains at least one entry + elm = STAILQ_FIRST(&r_hist_); + if (packet->seq_num_ > elm->seq_num_){ //insert first in history + STAILQ_INSERT_HEAD(&r_hist_, packet, linfo_); + if (packet->type_ != DCCP_ACK) + r_last_data_pkt_ = packet; + } else if (packet->seq_num_ == elm->seq_num_) //duplicate + return false; + else { //packet should be inserted somewhere after the head + num_later = 1; + + //walk through the history to find the correct place + elm2 = STAILQ_NEXT(elm,linfo_); + while(elm2 != NULL){ + if (packet->seq_num_ > elm2->seq_num_){ + STAILQ_INSERT_AFTER(&r_hist_, elm, packet, linfo_); + if (packet->type_ != DCCP_ACK) + r_last_data_pkt_ = packet; + + break; + } else if (packet->seq_num_ == elm2->seq_num_) { + return false; //duplicate + } + + elm = elm2; + elm2 = STAILQ_NEXT(elm,linfo_); + + num_later++; + + if (num_later == num_dup_acks_){ //packet is "lost" + return false; + } + } + + if(elm2 == NULL && num_later < num_dup_acks_){ + STAILQ_INSERT_TAIL(&r_hist_, packet, linfo_); + if (packet->type_ != DCCP_ACK) + r_last_data_pkt_ = packet; + } + + } + } + + removeAcksRecvHistory(); + + return true; +} + +/* Prints the contents of the receive history */ +void DCCPTFRCAgent::printRecvHistory(){ + struct r_hist_entry *elm = STAILQ_FIRST(&r_hist_); + if (elm == NULL) + fprintf(stdout, "Packet history is empty (recv)\n"); + else { + fprintf(stdout, "Packet history (recv):\n"); + while(elm != NULL){ + fprintf(stdout,"Packet: seq %d, type %s, ndp %d, size %d, win_count %d, t_recv %f\n", elm->seq_num_, packetTypeAsStr(elm->type_), elm->ndp_, elm->size_, elm->win_count_, elm->t_recv_); + elm = STAILQ_NEXT(elm, linfo_); + } + } +} + +/* Trim receive history + * arg: time - remove packet with recv time less than this + * seq_num - remove packet with seq num less than this + * Note: both of the above condition must be true for a packet + * to be removed. Furthermore, the function always keeps + * at least num_dup_acks_ packets in the history. + */ +void DCCPTFRCAgent::trimRecvHistory(double time, u_int32_t seq_num){ + struct r_hist_entry *elm, *elm2; + int num_later = 1; + elm = STAILQ_FIRST(&r_hist_); + + //find the packet after the num_dup_acks_ limit + while (elm != NULL && num_later <= num_dup_acks_){ + num_later++; + elm = STAILQ_NEXT(elm, linfo_); + } + + if (elm != NULL){ + //ensure that there exist atleast one data packet after num_dup_acks+1 limit (for loss event detection) + elm = findDataPacketInRecvHistory(STAILQ_NEXT(elm,linfo_)); + + if (elm != NULL){ + elm2 = STAILQ_NEXT(elm, linfo_); + while(elm2 != NULL){ + if (elm2->seq_num_ < seq_num && elm2->t_recv_ < time){ + STAILQ_REMOVE(&r_hist_,elm2,r_hist_entry,linfo_); + delete elm2; + } else + elm = elm2; + elm2 = STAILQ_NEXT(elm, linfo_); + } + } + } +} + +/* Remove all acks after the num_dup_acks_ limit */ +void DCCPTFRCAgent::removeAcksRecvHistory(){ + struct r_hist_entry *elm1 = STAILQ_FIRST(&r_hist_); + struct r_hist_entry *elm2; + + int num_later = 1; + //find the packet after the num_dup_acks_ limit + while (elm1 != NULL && num_later <= num_dup_acks_){ + num_later++; + elm1 = STAILQ_NEXT(elm1, linfo_); + } + + if(elm1 == NULL) + return; + + elm2 = STAILQ_NEXT(elm1, linfo_); + while(elm2 != NULL){ + if (elm2->type_ == DCCP_ACK){ + STAILQ_REMOVE(&r_hist_,elm2,r_hist_entry,linfo_); + delete elm2; + } else { + elm1 = elm2; + } + elm2 = STAILQ_NEXT(elm1, linfo_); + } +} + +/* Find a data packet in the receive history + * arg: start - pointer to the first packet to search from + * ret: pointer to the found data packet or + * or NULL if none is found. + */ +inline r_hist_entry *DCCPTFRCAgent::findDataPacketInRecvHistory(r_hist_entry *start){ + while(start != NULL && start->type_ == DCCP_ACK) + start = STAILQ_NEXT(start,linfo_); + return start; +} + +/* Detects packet loss in recv history. + * arg: seq_start, seq_end - the lost packet is in [start,end] + * win_count - window counter of packet before the loss + * ret: true if a packet loss was detected, otherwise false. + */ +bool DCCPTFRCAgent::detectLossRecv(u_int32_t *seq_start, u_int32_t *seq_end, u_int8_t *win_count){ + bool result = false; + struct r_hist_entry *before = STAILQ_FIRST(&r_hist_); + int num_later = 1; + //find the packet before the num_dup_acks_ limit + while (before != NULL && num_later < num_dup_acks_){ + num_later++; + before = STAILQ_NEXT(before, linfo_); + } + + struct r_hist_entry *after = NULL; + //find the packet after the limit + if (before != NULL) + after = STAILQ_NEXT(before, linfo_); + + if (before == NULL || after == NULL) + return false; + + u_int32_t dist = before->seq_num_ - after->seq_num_; + + if (dist == 1) //no loss + return false; + + //we have loss, check if it includes a data packet by comparing ndp values + /* check no data packets */ + if(before->type_ == DCCP_DATA || before->type_ == DCCP_DATAACK) + dist -= 1; + + if(dist % DCCP_NDP_LIMIT != (u_int32_t) ((int) before->ndp_ - (int) after->ndp_+DCCP_NDP_LIMIT) % DCCP_NDP_LIMIT){ + result = true; + *seq_start = before->seq_num_ - 1; + *seq_end = after->seq_num_ + 1; + after = findDataPacketInRecvHistory(after); + if (after == NULL) + *win_count = 0; + else + *win_count = after->win_count_; + } + + return result; +} + +/* Detect packet marks in recv history before num_dup_ack limit. + * arg: seq_num - sequence number of marked packet (if any) + * win_count - win_count of marked packet (if any) + * ret: true if a packet mark was found, otherwise false. + */ +bool DCCPTFRCAgent::detectECNRecv(u_int32_t *seq_num, u_int8_t *win_count){ + struct r_hist_entry *elm; + int num_later = 1; + elm = STAILQ_FIRST(&r_hist_); + + //walk through all packets <= num_dup_acks_ + while (elm != NULL && num_later <= num_dup_acks_){ + if ((elm->type_ == DCCP_DATA || elm->type_ == DCCP_DATAACK) + && elm->ecn_ == ECN_CE){ + elm->ecn_ = ECN_NOT_ECT; + *seq_num = elm->seq_num_; + *win_count = elm->win_count_; + return true; + } + num_later++; + elm = STAILQ_NEXT(elm, linfo_); + } + return false; +} + +/* Calculate the total amount of data in recent packets. + * arg: time - sum data in packets received later than this time + * ret: total amount of data + */ +u_int32_t DCCPTFRCAgent::sumPktSizes(double time){ + struct r_hist_entry *walker = STAILQ_FIRST(&r_hist_); + u_int32_t sum = 0; + walker = findDataPacketInRecvHistory(walker); + + while(walker != NULL){ + if (walker->t_recv_ >= time) + sum += walker->size_; + walker = findDataPacketInRecvHistory(STAILQ_NEXT(walker,linfo_)); + } + return sum; +} + +/* Sample the round trip time + * arg: rtt - the obtained rtt + * last_seq - sequence number of oldest packet used + * ret: true if the rtt could succesfully be estimated from wc + * false otherwise. Note that rtt above is always valid. + * Note: If not enough packets exist to use wc to estimate rtt, + * the rtt measured on handshake packets are used. + * The function will disregard packets with wc >= max_wc_inc_. + */ +bool DCCPTFRCAgent::sampleRTT(double *rtt, u_int32_t *last_seq){ + *last_seq = 0; + if (r_last_data_pkt_ == NULL){ + *rtt = rtt_conn_est_; + return false; + } + + struct r_hist_entry *elm, *elm2; + struct r_hist_entry *last = NULL; + + elm = findDataPacketInRecvHistory(STAILQ_NEXT(r_last_data_pkt_, linfo_)); + int prev_wc = r_last_data_pkt_->win_count_; + + while (elm != NULL) { + last = elm; + if ((prev_wc - (int)(elm->win_count_) + ccval_limit_) % ccval_limit_ >= max_wc_inc_) { + debug("%f, DCCP/TFRC(%s)::sampleRTT() - Win count distance of %d between to packets is too large (r_rtt_ %f)\n", now(), name(),(prev_wc - (int)(elm->win_count_) + ccval_limit_) % ccval_limit_, (double) r_rtt_); + //printRecvHistory(); + if (r_rtt_ > 0.0){ //we already have an rtt estimate + *rtt = r_rtt_; + *last_seq = last->seq_num_; + } else { //first estimation + *rtt = rtt_conn_est_; + } + return false; + } + prev_wc = (int)(elm->win_count_); + + if (((int) r_last_data_pkt_->win_count_ - (int)(elm->win_count_) + ccval_limit_) % ccval_limit_ > win_count_per_rtt_){ + + elm2 = findDataPacketInRecvHistory(STAILQ_NEXT(elm,linfo_)); + while(elm2 != NULL){ + if(elm2->win_count_ == last->win_count_ && elm2->t_recv_ > last->t_recv_) + last = elm2; + else if (elm2->win_count_ != last->win_count_) + break; + elm2 = findDataPacketInRecvHistory(STAILQ_NEXT(elm2,linfo_)); + } + break; + } + + elm = findDataPacketInRecvHistory(STAILQ_NEXT(elm, linfo_)); + } + + if (last == NULL){ + *rtt = rtt_conn_est_; + return false; + } + + int d = ((int) r_last_data_pkt_->win_count_ - (int) (last->win_count_) + ccval_limit_) % ccval_limit_; + if (d <= win_count_per_rtt_){ + debug("%f, DCCP/TFRC(%s)::sampleRTT() - Win count distance of %d is too small (r_rtt_ %f)\n", now(), name(), d, (double) r_rtt_); + if (r_rtt_ > 0.0){ //we already have an rtt estimate + *rtt = r_rtt_; + *last_seq = last->seq_num_; + } else { //first estimation + *rtt = rtt_conn_est_; + } + return false; + } + + *last_seq = last->seq_num_; + *rtt = ((double) win_count_per_rtt_)*(r_last_data_pkt_->t_recv_ - last->t_recv_)/(double) (d); + + return (elm != NULL); +} + +/* Calculate the receive rate to send on feedbacks + * ret: the receive rate + */ +double DCCPTFRCAgent::calcXrecv(){ + u_int32_t size = 0; + double t = now() - r_t_last_feedback_; + if (t < r_rtt_){ + size = sumPktSizes(now()-r_rtt_); + t = r_rtt_; + debug("%f, DCCP/TFRC(%s)::calcXrecv() - Used rtt %f as t -> xrecv = %f\n", now(), name(), t, ((double) size) / t); + } else { + size = r_bytes_recv_; + debug("%f, DCCP/TFRC(%s)::calcXrecv() - Used time since last feedback %f as t -> xrecv = %f\n", now(), name(), t, ((double) size) / t); + } + return ( ((double) size) / t); +} + +/* Clear the loss event history */ +void DCCPTFRCAgent::clearLIHistory(){ + struct li_hist_entry *li_elm,*li_elm2; + /* Empty loss interval history */ + li_elm = TAILQ_FIRST(&(r_li_hist_)); + while (li_elm != NULL) { + li_elm2 = TAILQ_NEXT(li_elm, linfo_); + delete li_elm; + li_elm = li_elm2; + } + TAILQ_INIT(&(r_li_hist_)); +} + +/* Print the loss event history */ +void DCCPTFRCAgent::printLIHistory(){ + struct li_hist_entry *li_elm; + + if (TAILQ_EMPTY(&r_li_hist_)) + printf("Loss interval history is empty\n"); + else { + printf("Loss interval history:\n"); + li_elm = TAILQ_FIRST(&(r_li_hist_)); + while (li_elm != NULL) { + printf("Length %d, start %d, win_count %d\n",li_elm->interval_,li_elm->seq_num_,li_elm->win_count_); + li_elm = TAILQ_NEXT(li_elm, linfo_); + } + } +} + +/* Detects quiescence of the corresponding sender + * ret: true if sender is quiescent + * false otherwise + */ +bool DCCPTFRCAgent::detectQuiescence(){ + debug("%f, DCCP/TFRC(%s)::detectQuiescence() - scheme %d, time now %f, time high data %f, rtt %f\n", now(), name(), q_scheme_, now(), r_q_t_data_,(double) r_rtt_); + + if (q_scheme_ == DCCP_Q_SCHEME_Q_OPT){ + if(r_sender_quiescent_) + debug("%f, DCCP/TFRC(%s)::detectQuiescence() - Receiver detected that the corresponding sender is quiescent (Q_OPT)\n", now(),name()); + return r_sender_quiescent_; + } else if (q_scheme_ == DCCP_Q_SCHEME_Q_FEAT){ + if (q_local_) + debug("%f, DCCP/TFRC(%s)::detectQuiescence() - Receiver detected that the corresponding sender is quiescent (Q_FEAT)\n", now(),name()); + return q_local_; + } + + if (r_rtt_ <= 0.0) + return false; + + double t = q_min_t_; + if (t < 2*r_rtt_) + t = 2*r_rtt_; + if (now()-r_q_t_data_ >= t) + debug("%f, DCCP/TFRC(%s)::detectQuiescence() - Receiver detected that the corresponding sender is quiescent (NORMAL)\n", now(), name()); + return (now()-r_q_t_data_ >= t); +} + +//protected methods + +/* OTcl binding of variables */ +void DCCPTFRCAgent::delay_bind_init_all(){ + delay_bind_init_one("use_loss_rate_local_"); + delay_bind_init_one("use_loss_rate_remote_"); + delay_bind_init_one("rtt_scheme_local_"); + delay_bind_init_one("rtt_scheme_remote_"); + delay_bind_init_one("num_dup_acks_"); + delay_bind_init_one("p_tol_"); + delay_bind_init_one("win_count_per_rtt_"); + delay_bind_init_one("max_wc_inc_"); + delay_bind_init_one("s_use_osc_prev_"); + delay_bind_init_one("s_smallest_p_"); + delay_bind_init_one("s_rtt_q_"); + delay_bind_init_one("s_rtt_q2_"); + delay_bind_init_one("s_t_mbi_"); + delay_bind_init_one("s_os_time_gran_"); + delay_bind_init_one("s_s_"); + delay_bind_init_one("s_initial_x_"); + delay_bind_init_one("s_initial_rto_"); + delay_bind_init_one("s_x_"); + delay_bind_init_one("s_x_inst_"); + delay_bind_init_one("s_x_recv_"); + delay_bind_init_one("s_r_sample_"); + delay_bind_init_one("s_rtt_"); + delay_bind_init_one("s_r_sqmean_"); + delay_bind_init_one("s_p_"); + delay_bind_init_one("s_q_opt_ratio_"); + delay_bind_init_one("r_s_"); + delay_bind_init_one("r_rtt_"); + delay_bind_init_one("r_p_"); + delay_bind_init_one("q_min_t_"); + DCCPAgent::delay_bind_init_all(); +} + +int DCCPTFRCAgent::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer){ + if (delay_bind(varName, localName, "use_loss_rate_local_", &use_loss_rate_local_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "use_loss_rate_remote_", &use_loss_rate_remote_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "rtt_scheme_local_", &rtt_scheme_local_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "rtt_scheme_remote_", &rtt_scheme_remote_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "num_dup_acks_", &num_dup_acks_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "p_tol_", &p_tol_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "win_count_per_rtt_", &win_count_per_rtt_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "max_wc_inc_", &max_wc_inc_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_use_osc_prev_", &s_use_osc_prev_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_smallest_p_", &s_smallest_p_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_rtt_q_", &s_rtt_q_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_rtt_q2_", &s_rtt_q2_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_t_mbi_", &s_t_mbi_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_os_time_gran_", &s_os_time_gran_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_s_", &s_s_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_initial_x_", &s_initial_x_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_initial_rto_", &s_initial_rto_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_x_", &s_x_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_x_inst_", &s_x_inst_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_x_recv_", &s_x_recv_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_r_sample_", &s_r_sample_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_rtt_", &s_rtt_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_p_", &s_p_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_r_sqmean_", &s_r_sqmean_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "s_q_opt_ratio_", &s_q_opt_ratio_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "r_s_", &r_s_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "r_rtt_", &r_rtt_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "r_p_", &r_p_, tracer)) return TCL_OK; + if (delay_bind(varName, localName, "q_min_t_", &q_min_t_, tracer)) return TCL_OK; + return DCCPAgent::delay_bind_dispatch(varName, localName, tracer); +} + +void DCCPTFRCAgent::reset(){ + DCCPAgent::reset(); + cancelTimers(); + + s_t_last_data_sent_ = (-q_min_t_)*2; + s_q_packets_wo_opt_ = 0; + + //sender + s_hist_last_seq_ = 0; + + clearSendHistory(); + + s_x_ = s_initial_x_; + s_x_inst_ = s_initial_x_; + s_x_recv_ = 0.0; + s_x_calc_ = 0.0; + + s_r_sample_ = 0.0; + s_rtt_ = 0.0; + s_r_sqmean_ = 0.0; + s_p_ = 0.0; + + s_t_ld_ = -1.0; + + s_last_win_count_ = 0; + s_t_last_win_count_ = -1.0; + s_wc_ = 0; + s_recv_wc_ = 0; + + s_idle_ = 0; + s_t_rto_ = 0.0; + + s_t_nom_ = 0.0; + s_t_ipi_ = 0.0; + s_delta_ = 0.0; + + /* init packet history */ + STAILQ_INIT(&(s_hist_)); + + s_state_ = TFRC_S_STATE_NO_SENT; + + //receiver + + clearRecvHistory(); + clearLIHistory(); + + r_high_seq_recv_ = 0; + r_high_ndp_recv_ = -1; + r_t_high_recv_ = 0.0; + + r_rtt_ = 0.0; + r_p_ = 0.0; + + r_last_counter_ = 0; + r_seq_last_counter_ = 0; + + r_t_last_feedback_ = 0.0; + r_bytes_recv_ = 0; + + r_q_t_data_ = 0.0; + r_sender_quiescent_ = false; + + /* init packet history */ + STAILQ_INIT(&(r_hist_)); + + /* init loss interval history */ + TAILQ_INIT(&(r_li_hist_)); + + r_state_ = TFRC_R_STATE_NO_DATA; + r_last_data_pkt_ = NULL; +} + +void DCCPTFRCAgent::cancelTimers(){ + s_timer_send_->force_cancel(); + s_timer_no_feedback_->force_cancel(); + DCCPAgent::cancelTimers(); +} + +bool DCCPTFRCAgent::processOption(u_int8_t type, u_char* data, u_int8_t size, Packet *pkt){ + bool result = DCCPAgent::processOption(type, data, size, pkt); + u_int32_t ui32; + if (result){ + switch(type){ + case DCCP_OPT_ACK_VECTOR_N0: + case DCCP_OPT_ACK_VECTOR_N1: + //check if we received a new ack vector + if (ackv_recv_ != NULL){//&& ackv_recv_->getFirstSeqNum() >= s_high_ack_recv_){ + //check if we got all needed packets in the history + if(s_hist_last_seq_ <= ackv_recv_->getLastSeqNum()){ + + u_int8_t ene = 0; + u_int32_t seqnum; + dccp_packet_state state; + struct s_hist_entry *elm = STAILQ_FIRST(&s_hist_); + + //walk through the ack vector and compute the ECN Nonce Echo + ackv_recv_->startPacketWalk(); + while(ackv_recv_->nextPacket(&seqnum, &state)){ + if (state == DCCP_PACKET_RECV){ + elm = findPacketInSendHistory(seqnum,elm); + if (elm != NULL && elm->seq_num_ == seqnum){ + ene = ene ^ (elm->ecn_); + } else if (elm == NULL) + break; + } + } + //compare with the received echo + if (ene != ackv_recv_->getENE()){ + printSendHistory(); + ackv_recv_->printPackets(); + + fprintf(stdout,"%f, DCCP/TFRC(%s)::processOption() - ECN check failed! \n", now(), name()); + sendReset(DCCP_RST_AGG_PEN,0,0,0); + result = false; + } + + //trim send history + trimSendHistory(ackv_recv_->getLastSeqNum()); + } else + debug("%f, DCCP/TFRC(%s)::processOption() - Ack vector includes packets not in history. Skipping ecn check for now...\n", now(), name()); + } + break; + case DCCP_OPT_QUIESCENCE: + r_sender_quiescent_ = true; + break; + case DCCP_TFRC_OPT_RTT: + if (size == 4) { + ((u_char*) &ui32)[0] = data[0]; + ((u_char*) &ui32)[1] = data[1]; + ((u_char*) &ui32)[2] = data[2]; + ((u_char*) &ui32)[3] = data[3]; + r_rtt_ = ((double) (ui32))*DCCP_TFRC_OPT_RTT_UNIT; + } else { + fprintf(stdout,"%f, DCCP/TFRC(%s)::processOption() - RTT option with wrong size %d received\n",now(), name(), size); + } + break; + } + } + return result; +} + +void DCCPTFRCAgent::buildInitialFeatureList(){ + DCCPAgent::buildInitialFeatureList(); + changeFeature(DCCP_TFRC_FEAT_LOSS_EVENT_RATE, DCCP_FEAT_LOC_LOCAL); + changeFeature(DCCP_TFRC_FEAT_LOSS_EVENT_RATE, DCCP_FEAT_LOC_REMOTE); + changeFeature(DCCP_TFRC_FEAT_RTT_SCHEME, DCCP_FEAT_LOC_LOCAL); + changeFeature(DCCP_TFRC_FEAT_RTT_SCHEME, DCCP_FEAT_LOC_REMOTE); +} + +int DCCPTFRCAgent::setFeature(u_int8_t num, dccp_feature_location location, + u_char* data, u_int8_t size, bool testSet){ + switch(num){ + case DCCP_TFRC_FEAT_LOSS_EVENT_RATE: + if (size == 1){ + if (location == DCCP_FEAT_LOC_LOCAL){ + if (use_loss_rate_local_ && data[0] || + !(use_loss_rate_local_ || data[0])) + return DCCP_FEAT_OK; + else + return DCCP_FEAT_NOT_PREFERED; + + } else { + if (use_loss_rate_remote_ && data[0] || + !(use_loss_rate_remote_ || data[0])) + return DCCP_FEAT_OK; + else + return DCCP_FEAT_NOT_PREFERED; + } + } else + return DCCP_FEAT_ERR_SIZE; + break; + case DCCP_TFRC_FEAT_RTT_SCHEME: + if (size == 1){ + if (location == DCCP_FEAT_LOC_LOCAL){ + if (rtt_scheme_local_ == data[0]) + return DCCP_FEAT_OK; + else + return DCCP_FEAT_NOT_PREFERED; + } else { + if (rtt_scheme_remote_ == data[0]) + return DCCP_FEAT_OK; + else + return DCCP_FEAT_NOT_PREFERED; + } + } else + return DCCP_FEAT_ERR_SIZE; + break; + default: + return DCCPAgent::setFeature(num, location, data, size); + } +} + +int DCCPTFRCAgent::getFeature(u_int8_t num, dccp_feature_location location, + u_char* data, u_int8_t maxSize){ + + switch(num){ + case DCCP_TFRC_FEAT_LOSS_EVENT_RATE: + if (maxSize > 0){ + if (location == DCCP_FEAT_LOC_LOCAL) + data[0] = (u_int8_t) use_loss_rate_local_; + else + data[0] = (u_int8_t) use_loss_rate_remote_; + + return 1; + } else + return DCCP_FEAT_ERR_SIZE; + break; + case DCCP_TFRC_FEAT_RTT_SCHEME: + if (maxSize > 0){ + if (location == DCCP_FEAT_LOC_LOCAL) + data[0] = (u_int8_t) rtt_scheme_local_; + else + data[0] = (u_int8_t) rtt_scheme_remote_; + return 1; + } else + return DCCP_FEAT_ERR_SIZE; + break; + default: + return DCCPAgent::getFeature(num, location, data, maxSize); + } +} + +dccp_feature_type DCCPTFRCAgent::getFeatureType(u_int8_t num){ + switch(num){ + case DCCP_TFRC_FEAT_LOSS_EVENT_RATE: + case DCCP_TFRC_FEAT_RTT_SCHEME: + return DCCP_FEAT_TYPE_SP; + break; + default: + return DCCPAgent::getFeatureType(num); + } +} + +bool DCCPTFRCAgent::send_askPermToSend(int dataSize, Packet *pkt){ + ack_num_ = seq_num_recv_; + bool result = ( tfrc_send_packet(dataSize) > 0 ); + + if (result && dataSize == 0){ //no ecn on pure acks + hdr_flags* flagsh = hdr_flags::access(pkt); + flagsh->ce() = 0; + flagsh->ect() = 0; + } + + u_int32_t el_time32 = (u_int32_t) ((now() - r_t_high_recv_)/DCCP_OPT_ELAPSED_TIME_UNIT); + double t; + if (q_scheme_ == DCCP_Q_SCHEME_Q_OPT && !infinite_send_ && dataSize == 0){ + t = q_min_t_; + if (t < 2*s_rtt_) + t = 2*s_rtt_; + if (now() - s_t_last_data_sent_ > t && sb_->empty()){ + s_q_packets_wo_opt_++; + if (s_q_packets_wo_opt_ >= s_q_opt_ratio_){ + opt_->addOption(DCCP_OPT_QUIESCENCE,NULL,0); + s_q_packets_wo_opt_ = 0; + } + } + } else if (q_scheme_ == DCCP_Q_SCHEME_Q_FEAT && !infinite_send_){ + if(dataSize > 0 && q_remote_){ + if (changeFeature(DCCP_FEAT_Q,DCCP_FEAT_LOC_REMOTE)) + q_remote_ = 0; + } else if (dataSize == 0) { + t = q_min_t_; + if (t < 2*s_rtt_) + t = 2*s_rtt_; + if (now() - s_t_last_data_sent_ > t && sb_->empty() && !q_remote_){ + if (changeFeature(DCCP_FEAT_Q,DCCP_FEAT_LOC_REMOTE)) + q_remote_ = 1; + + } + } + + } + + //add elapsed time on acknowledgments if needed + if(result && send_ack_ && el_time32 > 0){ + debug("%f, DCCP/TFRC(%s)::send_askPermToSend() - Ack is delayed by %d (t_high_recv_ %f)\n", now(), name(),el_time32,r_t_high_recv_); + if (el_time32 > 0 && el_time32 <= 0xFFFF){ + u_int16_t el_time16 = (u_int16_t) el_time32; + opt_->addOption(DCCP_OPT_ELAPSED_TIME,((u_char*) &el_time16), 2); + } else if (el_time32 > 0) + opt_->addOption(DCCP_OPT_ELAPSED_TIME,((u_char*) &el_time32), 4); + } + return result; +} + +void DCCPTFRCAgent::send_packetSent(Packet *pkt, bool moreToSend, int dataSize){ + hdr_dccp *dccph = hdr_dccp::access(pkt); + + switch(dccph->type_){ + case DCCP_DATA: + case DCCP_DATAACK: + if (q_scheme_ == DCCP_Q_SCHEME_Q_OPT) + s_q_packets_wo_opt_ = 0; + s_t_last_data_sent_ = now(); + break; + default: + ; + } + + tfrc_send_packet_sent(pkt,(int) moreToSend, dataSize); +} + +void DCCPTFRCAgent::send_packetRecv(Packet *pkt, int dataSize){ + tfrc_send_packet_recv(pkt); +} + +void DCCPTFRCAgent::recv_packetRecv(Packet *pkt, int dataSize){ + hdr_dccp *dccph = hdr_dccp::access(pkt); + + if (r_q_t_data_ == 0.0) + r_q_t_data_ = now(); + + if(dccph->type_ == DCCP_DATA || dccph->type_ == DCCP_DATAACK){ + r_q_t_data_ = now(); + r_sender_quiescent_ = false; + } else if (r_high_ndp_recv_ >= 0 && (int) (dccph->seq_num_ - r_high_seq_recv_) > 1){ + //some packet is missing + if ( (int) (dccph->seq_num_ - r_high_seq_recv_) > + ((dccph->ndp_ - r_high_ndp_recv_ + DCCP_NDP_LIMIT) % DCCP_NDP_LIMIT) + ){ //data packet is "lost" + debug("%f, DCCP/TFRC(%s)::recv_packetRecv() - Data loss detected by receiver seq_num %d - ndp %d, high_seq_recv %d - ndp %d\n",now(),name(), dccph->seq_num_, dccph->ndp_,r_high_seq_recv_, r_high_ndp_recv_); + r_q_t_data_ = now(); + r_sender_quiescent_ = false; + } + } + + if (dccph->seq_num_ > r_high_seq_recv_){ + r_high_seq_recv_ = dccph->seq_num_; + r_high_ndp_recv_ = dccph->ndp_; + r_t_high_recv_ = now(); + } + tfrc_recv_packet_recv(pkt, dataSize); +} + +/* Variable tracing */ + +void DCCPTFRCAgent::traceAll() { + char wrk[500]; + + sprintf(wrk,"%f %f %f %f %f %f %f %f %f %f\n", + now(), (double) s_x_, (double) s_x_inst_,(double) s_x_recv_, + (double) s_r_sample_, (double) s_rtt_, (double) s_r_sqmean_, + (double) s_p_, (double) r_rtt_, (double) r_p_); + + if (channel_) + Tcl_Write(channel_, wrk, strlen(wrk)); +} + + +void DCCPTFRCAgent::traceVar(TracedVar* v) +{ + char wrk[500]; + + if (!strcmp(v->name(), "s_x_") || + !strcmp(v->name(), "s_x_inst_") || + !strcmp(v->name(), "s_x_recv_") || + !strcmp(v->name(), "s_r_sample_") || + !strcmp(v->name(), "s_rtt_") || + !strcmp(v->name(), "s_r_sample_") || + !strcmp(v->name(), "s_rtt_") || + !strcmp(v->name(), "s_r_sqmean_") || + !strcmp(v->name(), "s_p_") || + !strcmp(v->name(), "r_rtt_") || + !strcmp(v->name(), "r_p_")){ + sprintf(wrk,"%f %f %s\n", now(), double(*((TracedDouble*) v)), v->name()); + if (channel_) + Tcl_Write(channel_, wrk, strlen(wrk)); + } else + DCCPAgent::traceVar(v); +} + + +//public methods + +/* Constructor + * ret: a new DCCPTCPlikeAgent + */ +DCCPTFRCAgent::DCCPTFRCAgent() : DCCPAgent() { + + ccid_ = DCCP_TFRC_CCID; + use_ecn_local_ = 1; + use_ecn_remote_ = 1; + use_ackv_local_ = 1; + use_ackv_remote_ = 1; + use_loss_rate_local_ = DCCP_TFRC_FEAT_DEF_LOSS_EVENT_RATE; + use_loss_rate_remote_ = DCCP_TFRC_FEAT_DEF_LOSS_EVENT_RATE; + rtt_scheme_local_ = DCCP_TFRC_FEAT_DEF_RTT_SCHEME; + rtt_scheme_remote_ = DCCP_TFRC_FEAT_DEF_RTT_SCHEME; + win_count_per_rtt_ = DCCP_TFRC_WIN_COUNT_PER_RTT; + + q_min_t_ = DCCP_TFRC_MIN_T; + + p_tol_ = DCCP_TFRC_P_TOLERANCE; + + s_t_last_data_sent_ = (-q_min_t_)*2; + s_q_opt_ratio_ = DCCP_TFRC_QUIESCENCE_OPT_RATIO; + s_q_packets_wo_opt_ = 0; + + s_timer_send_ = new DCCPTFRCSendTimer(this); + s_timer_no_feedback_ = new DCCPTFRCNoFeedbackTimer(this); + + s_initial_x_ = DCCP_TFRC_INIT_SEND_RATE; + s_initial_rto_ = DCCP_TFRC_INIT_RTO; + + s_rtt_q_ = DCCP_TFRC_RTT_Q; + s_rtt_q2_ = DCCP_TFRC_RTT_Q2; + s_t_mbi_ = DCCP_TFRC_MBI; + + s_s_ = DCCP_TFRC_STD_PACKET_SIZE; + s_x_ = s_initial_x_; + s_x_inst_ = s_initial_x_; + + s_use_osc_prev_ = 1; + + s_x_recv_ = 0.0; + s_x_calc_ = 0.0; + + s_r_sample_ = 0.0; + s_rtt_ = 0.0; + s_r_sqmean_ = 0.0; + s_p_ = 0.0; + s_smallest_p_ = DCCP_TFRC_SMALLEST_P; + + s_t_ld_ = -1.0; + + s_last_win_count_ = 0; + s_t_last_win_count_ = -1.0; + s_wc_ = 0; + s_recv_wc_ = 0; + + s_idle_ = 0; + s_t_rto_ = 0.0; + + s_t_nom_ = 0.0; + s_t_ipi_ = 0.0; + s_delta_ = 0.0; + s_os_time_gran_ = DCCP_TFRC_OPSYS_TIME_GRAN; + + /* init packet history */ + STAILQ_INIT(&(s_hist_)); + + s_state_ = TFRC_S_STATE_NO_SENT; + + max_wc_inc_ = DCCP_TFRC_MAX_WC_INC; + + s_hist_last_seq_ = 0; + + //receiver + + r_rtt_ = 0.0; + r_p_ = 0.0; + + r_last_counter_ = 0; + r_seq_last_counter_ = 0; + + r_t_last_feedback_ = 0.0; + r_bytes_recv_ = 0; + + r_s_ = DCCP_TFRC_STD_PACKET_SIZE; + + r_num_w_ = DCCP_TFRC_NUM_W; + + try { + r_w_ = new double[r_num_w_]; + } catch (...) { + fprintf(stderr, "DCCP/TFRC::DCCPTFRCAgent() - Out of memory\n"); + fflush(stdout); + abort(); + } + int r_num_w_half = r_num_w_/2; + + /* Init weights */ + for (int i = 0; i < r_num_w_; i++) { + if (i < r_num_w_half) + r_w_[i] = 1.0; + else + r_w_[i] = 1.0 - ((double) (i-(r_num_w_half - 1)))/(r_num_w_half + 1.0); + } + + /* init packet history */ + STAILQ_INIT(&(r_hist_)); + + /* init loss interval history */ + TAILQ_INIT(&(r_li_hist_)); + + r_state_ = TFRC_R_STATE_NO_DATA; + r_last_data_pkt_ = NULL; + + r_high_seq_recv_ = 0; + r_high_ndp_recv_ = -1; + r_t_high_recv_ = 0.0; + + r_q_t_data_ = 0.0; + r_sender_quiescent_ = false; +} + +/* Destructor */ +DCCPTFRCAgent::~DCCPTFRCAgent(){ + /* uninit sender */ + + /* unschedule timers */ + cancelTimers(); + + /* Empty packet history */ + + clearSendHistory(); + + clearRecvHistory(); + clearLIHistory(); + + delete [] r_w_; + delete s_timer_send_; + delete s_timer_no_feedback_; +} + +/* Process a "function call" from OTCl + * + * Supported function call handled by this agent: + * weights ++.... - set the weights for avg li calc. + * example $dccp weights 8+1.0+1.0+1.0+1.0+0.8+0.6+0.4+0.2 + */ +int DCCPTFRCAgent::command(int argc, const char*const* argv){ + if (argc == 3) { + if (strcmp(argv[1], "weights") == 0) { + int len = strlen(argv[2])+1; + if (len < 4) + return TCL_ERROR; + char *temp; + try { + temp = new char[len]; + strcpy(temp, argv[2]); + r_num_w_ = atoi(strtok(temp, "+")); + if (r_num_w_ <= 0){ + fprintf(stderr, "%f, DCCP/TFRC(%s)::command()/weights - Number of weights is invalid (num weights %i)\n",now(),name(),r_num_w_); + return TCL_ERROR; + } + delete r_w_; + r_w_ = new double[r_num_w_]; + } catch (...) { + fprintf(stderr, "%f, DCCP/TFRC(%s)::command()/weights - Out of memory (num weights %i)\n",now(), name(), r_num_w_); + return TCL_ERROR; + } + + char *weight; + for (int i = 0; i < r_num_w_; i++){ + weight = strtok(NULL, "+"); + if (weight == NULL){ + fprintf(stderr, "%f, DCCP/TFRC(%s)::command()/weights - Missing weight %i\n",now(),name(), i+1); + return TCL_ERROR; + } + r_w_[i] = atof(weight); + } + delete [] temp; + + printf("%f, DCCP/TFRC(%s)::command() - Weights:",now(), name()); + for (int i = 0; i < r_num_w_; i++) { + printf(" %f", r_w_[i]); + } + + printf(" (tot %i)\n", r_num_w_); + fflush(stdout); + return TCL_OK; + } + } + return (DCCPAgent::command(argc, argv)); +} + +/* A timeout has occured + * arg: tno - id of timeout event + * Handles: Send timer - DCCP_TFRC_TIMER_SEND + * No feedback timer - DCCP_TFRC_TIMER_NO_FEEDBACK + */ +void DCCPTFRCAgent::timeout(int tno){ + switch (tno){ + case DCCP_TFRC_TIMER_SEND: + debug("%f, DCCP/TFRC(%s)::timeout() - Send timer expired\n", now(), name()); + output(false); + //make sure we schedule next send time + tfrc_send_packet_sent(NULL,0,-1); + break; + case DCCP_TFRC_TIMER_NO_FEEDBACK: + debug("%f, DCCP/TFRC(%s)::timeout() - No feedback timer expired\n", now(), name()); + tfrc_time_no_feedback(); + break; + default: + DCCPAgent::timeout(tno); + } +} + +/* Sender side */ + +/* Calculate new t_ipi (inter packet interval) by + * t_ipi = s/X_inst; + * Note: No check for x=0 -> t_ipi ={0xFFF...,0xFFF} + */ +#define CALCNEWTIPI(ccbp) \ + do{ \ + s_t_ipi_ = ((double) s_s_) / s_x_inst_; \ + }while (0) + +/* Calculate new delta by + * delta = min(t_ipi/2, t_gran/2); + */ +#define CALCNEWDELTA(ccbp) \ + do { \ + s_delta_ = s_os_time_gran_ / 2.0; \ + if (s_t_ipi_ < s_os_time_gran_){ \ + s_delta_ = s_t_ipi_/2.0; \ + } \ + } while (0) + + +/* Calculate sending rate from the throughput equation + * arg: s - packet size (in bytes) + * R - round trip time (int seconds) + * p - loss event rate + * ret: maximum allowed sending rate + */ +inline double DCCPTFRCAgent::tfrc_calcX(u_int16_t s, double R, double p){ + double temp = R*sqrt(2*p/3) + (4*R*(3*sqrt(3*p/8))*p*(1+32*p*p)); + + return (((double) s) / temp); +} + +/* Find the loss event rate corresponding to a send rate + * in the TCP throughput eq. + * args: s - packet size (in bytes) + * R - Round trip time (in seconds) + * x - send rate (in bytes/s) + * returns: the loss eventrate accurate to 5% with resp. to x + */ +double DCCPTFRCAgent::tfrc_calcP(u_int16_t s, double R, double x){ + double p0 = 0.5; + double dp = 0.25; + double p, res; + int i = 0; + p = p0; + if (x <= tfrc_calcX(s,R,1.0)){ + return 1.0; + } + + while (i < 50){ + res = tfrc_calcX(s,R,p); + if (res > (1.0-p_tol_)*x && res < (1.0+p_tol_)*x) + return p; + else if (res > x) + p += dp; + else + p -= dp; + dp = dp/2; + i++; + } + debug("%f, DCCP/TFRC(%s)::tfrc_calcP() - Max iterations reached\n", now(), name()); + return p; +} + +/* Set the send timer + * arg: t_now - time now + */ +void DCCPTFRCAgent::tfrc_set_send_timer(double t_now){ + double t_temp; + + /* set send timer to expire in t_ipi - (t_now-t_nom_old) + * or in other words after t_nom - t_now */ + t_temp = s_t_nom_-t_now; + + if(t_temp < 0) { + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_set_send_timer() - Scheduled a negative time!\n", now(), name()); + fflush(stdout); + abort(); + } + debug("%f, DCCP/TFRC(%s)::tfrc_set_send_timer() - Send timer is scheduled to expire in %f s\n", now(), name(), t_temp); + + s_timer_send_->resched(t_temp); +} + +/* + * Update X by + * If (p > 0) + * x_calc = calcX(s,R,p); + * X = max(min(X_calc, 2*X_recv), s/t_mbi); + * Else + * If (t_now - tld >= R) + * X = max(min("2*X, 2*X_recv),s/R); + * tld = t_now; + * t_now - time now + */ +void DCCPTFRCAgent::tfrc_updateX(double t_now){ + double temp = 0; + double t_temp = 0; + + if (s_p_ >= s_smallest_p_){ /* to avoid large error in calcX */ + s_x_calc_ = tfrc_calcX(s_s_, s_rtt_, s_p_); + temp = 2*s_x_recv_; + if (s_x_calc_ < temp) + temp = s_x_calc_; + s_x_ = temp; + if(temp < ((double) s_s_)/s_t_mbi_) + s_x_ = ((double) s_s_)/s_t_mbi_; + + debug("%f, DCCP/TFRC(%s)::tfrc_updateX() - Updated send rate to %f bytes/s (p > 0, %f)\n", now(), name(), (double) s_x_, (double) s_p_); + + if (s_use_osc_prev_){ + s_x_inst_ = s_x_ * s_r_sqmean_ / sqrt(s_r_sample_); + debug("%f, DCCP/TFRC(%s)::tfrc_updateX() - Oscillation prevention adjusted send rate to %f bytes/s (p > 0, %f)\n", now(), name(), (double) s_x_inst_, (double) s_p_); + } else + s_x_inst_ = s_x_; + } else { + t_temp = t_now-s_t_ld_; + if (t_temp >= s_rtt_) { + temp = 2*s_x_recv_; + if (2*s_x_ < temp) + temp = 2*s_x_; + s_x_ = ((double)(s_s_))/s_rtt_; + if(temp > s_x_) + s_x_ = temp; + s_t_ld_ = t_now; + s_x_inst_ = s_x_; + debug("%f, DCCP/TFRC(%s)::tfrc_updateX() - Updated send rate to %f bytes/s (p == 0, %f)\n", now(), name(), (double) s_x_, (double) s_p_); + } else + debug("%f, DCCP/TFRC(%s)::tfrc_updateX() - Did not update send rate (p == 0, %f)\n", now(), name(), (double) s_p_); + } +} + +/* Halve the sending rate when no feedback timer expires */ +void DCCPTFRCAgent::tfrc_time_no_feedback(){ + double next_time_out = -1.0; + + switch(s_state_){ + case TFRC_S_STATE_NO_FBACK: + debug("%f, DCCP/TFRC(%s)::tfrc_time_no_feedback() - No feedback timer expired state NO_FBACK\n", now(), name()); + + /* half send rate */ + s_x_ = s_x_ / 2.0; + if (s_x_ < ((double) s_s_) / s_t_mbi_) + s_x_ = ((double) s_s_) / s_t_mbi_; + + s_x_inst_ = s_x_; + debug("%f, DCCP/TFRC(%s)::tfrc_time_no_feedback() - Updated send rate to %f bytes/s\n", now(), name(), (double) s_x_); + + /* calculate next time out */ + + next_time_out = 2.0*((double) (s_s_))/ s_x_; + + if (next_time_out < s_initial_rto_) + next_time_out = s_initial_rto_; + break; + case TFRC_S_STATE_FBACK: + /* Check if IDLE since last timeout and recv rate is less than 4 packets per RTT */ + + if(!(s_idle_) || (s_x_recv_ >= 4.0 * ((double) s_s_) / s_rtt_)){ + debug("%f, DCCP/TFRC(%s)::tfrc_time_no_feedback() - No feedback timer expired, state FBACK, not idle\n", now(), name()); + + /* Half sending rate */ + + /* If (X_calc > 2* X_recv) + * X_recv = max(X_recv/2, s/(2*t_mbi)); + * Else + * X_recv = X_calc/4; + */ + if(s_p_ >= s_smallest_p_ && s_x_calc_ == 0.0){ + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_time_no_feedback() - X_calc is zero while p = %f > 0\n", now(), name(), (double) s_p_); + fflush(stdout); + abort(); + } + + + /* check also if p i zero -> x_calc is infinity ?? */ + if(s_p_ < s_smallest_p_ || s_x_calc_ > 2*s_x_recv_){ + s_x_recv_ = s_x_recv_ / 2; + if (s_x_recv_ < ((double)s_s_)/(2*s_t_mbi_)) + s_x_recv_ = ((double)s_s_)/(2*s_t_mbi_); + } else { + s_x_recv_ = s_x_calc_ / 4.0; + } + + /* Update sending rate */ + tfrc_updateX(now()); + } + + /* Schedule no feedback timer to + * expire in max(4*R, 2*s/X) + */ + next_time_out = 2.0*((double) s_s_)/ s_x_; + if (next_time_out < s_t_rto_) + next_time_out = s_t_rto_; + + break; + default: + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_time_no_feedback() - Illegal state!\n", now(), name()); + fflush(stdout); + abort(); + break; + } + + /* Set timer */ + + if (next_time_out <= 0.0){ + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_time_no_feedback() - About to schedule no feedback timer in %f s.\n", now(), name(), next_time_out); + fflush(stdout); + abort(); + } + + debug("%f, DCCP/TFRC(%s)::tfrc_time_no_feedback() - Scheduled no feedback timer to expire in %f s\n", now(), name(), next_time_out); + + s_timer_no_feedback_->resched(next_time_out); + + /* set idle flag */ + s_idle_ = 1; +} + + + +/* Similar to send_askPermToSend() */ +int DCCPTFRCAgent::tfrc_send_packet(int datasize){ + u_int8_t answer = 0; + u_int8_t win_count = 0; + u_int32_t uw_win_count = 0; + double t_temp; + + /* check if pure ACK */ + if(datasize == 0){ + return 1; + } + + switch (s_state_){ + case TFRC_S_STATE_NO_SENT : + debug("%f, DCCP/TFRC(%s)::tfrc_send_packet() - DCCP asks for permission to send the first data packet\n", now(), name()); + + s_t_nom_ = now(); /* set nominal send time for initial packet */ + + /* init feedback timer */ + + s_timer_no_feedback_->sched((double) s_initial_rto_); + + s_t_last_win_count_ = now(); + debug("%f, DCCP/TFRC(%s)::tfrc_send_packet() - TFRC - Permission granted. Scheduled no feedback timer (initial) to expire in %f s\n", now(), name(),(double) s_initial_rto_); + + /* start send timer */ + + /* Calculate new t_ipi */ + CALCNEWTIPI(cb); + s_t_nom_ += s_t_ipi_; /* t_nom += t_ipi */ + + /* Calculate new delta */ + CALCNEWDELTA(cb); + tfrc_set_send_timer(now()); /* schedule sendtimer */ + s_state_ = TFRC_S_STATE_NO_FBACK; + answer = 1; + break; + case TFRC_S_STATE_NO_FBACK : + case TFRC_S_STATE_FBACK : + //if(!(s_ch_stimer.callout)){ + if (s_timer_send_->status() != TIMER_PENDING){ + t_temp = now() + s_delta_; + + //if (( timevalcmp(&(t_temp),&(s_t_nom_),>))){ + if (t_temp > s_t_nom_){ + /* Packet can be sent */ + + if (s_t_last_win_count_ == -1.0){ + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_send_packet() - t_last_win_count unitialized!\n", now(), name()); + fflush(stdout); + abort(); + } + + t_temp = now()-s_t_last_win_count_; + + /* calculate win_count option */ + if(s_state_ == TFRC_S_STATE_NO_FBACK){ + /* Assume RTT= t_rto(initial)/4 */ + uw_win_count = (u_int32_t) (t_temp / ((double) s_initial_rto_/(4.0*((double)win_count_per_rtt_)))); + }else{ + uw_win_count = (u_int32_t) (t_temp / (s_rtt_/((double)win_count_per_rtt_))); + } + /* don't increse wc with more than max_wc_inc_*/ + debug("%f, DCCP/TFRC(%s)::tfrc_send_packet() - window counter update: time since last wc %f, rtt %f, inc %d\n", now(), name(), t_temp, (double) s_rtt_, uw_win_count); + if(uw_win_count > (u_int32_t) max_wc_inc_) + uw_win_count = max_wc_inc_; + uw_win_count += s_last_win_count_; + + if (s_rtt_ > 0.0 && uw_win_count < s_recv_wc_ + win_count_per_rtt_) { + debug("%f, DCCP/TFRC(%s)::tfrc_send_packet() - window counter changed due to last acked wc restriction: wc before %d (%d) last wc %d (%d)\n", now(), name(), uw_win_count, uw_win_count % ccval_limit_, s_recv_wc_, s_recv_wc_ % ccval_limit_); + uw_win_count = s_recv_wc_ + win_count_per_rtt_; + } + s_wc_ = uw_win_count; + + win_count = uw_win_count % ccval_limit_; + answer = 1; + } else { + answer = 0; + } + } else { + answer = 0; + } + break; + default: + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_send_packet() - Illegal state!\n", now(), name()); + fflush(stdout); + abort(); + break; + } + + /* can we send? if so add options and add to packet history */ + if(answer){ + ccval_ = win_count; + if (rtt_scheme_local_ == DCCP_TFRC_RTT_SCHEME_OPTION && s_rtt_ > 0){ + u_int32_t temp_rtt = (u_int32_t) (s_rtt_/DCCP_TFRC_OPT_RTT_UNIT); + opt_->addOption(DCCP_TFRC_OPT_RTT,((u_char*) &temp_rtt), 4); + } + } + return answer; +} + +/* Similar to send_packetSent() */ +void DCCPTFRCAgent::tfrc_send_packet_sent(Packet *pkt, int moreToSend, int datasize){ + hdr_dccp *dccph = (pkt == NULL ? NULL : hdr_dccp::access(pkt)); + double t_temp; + struct s_hist_entry *packet; + int result; + + /* check if we have sent a data packet */ + if(datasize > 0){ + /* add packet to history */ + packet = new struct s_hist_entry; + + packet->t_sent_ = now(); + packet->seq_num_ = dccph->seq_num_; + result = getNonce(pkt); + + packet->ecn_ = (result >= 0 ? result : 0); + packet->win_count_ = s_wc_; + + STAILQ_INSERT_HEAD(&(s_hist_), packet, linfo_); + + /* check if win_count have changed */ + if(s_wc_ != s_last_win_count_){ + s_t_last_win_count_ = now(); + s_last_win_count_ = s_wc_; + } + debug("%f, DCCP/TFRC(%s)::tfrc_send_packet_sent() - Packet sent (seq: %u,win_count: %u (%u),t_sent %f )\n", now(), name(), packet->seq_num_,packet->win_count_, packet->win_count_ % ccval_limit_,packet->t_sent_); + s_idle_ = 0; + } + + /* if timer is running, do nothing */ + + if(s_timer_send_->status() == TIMER_PENDING) + return; + + switch (s_state_){ + case TFRC_S_STATE_NO_SENT : + /* if first was pure ack */ + if(datasize == 0){ + return; //goto sps_release; + } else { + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_send_packet_sent() - Packet sent in state NO_SENT included data!\n", now(), name()); + fflush(stdout); + abort(); + } + break; + case TFRC_S_STATE_NO_FBACK : + case TFRC_S_STATE_FBACK : + if(datasize <= 0){ /* we have ack (which never can have more to send?!?!?!?) (or simulate a sent packet to schedule send timer) */ + moreToSend = 0; + } else { + /* Calculate new t_ipi */ + CALCNEWTIPI(cb); + s_t_nom_ += s_t_ipi_; /* t_nom += t_ipi */ + /* Calculate new delta */ + CALCNEWDELTA(cb); + } + + if(!moreToSend){ + /* loop until we find a send time in the future */ + + t_temp = now()+s_delta_; + + //while((timevalcmp(&(t_temp),&(s_t_nom_),>))){ + while(t_temp > s_t_nom_){ + /* Calculate new t_ipi */ + CALCNEWTIPI(cb); + s_t_nom_ += s_t_ipi_; + /* Calculate new delta */ + CALCNEWDELTA(cb); + + t_temp = now() + s_delta_; + } + tfrc_set_send_timer(now()); + } else { + t_temp = now() + s_delta_; + /* Check if next packet can not be sent immediately */ + + if(!(t_temp > s_t_nom_)){ + tfrc_set_send_timer(now()); /* if so schedule sendtimer */ + } + } + break; + default: + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_send_packet_sent() - Illegal state!\n", now(), name()); + fflush(stdout); + abort(); + break; + } +} + +/* Similar to send_packetRecv() */ +void DCCPTFRCAgent::tfrc_send_packet_recv(Packet *pkt){ + hdr_dccp *dccph = hdr_dccp::access(pkt); + hdr_dccpack *dccpah = hdr_dccpack::access(pkt); + double next_time_out; + int res; + u_int32_t pinv = 0; + u_int32_t x_recv = 0; + + struct s_hist_entry *elm; //,*elm2; + bool gotP; + bool gotXrecv; + + /* we are only interested in ACKs */ + if (!(dccph->type_ == DCCP_ACK || dccph->type_ == DCCP_DATAACK)) + return; + + res = dccph->options_->getOption(DCCP_TFRC_OPT_LOSS_EVENT_RATE,(u_char *) &pinv,4); + gotP = (res == 4); + res = dccph->options_->getOption(DCCP_TFRC_OPT_RECV_RATE,(u_char *) &x_recv,4); + gotXrecv = (res == 4); + + if (!gotP || !gotXrecv){ + debug("%f, DCCP/TFRC(%s)::tfrc_send_packet_recv() - Missing options: gotP %u, gotXrecv %u\n", now(), name(), (int) gotP, (int) gotXrecv); + return; + } + + debug("%f, DCCP/TFRC(%s)::tfrc_send_packet_recv() - Received options on seq %u, ack %u: pinv=%u, t_elapsed=%u, x_recv=%u\n",now(),name(),dccph->seq_num_, dccpah->ack_num_,pinv,elapsed_time_recv_,x_recv); + + + switch (s_state_){ + case TFRC_S_STATE_NO_FBACK : + case TFRC_S_STATE_FBACK : + /* Calculate new round trip sample by + * R_sample = (t_now - t_recvdata)-t_delay; + */ + + /* get t_recvdata from history */ + elm = STAILQ_FIRST(&(s_hist_)); + while (elm != NULL) { + if (elm->seq_num_ == dccpah->ack_num_) + break; + elm = STAILQ_NEXT(elm, linfo_); + } + + if(elm == NULL){ //probably an ack-of-ack (but with feedback) + debug("%f, DCCP/TFRC(%s)::tfrc_send_packet_recv() - Acked packet (ack: %d) does not exist in history.\n", now(), name(),dccpah->ack_num_); + + } else { + //store wc on acked packet + + s_recv_wc_ = elm->win_count_; + + /* Update RTT */ + + s_r_sample_ = now() - (elm->t_sent_) - ((double) elapsed_time_recv_)*DCCP_OPT_ELAPSED_TIME_UNIT; + + /* Update RTT estimate by + * If (No feedback recv) + * R = R_sample; + * Else + * R = q*R+(1-q)*R_sample; + */ + if (s_state_ == TFRC_S_STATE_NO_FBACK){ + s_state_ = TFRC_S_STATE_FBACK; + s_rtt_ = s_r_sample_; + + if (s_use_osc_prev_) + s_r_sqmean_ = sqrt(s_r_sample_); + } else { + s_rtt_ = s_rtt_q_ * s_rtt_ + (1-s_rtt_q_)* s_r_sample_; + if (s_use_osc_prev_) + s_r_sqmean_ = s_rtt_q2_*s_r_sqmean_ + (1-s_rtt_q2_)*sqrt(s_r_sample_); + } + + debug("%f, DCCP/TFRC(%s)::tfrc_send_packet_recv() - New RTT estimate %f (last sample %f)\n", now(), name(), (double) s_rtt_, (double) s_r_sample_); + + if (s_use_osc_prev_) + debug("%f, DCCP/TFRC(%s)::tfrc_send_packet_recv() - New Long term RTT estimate %f (last sample %f)\n", now(), name(), (double) s_r_sqmean_, (double) s_r_sample_); + /* Update timeout interval */ + s_t_rto_ = 4*s_rtt_; + } + + /* Update receive rate */ + s_x_recv_ = (double) x_recv; /* x_recv in bytes per second */ + + /* Update loss event rate */ + if (pinv == 0) + s_p_ = 0; + else { + s_p_ = 1.0 / ((double) pinv); + + if(s_p_ < s_smallest_p_){ + s_p_ = s_smallest_p_; + + debug("%f, DCCP/TFRC(%s)::tfrc_send_packet_recv() - Smallest value of p is used!\n", now(), name()); + } + } + + /* unschedule no feedback timer */ + s_timer_no_feedback_->cancel(); + + /* Update sending rate */ + tfrc_updateX(now()); + + /* Update next send time */ + s_t_nom_ -= s_t_ipi_; /* revert to previous send time */ + + /* Calculate new t_ipi */ + CALCNEWTIPI(cb); + s_t_nom_ += s_t_ipi_; /* a new next send time */ + /* Calculate new delta */ + CALCNEWDELTA(cb); + + + s_timer_send_->cancel(); + + if(detectQuiescence()){ + send_ack_ = true; + } + output(false); + tfrc_send_packet_sent(NULL,0,-1); /* make sure we schedule next send time */ + + /* remove all packets older than the one acked from history */ + /* elm points to acked package! */ + + if(!use_ackv_remote_) + trimSendHistory(elm->seq_num_); + + /* Schedule no feedback timer to + * expire in max(4*R, 2*s/X) + */ + next_time_out = 2*((double) s_s_) / s_x_; + + if (next_time_out < s_t_rto_) + next_time_out = s_t_rto_; + + if (next_time_out <= 0.0){ + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_send_packet_recv() - About to schedule no feedback timer in %f s.\n", now(), name(), next_time_out); + fflush(stdout); + abort(); + } + + s_timer_no_feedback_->sched(next_time_out); + + debug("%f, DCCP/TFRC(%s)::tfrc_send_packet_recv() - Scheduled no feedback timer to expire in %f s\n", now(), name(), next_time_out); + + /* set idle flag */ + s_idle_ = 1; + + break; + default: + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_send_packet_recv() - Illegal state!\n", now(), name()); + fflush(stdout); + abort(); + break; + } +} + +/* Receiver side */ + +/* Find a data packet in history + * args: cb - ccb of receiver + * elm - pointer to element (variable) + * num - number in history (variable) + * returns: elm points to found packet, otherwise NULL + */ +#define TFRC_RECV_FINDDATAPACKET(cb,elm,num) \ + do{ \ + elm = STAILQ_FIRST(&(r_hist_)); \ + while((elm) != NULL){ \ + if((elm)->type_ == DCCP_DATA || (elm)->type_ == DCCP_DATAACK) \ + (num)--; \ + if(num == 0) \ + break; \ + elm = STAILQ_NEXT((elm), linfo_); \ + } \ + } while (0) + +/* Calculate the average loss interval + * ret: average loss interval (1/p) + */ +double DCCPTFRCAgent::tfrc_calcImean(){ + struct li_hist_entry *elm; + double I_tot; + double I_tot0 = 0.0; + double I_tot1 = 0.0; + double W_tot = 0.0; + int i; + elm = TAILQ_FIRST(&(r_li_hist_)); + + for (i = 0; i< r_num_w_; i++) { + if (elm == NULL) + goto I_panic; + + I_tot0 = I_tot0 + (elm->interval_ * r_w_[i]); + W_tot = W_tot + r_w_[i]; + elm = TAILQ_NEXT(elm, linfo_); + } + + elm = TAILQ_FIRST(&(r_li_hist_)); + elm = TAILQ_NEXT(elm, linfo_); + + for (i = 1; i <= r_num_w_; i++) { + if (elm == NULL) + goto I_panic; + + I_tot1 = I_tot1 + (elm->interval_ * r_w_[i-1]); + elm = TAILQ_NEXT(elm, linfo_); + } + + I_tot = I_tot0; /* I_tot = max(I_tot0, I_tot1) */ + if (I_tot0 < I_tot1) + I_tot = I_tot1; + + debug("%f, DCCP/TFRC(%s)::tfrc_calcImean() - I_tot0 %f, I_tot1 %f -> I_tot %f\n", now(), name(), I_tot0, I_tot1, I_tot); + + if(I_tot < W_tot) + I_tot = W_tot; + + return (I_tot/W_tot); + + I_panic: fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_calcImean() - Loss interval history is corrupt!\n", now(), name()); + fflush(stdout); + abort(); + return 0; +} + +/* Prepare and send a feedback packet */ +void DCCPTFRCAgent::tfrc_recv_send_feedback(){ + u_int32_t x_recv, pinv; + //u_int16_t t_elapsed; + struct r_hist_entry *elm; + int num; + + if (r_p_ < 0.00000000025) /* -> 1/p > 4 000 000 000 */ + pinv = 0; + else + pinv = (u_int32_t) ((double) 1.0 / r_p_); + + switch (r_state_){ + case TFRC_R_STATE_NO_DATA : + x_recv = 0; + break; + case TFRC_R_STATE_DATA : + /* Calculate x_recv */ + x_recv = (u_int32_t) calcXrecv(); + break; + default: + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_recv_send_feedback() - Illegal state!\n", now(), name()); + fflush(stdout); + abort(); + break; + } + + /* Find largest win_count so far (data packet with highest seqnum so far) */ + num = 1; + TFRC_RECV_FINDDATAPACKET(cb,elm,num); + + if(elm == NULL){ + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_recv_send_feedback() - No data packet in history!\n", now(), name()); + fflush(stdout); + abort(); + } + + /* add options from variables above */ + if(opt_->addOption(DCCP_TFRC_OPT_LOSS_EVENT_RATE, (u_char*) &pinv, 4) != DCCP_OPT_NO_ERR + || opt_->addOption(DCCP_TFRC_OPT_RECV_RATE, (u_char*) &x_recv, 4) != DCCP_OPT_NO_ERR){ + debug("%f, DCCP/TFRC(%s)::tfrc_recv_send_feedback() - Can't add options. No feedback sent\n", now(), name()); + //todo: remove option + return; + } + + send_ack_ = true; + + r_last_counter_ = elm->win_count_; + r_seq_last_counter_ = elm->seq_num_; + r_t_last_feedback_ = now(); + r_bytes_recv_ = 0; + + debug("%f, DCCP/TFRC(%s)::tfrc_recv_send_feedback() - Sending a feedback packet with pinv %u, x_recv %u, ack=%u (elm %u)\n",now(),name(),pinv, x_recv,seq_num_recv_, elm->seq_num_); + + output_ = true; + output_flag_ = true; +} + + +/* Approximate the length of the first loss interval + * ret: the length of the first loss interval + */ +u_int32_t DCCPTFRCAgent::tfrc_recv_calcFirstLI(){ + double x_recv, p; + u_int32_t temp, pinv; + + //if we haven't got an r_rtt_ approximation yet + if (r_rtt_ == 0.0){ + if (rtt_scheme_remote_ == DCCP_TFRC_RTT_SCHEME_WIN_COUNT){ + double temp_rtt = r_rtt_; + sampleRTT(&temp_rtt, &temp); + r_rtt_ = temp_rtt; + } else + r_rtt_ = rtt_conn_est_; + } + + x_recv = calcXrecv(); + + debug("%f, DCCP/TFRC(%s)::tfrc_recv_calcFirstLI() - Receive rate: %f bytes/s\n", now(), name(), x_recv); + + p = tfrc_calcP(r_s_,r_rtt_, x_recv); + pinv = (u_int32_t) ceil(1/p); + + debug("%f, DCCP/TFRC(%s)::tfrc_recv_calcFirstLI() - Approximated p to %f -> pinv = interval length = %u (xcalc/xrecv = %f)\n", now(), name(), p, pinv, tfrc_calcX(r_s_,r_rtt_,p) / x_recv); + + return pinv; +} + +/* Update the loss interval history */ +void DCCPTFRCAgent::tfrc_recv_updateLI(){ + struct r_hist_entry *elm; + struct li_hist_entry *li_elm = NULL; + u_int32_t seq_temp = 0; + u_int8_t win_start; + u_int8_t win_loss; + int debug_info = 0; + u_int32_t seq_start; + u_int32_t seq_end; + u_int32_t seq_loss; + int i = 0; + while(i < 2){ + i++; + if(i == 1 && detectLossRecv(&seq_start, &seq_end, &win_loss)){ + /* we have found a packet loss! */ + seq_loss = seq_end; + debug("%f, DCCP/TFRC(%s)::tfrc_recv_updateLI() - Loss found: seqloss=%d, winloss=%d\n", now(), name(), seq_loss, win_loss); + } else if (i == 1) { + continue; + } else if (i == 2 && detectECNRecv(&seq_end, &win_loss)) { + seq_loss = seq_end; + debug("%f, DCCP/TFRC(%s)::tfrc_recv_updateLI() - ECN mark found: seqloss=%d, winloss=%d\n", now(), name(), seq_loss, win_loss); + } else + break; + + if(TAILQ_EMPTY(&(r_li_hist_))){ + debug_info = 1; + /* first loss detected */ + debug("%f, DCCP/TFRC(%s)::tfrc_recv_updateLI() - First loss event detected\n", now(), name()); + + /* create history */ + try{ + for(i=0; i< r_num_w_+1; i++){ + li_elm = new struct li_hist_entry; + + li_elm->interval_ = 0; + li_elm->seq_num_ = 0; + li_elm->win_count_ = 0; + TAILQ_INSERT_HEAD(&(r_li_hist_),li_elm,linfo_); + } + } catch (...) { + debug("%f, DCCP/TFRC(%s)::tfrc_recv_updateLI() - Not enough memory for loss interval history!\n", now(), name()); + clearLIHistory(); + return; + } + + li_elm->seq_num_ = seq_loss; + li_elm->win_count_ = win_loss; + + li_elm = TAILQ_NEXT(li_elm,linfo_); + /* add approx interval */ + li_elm->interval_ = tfrc_recv_calcFirstLI(); + + } else { /* we have a loss interval history */ + debug_info = 2; + /* Check if the loss is in the same loss event as interval start */ + if ((TAILQ_FIRST(&(r_li_hist_)))->seq_num_ > seq_loss) + continue; + + win_start = (TAILQ_FIRST(&(r_li_hist_)))->win_count_; + if ((win_loss > win_start + && win_loss - win_start > win_count_per_rtt_) || + (win_loss < win_start + && win_start - win_loss < ccval_limit_-win_count_per_rtt_)){ + /* new loss event detected */ + /* calculate last interval length */ + if (seq_loss <= TAILQ_FIRST(&(r_li_hist_))->seq_num_){ + fprintf(stderr, "seq_loss is less than last interval start\n"); + fflush(stdout); + abort(); + } + + seq_temp = seq_loss - TAILQ_FIRST(&(r_li_hist_))->seq_num_; + + (TAILQ_FIRST(&(r_li_hist_)))->interval_ = seq_temp; + + debug("%f, DCCP/TFRC(%s)::tfrc_recv_updateLI() - New loss event detected\n", now(), name()); + + /* Remove oldest interval */ + li_elm = TAILQ_LAST(&(r_li_hist_),li_hist_head); + TAILQ_REMOVE(&(r_li_hist_),li_elm,linfo_); + + /* Create the newest interval */ + li_elm->seq_num_ = seq_loss; + li_elm->win_count_ = win_loss; + + /* insert it into history */ + TAILQ_INSERT_HEAD(&(r_li_hist_),li_elm,linfo_); + } else + debug("%f, DCCP/TFRC(%s)::tfrc_recv_updateLI() - Loss belongs to previous loss event\n", now(), name()); + } + } + + if(TAILQ_FIRST(&(r_li_hist_)) != NULL){ + /* calculate interval to last loss event */ + + elm = STAILQ_FIRST(&r_hist_); + if (elm->seq_num_ < TAILQ_FIRST(&(r_li_hist_))->seq_num_){ + fprintf(stderr, "updating the recent li : seq_num_%d < last_loss_start %d\n",elm->seq_num_, TAILQ_FIRST(&(r_li_hist_))->seq_num_); + fflush(stdout); + abort(); + } + seq_temp = elm->seq_num_- + TAILQ_FIRST(&(r_li_hist_))->seq_num_; + + (TAILQ_FIRST(&(r_li_hist_)))->interval_ = seq_temp; + if (debug_info > 0){ + debug("%f, DCCP/TFRC(%s)::tfrc_recv_updateLI() - Highest data packet received %u\n", now(), name(), elm->seq_num_); + + } + + } +} + + +/* Similar to recv_packetRecv */ +void DCCPTFRCAgent::tfrc_recv_packet_recv(Packet* pkt, int dataSize){ + hdr_dccp *dccph = hdr_dccp::access(pkt); + //hdr_dccpack *dccpah = hdr_dccpack::access(pkt); + struct r_hist_entry *packet; + u_int8_t win_count = 0; + double p_prev; + bool ins = false; + u_int32_t trim_to; + double temp_rtt; + if(!(r_state_ == TFRC_R_STATE_NO_DATA || r_state_ == TFRC_R_STATE_DATA)){ + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_recv_packet_recv() - Illegal state!\n", now(), name()); + fflush(stdout); + abort(); + return; + } + + if (r_rtt_ <= 0.0){ //initiate the rtt estimate + if (rtt_scheme_remote_ == DCCP_TFRC_RTT_SCHEME_WIN_COUNT){ + temp_rtt = r_rtt_; + sampleRTT(&temp_rtt, &trim_to); + r_rtt_ = temp_rtt; + } else { + r_rtt_ = rtt_conn_est_; + trim_to = dccph->seq_num_; + } + } + + /* Check which type */ + switch(dccph->type_){ + case DCCP_ACK: + if(r_state_ == TFRC_R_STATE_NO_DATA) + return; + break; + case DCCP_DATA: + case DCCP_DATAACK: + break; + default: + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_recv_packet_recv() - Received wrong packet type!\n", now(), name()); + fflush(stdout); + abort(); + return; + } + + /* Add packet to history */ + + packet = new struct r_hist_entry; + + if (packet == NULL){ + debug("%f, DCCP/TFRC(%s)::tfrc_recv_packet_recv() - Not enough memory to add received packet to history (consider it lost)!\n", now(), name()); + return; + } + + packet->t_recv_ = now(); + packet->seq_num_ = dccph->seq_num_; + packet->type_ = dccph->type_; + packet->ndp_ = dccph->ndp_; + packet->size_ = dataSize; + packet->ecn_ = getECNCodePoint(pkt); + win_count = dccph->ccval_; + packet->win_count_ = dccph->ccval_; + + ins = insertInRecvHistory(packet); + + if (!ins){ //packet insertion failed + delete packet; + return; + } + + + if (dccph->type_ != DCCP_ACK){ + if (rtt_scheme_remote_ == DCCP_TFRC_RTT_SCHEME_WIN_COUNT){ + temp_rtt = r_rtt_; + sampleRTT(&temp_rtt, &trim_to); + r_rtt_ = temp_rtt; + } else { + r_rtt_ = rtt_conn_est_; + trim_to = dccph->seq_num_; + } + debug("%f, DCCP/TFRC(%s)::tfrc_recv_packet_recv() - RTT estimated to %f (last seq %d)\n", now(), name(), (double) r_rtt_, trim_to); + } + switch (r_state_){ + case TFRC_R_STATE_NO_DATA : + if (dccph->type_ != DCCP_ACK){ + debug("%f, DCCP/TFRC(%s)::tfrc_recv_packet_recv() - Sending the initial feedback packet\n", now(), name()); + tfrc_recv_send_feedback(); + r_state_ = TFRC_R_STATE_DATA; + } + break; + case TFRC_R_STATE_DATA : + r_bytes_recv_ = r_bytes_recv_ + dataSize; + + /* find loss events */ + tfrc_recv_updateLI(); + p_prev = r_p_; + + /* Calculate loss event rate */ + if(!TAILQ_EMPTY(&(r_li_hist_))){ + r_p_ = 1/tfrc_calcImean(); + } + /* check send conditions then send */ + if(r_p_ > p_prev){ + debug("%f, DCCP/TFRC(%s)::tfrc_recv_packet_recv() - Sending a feedback packet because p>p_prev\n", now(), name()); + tfrc_recv_send_feedback(); + trimRecvHistory(now() - r_rtt_,trim_to); + } else if (dccph->type_ != DCCP_ACK) { + if (dccph->seq_num_ > r_seq_last_counter_){ + /* the sequence number is newer than seq_last_count */ + if ((win_count > r_last_counter_ + && win_count-r_last_counter_ >= win_count_per_rtt_) || + (win_count < r_last_counter_ + && r_last_counter_ - win_count <= ccval_limit_-win_count_per_rtt_)){ + debug("%f, DCCP/TFRC(%s)::tfrc_recv_packet_recv() - Sending a feedback packet because one rtt has passed since last packet (diff in win_count %i)\n", now(), name(),(win_count-r_last_counter_+ccval_limit_) % ccval_limit_); + + tfrc_recv_send_feedback(); + trimRecvHistory(now() - r_rtt_,trim_to); + } + } + } + break; + default: + fprintf(stderr,"%f, DCCP/TFRC(%s)::tfrc_recv_packet_recv() - Illegal state!\n", now(), name()); + fflush(stdout); + abort(); + break; + } +} + + + + + + + + + + + + + + diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp_tfrc.h ns-2.34_dccp/dccp/dccp_tfrc.h --- ns-2.34_orig/dccp/dccp_tfrc.h 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp_tfrc.h 2010-03-08 15:09:55.797597400 +0100 @@ -0,0 +1,539 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp_tfrc.h,v 1.29 2004/02/24 12:37:32 nilmat-8 Exp $ */ + +#ifndef ns_dccp_tfrc_h +#define ns_dccp_tfrc_h + +#include "dccp.h" +#include "bsd_queue.h" + +/* use traced variables */ +#define DCCP_TFRC_USE_TRACED_VARS + +#define DCCP_TFRC_CCID 3 + +/* features */ +#define DCCP_TFRC_FEAT_LOSS_EVENT_RATE 192 +#define DCCP_TFRC_FEAT_RTT_SCHEME 191 /* experimental */ + +/* rtt schemes (experimental) */ +#define DCCP_TFRC_RTT_SCHEME_WIN_COUNT 0 +#define DCCP_TFRC_RTT_SCHEME_OPTION 1 + +#define DCCP_TFRC_FEAT_DEF_LOSS_EVENT_RATE 1 +#define DCCP_TFRC_FEAT_DEF_RTT_SCHEME DCCP_TFRC_RTT_SCHEME_WIN_COUNT + +/* options */ +#define DCCP_TFRC_OPT_LOSS_EVENT_RATE 192 +#define DCCP_TFRC_OPT_RECV_RATE 194 +#define DCCP_TFRC_OPT_RTT 191 /* experimental */ +#define DCCP_TFRC_OPT_RTT_UNIT 0.000001 + +#define DCCP_TFRC_TIMER_SEND 128 +#define DCCP_TFRC_TIMER_NO_FEEDBACK 129 + +#define DCCP_TFRC_MAX_WC_INC 5 /* limit increase in wc to this between two packets */ +#define DCCP_TFRC_WIN_COUNT_PER_RTT 4 + +//quiescence +#define DCCP_TFRC_MIN_T 0.2 //T value for quiescence detection +#define DCCP_TFRC_QUIESCENCE_OPT_RATIO 1 + +#define DCCP_TFRC_STD_PACKET_SIZE 500 /* standard packet size*/ + + +/* + * TFRC sender + */ + +/* TFRC sender states */ +#define DCCP_TFRC_NUM_S_STATES 3 +enum dccp_tfrc_s_state { TFRC_S_STATE_NO_SENT = 0, + TFRC_S_STATE_NO_FBACK = 1, + TFRC_S_STATE_FBACK = 2 }; + +/* Mechanism parameters */ + +/* initial sending rate rfc 3448 */ +#define DCCP_TFRC_INIT_SEND_RATE DCCP_TFRC_STD_PACKET_SIZE + +#define DCCP_TFRC_OPSYS_TIME_GRAN 0.01 /* operating system timer granularity in s */ +#define DCCP_TFRC_SMALLEST_P 0.00001 /* smallest value of p allowed */ + +#define DCCP_TFRC_RTT_Q 0.9 /* RTT Filter constant */ +#define DCCP_TFRC_RTT_Q2 0.9 /* Long term RTT filter constant */ +#define DCCP_TFRC_MBI 64.0 /* max backoff interval */ +#define DCCP_TFRC_INIT_RTO 2.0 /* initial rto value */ + + +/* Packet history */ +STAILQ_HEAD(s_hist_head,s_hist_entry); + +struct s_hist_entry { + STAILQ_ENTRY(s_hist_entry) linfo_; /* Tail queue. */ + u_int32_t seq_num_; /* Sequence number */ + double t_sent_; /* When the packet was sent */ + u_int32_t win_count_; /* Windowcounter for packet */ + u_int8_t ecn_; /* ecn nonce (0 or 1) sent */ +}; + + +/* + * TFRC Receiver + */ + +/* TFRC receiver states */ +#define DCCP_TFRC_NUM_R_STATES 2 +enum dccp_tfrc_r_state { TFRC_R_STATE_NO_DATA = 0, + TFRC_R_STATE_DATA = 1}; + +/* Receiver mechanism parameters */ +#define DCCP_TFRC_NUM_W 8 /* length(w[]) */ +#define DCCP_TFRC_P_TOLERANCE 0.05 /* tolerance allowed when inverting the throughput equation */ + +/* Packet history */ +STAILQ_HEAD(r_hist_head,r_hist_entry); + +struct r_hist_entry { + STAILQ_ENTRY(r_hist_entry) linfo_; /* Tail queue. */ + u_int32_t seq_num_; /* Sequence number */ + double t_recv_; /* When the packet was received */ + u_int8_t win_count_; /* Window counter for that packet */ + dccp_packet_type type_; /* Packet type received */ + u_int8_t ndp_; /* no data packets value */ + u_int16_t size_; /* size of data in packet */ + dccp_ecn_codepoint ecn_; /* ecn codepoint */ +}; + +/* Loss interval history */ +TAILQ_HEAD(li_hist_head,li_hist_entry); + +struct li_hist_entry { + TAILQ_ENTRY(li_hist_entry) linfo_; /* Tail queue. */ + u_int32_t interval_; /* Loss interval */ + u_int32_t seq_num_; /* Sequence number of the packet that started the interval */ + u_int8_t win_count_; /* Window counter for previous received packet */ +}; + +//forward decleration of class DCCPTCPlikeAgent +class DCCPTFRCAgent; + +//The DCCPTFRCSendTimer sends packets according to a predetermined sending rate +class DCCPTFRCSendTimer : public TimerHandler { +protected: + DCCPTFRCAgent *agent_; //the owning agent +public: + /* Constructor + * arg: agent - the owning agent (to notify about timeout) + * ret: A new DCCPTFRCSendTimer + */ + DCCPTFRCSendTimer(DCCPTFRCAgent* agent); + + /* Called when the timer has expired + * arg: e - The event that happened (i.e. the timer expired) + */ + virtual void expire(Event *e); +}; + +/* The DCCPTFRCNoFeedbackTimer expires when no feedback has been received */ +class DCCPTFRCNoFeedbackTimer : public TimerHandler { +protected: + DCCPTFRCAgent *agent_; //the owning agent +public: + /* Constructor + * arg: agent - the owning agent (to notify about timeout) + * ret: A new DCCPTFRCNoFeedbackTimer + */ + DCCPTFRCNoFeedbackTimer(DCCPTFRCAgent* agent); + + /* Called when the timer has expired + * arg: e - The event that happened (i.e. the timer expired) + */ + virtual void expire(Event *e); +}; + +/* The DCCPTFRC agent is based on the TFRC code from the FreeBSD + * implementation at www.dccp.org. + * If changes are made to tcl variables, the agent should be reset + * to enable all changes. + * Conforms to the October 2003 drafts. + */ +class DCCPTFRCAgent : public DCCPAgent { +private: + /* The number of packets with higher sequence number needed before + * a delayed packet is considered lost (similiar to NUMDUPACKS) */ + int num_dup_acks_; + + double p_tol_; /* tolerance in p when inverting thrp eq */ + + //features + int use_loss_rate_local_; + int use_loss_rate_remote_; + + int rtt_scheme_local_; + int rtt_scheme_remote_; + + int win_count_per_rtt_; /* Number of wincount ticks in one RTT */ + int max_wc_inc_; /* Maximum increment of win count */ + + double q_min_t_; /* T value for quiescence detection */ + //sender + +#ifdef DCCP_TFRC_USE_TRACED_VARS + TracedDouble s_x_; /* Current sending rate (bytes/s) */ + TracedDouble s_x_inst_; /* Instantaneous send rate */ + TracedDouble s_x_recv_; /* Receive rate (bytes/s) */ + TracedDouble s_r_sample_; /* Last rtt sample */ + TracedDouble s_rtt_; /* Estimate of current round trip time (s) */ + TracedDouble s_r_sqmean_; /* Long term rtt */ + TracedDouble s_p_; /* Current loss event rate */ +#else + double s_x_; /* Current sending rate (bytes/s) */ + double s_x_inst_; /* Instantaneous send rate */ + double s_x_recv_; /* Receive rate (bytes/s) */ + double s_r_sample_; /* Last rtt sample */ + double s_rtt_; /* Estimate of current round trip time (s) */ + double s_r_sqmean_; /* Long term rtt */ + double s_p_; /* Current loss event rate */ +#endif + double s_initial_x_; /* Initial send rate (in bytes/s) */ + double s_initial_rto_; /* Initial timeout (in s) */ + double s_rtt_q_; /* RTT Filter constant */ + double s_rtt_q2_; /* Long term RTT filter constant */ + double s_t_mbi_; /* Max back-off interval (in s) */ + + int s_use_osc_prev_; /* Use oscillation prevention? */ + + /* Timers */ + DCCPTFRCSendTimer *s_timer_send_; + DCCPTFRCNoFeedbackTimer *s_timer_no_feedback_; + + dccp_tfrc_s_state s_state_; /* Sender state */ + + double s_x_calc_; /* Calculated send rate (bytes/s) */ + + int s_s_; /* Packet size (bytes) */ + + double s_smallest_p_; /* Smallest p value */ + + u_int32_t s_last_win_count_; /* Last window counter sent */ + u_int32_t s_wc_; /* Window counter */ + u_int32_t s_recv_wc_; /* Last window counter of last ack packet */ + double s_t_last_win_count_; /* Timestamp of earliest packet with + last_win_count value sent */ + u_int8_t s_idle_; + double s_t_rto_; /* Time out value = 4*rtt */ + double s_t_ld_; /* Time last doubled during slow start */ + + double s_t_nom_, /* Nominal send time of next packet */ + s_t_ipi_, /* Interpacket (send) interval */ + s_delta_; /* Send timer delta */ + + double s_os_time_gran_; /* Operating system timer granularity */ + + struct s_hist_head s_hist_; /* Packet history */ + + u_int32_t s_hist_last_seq_; /* last sequence number in history */ + + + //quiescence + double s_t_last_data_sent_; /* timestamp of when last data pkt was sent */ + int s_q_opt_ratio_; /* add Q opt on each opt ratio acks sent */ + int s_q_packets_wo_opt_; /* ack packets sent without Q opt */ + + /* Clear the send history */ + void clearSendHistory(); + + /* Find a specific packet in the send history + * arg: seq_num - sequence number of packet to find + * start_elm - start searching from this element + * ret: pointer to the correct element if the element is found + * otherwise the closest packet with lower seq num is returned + * which can be NULL. + */ + struct s_hist_entry* findPacketInSendHistory(u_int32_t seq_num, s_hist_entry* start_elm); + + /* Remove packets from send history + * arg: trim_to - remove packets with lower sequence numbers than this + * Note: if trim_to does not exist in the history, trim_to is set + * to the next lower sequence number found before trimming. + */ + void trimSendHistory(u_int32_t trim_to); + + /* Prints the contents of the send history */ + void printSendHistory(); + + /* from project (slightly modified) */ + + /* Halve the sending rate when no feedback timer expires */ + void tfrc_time_no_feedback(); + + /* Set the send timer + * arg: t_now - time now + */ + void tfrc_set_send_timer(double t_now); + + /* Update the sending rate + * arg: t_now - time now + */ + void tfrc_updateX(double t_now); + + /* Calculate sending rate from the throughput equation + * arg: s - packet size (in bytes) + * R - round trip time (int seconds) + * p - loss event rate + * ret: maximum allowed sending rate + */ + double tfrc_calcX(u_int16_t s, double R, double p); + + /* Find the loss event rate corresponding to a send rate + * in the TCP throughput eq. + * args: s - packet size (in bytes) + * R - Round trip time (in seconds) + * x - send rate (in bytes/s) + * returns: the loss eventrate accurate to 5% with resp. to x + */ + double tfrc_calcP(u_int16_t s, double R, double x); + + /* Similar to send_askPermToSend(), send_packetSent(), send_packetRecv() */ + int tfrc_send_packet(int datasize); + void tfrc_send_packet_sent(Packet *pkt, int moreToSend, int datasize); + void tfrc_send_packet_recv(Packet *pkt); + + //receiver + +#ifdef DCCP_TFRC_USE_TRACED_VARS + TracedDouble r_rtt_; //receivers estimation of rtt + TracedDouble r_p_; /* Loss event rate */ +#else + double r_rtt_; //receivers estimation of rtt + double r_p_; /* Loss event rate */ +#endif + dccp_tfrc_r_state r_state_; /* Receiver state */ + + struct li_hist_head r_li_hist_; /* Loss interval history */ + + u_int8_t r_last_counter_; /* Highest value of the window counter + received when last feedback was sent */ + u_int32_t r_seq_last_counter_; /* Sequence number of the packet above */ + + double r_t_last_feedback_; /* Timestamp of when last feedback was sent */ + u_int32_t r_bytes_recv_; /* Bytes received since t_last_feedback */ + + struct r_hist_head r_hist_; /* Packet history */ + + int r_s_; /* Packet size */ + + int r_num_w_; /* Number of weights */ + double *r_w_; /* Weights */ + + u_int32_t r_high_seq_recv_; //highest sequence number received + int r_high_ndp_recv_; //ndp value of high_seq_recv_ + double r_t_high_recv_; //timestamp of high_seq_recv_ + r_hist_entry *r_last_data_pkt_; //pointer to last data packet recv + + //quiescence + double r_q_t_data_; //timestamp of last data pkt observed + bool r_sender_quiescent_; //the sender is quiescent (q opt recv) + + /* Clear the history of received packets */ + void clearRecvHistory(); + + /* Insert a packet in the receive history + * Will not insert "lost" packets or duplicates. + * arg: packet - entry representing the packet to insert + * ret: true if packet was inserted, otherwise false. + */ + bool insertInRecvHistory(r_hist_entry *packet); + + /* Prints the contents of the receive history */ + void printRecvHistory(); + + /* Trim receive history + * arg: time - remove packet with recv time less than this + * seq_num - remove packet with seq num less than this + * Note: both of the above condition must be true for a packet + * to be removed. Furthermore, the function always keeps + * at least num_dup_acks_ packets in the history. + */ + void trimRecvHistory(double time, u_int32_t seq_num); + + /* Remove all acks after the num_dup_acks_ limit */ + void removeAcksRecvHistory(); + + /* Find a data packet in the receive history + * arg: start - pointer to the first packet to search from + * ret: pointer to the found data packet or + * or NULL if none is found. + */ + r_hist_entry *findDataPacketInRecvHistory(r_hist_entry *start); + + /* Detects packet loss in recv history. + * arg: seq_start, seq_end - the lost packet is in [start,end] + * win_count - window counter of packet before the loss + * ret: true if a packet loss was detected, otherwise false. + */ + bool detectLossRecv(u_int32_t *seq_start, u_int32_t *seq_end, u_int8_t *win_count); + + /* Detect packet marks in recv history before num_dup_ack limit. + * arg: seq_num - sequence number of marked packet (if any) + * win_count - win_count of marked packet (if any) + * ret: true if a packet mark was found, otherwise false. + */ + bool detectECNRecv(u_int32_t *seq_num, u_int8_t *win_count); + + /* Calculate the total amount of data in recent packets. + * arg: time - sum data in packets received later than this time + * ret: total amount of data + */ + u_int32_t sumPktSizes(double time); + + /* Sample the round trip time + * arg: rtt - the obtained rtt + * last_seq - sequence number of oldest packet used + * ret: true if the rtt could succesfully be estimated from wc + * false otherwise. Note that rtt above is always valid. + * Note: If not enough packets exist to use wc to estimate rtt, + * the rtt measured on handshake packets are used. + * The function will disregard packets with wc >= max_wc_inc_. + */ + bool sampleRTT(double *rtt, u_int32_t *last_seq); + + + /* Calculate the receive rate to send on feedbacks + * ret: the receive rate + */ + double calcXrecv(); + + /* Clear the loss event history */ + void clearLIHistory(); + + /* Print the loss event history */ + void printLIHistory(); + + /* Detects quiescence of the corresponding sender + * ret: true if sender is quiescent + * false otherwise + */ + bool detectQuiescence(); + + /* from project (slightly modified) */ + + /* Calculate the average loss interval + * ret: average loss interval (1/p) + */ + double tfrc_calcImean(); + + /* Prepare and send a feedback packet */ + void tfrc_recv_send_feedback(); + + /* Approximate the length of the first loss interval + * ret: the length of the first loss interval + */ + u_int32_t tfrc_recv_calcFirstLI(); + + /* Update the loss interval history */ + void tfrc_recv_updateLI(); + + /* Similar to recv_packetRecv */ + void tfrc_recv_packet_recv(Packet* pkt, int dataSize); + +protected: + + /* Methods inherited from DCCPAgent. + * See dccp.h For detailed information regarding arguments etc. + */ + + virtual void delay_bind_init_all(); + virtual int delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer); + + virtual void reset(); + virtual void cancelTimers(); + + /* Process incoming options. + * This function will process: + * ack vectors (verify ECN Nonce Echo) + * quiescence option + * rtt option + */ + virtual bool processOption(u_int8_t type, u_char* data, u_int8_t size, Packet *pkt); + + virtual void buildInitialFeatureList(); + + virtual int setFeature(u_int8_t num, dccp_feature_location location, + u_char* data, u_int8_t size, bool testSet = false); + + virtual int getFeature(u_int8_t num, dccp_feature_location location, + u_char* data, u_int8_t maxSize); + + virtual dccp_feature_type getFeatureType(u_int8_t num); + + virtual bool send_askPermToSend(int dataSize, Packet *pkt); + virtual void send_packetSent(Packet *pkt, bool moreToSend, int dataSize); + virtual void send_packetRecv(Packet *pkt, int dataSize); + virtual void recv_packetRecv(Packet *pkt, int dataSize); + + virtual void traceAll(); + virtual void traceVar(TracedVar* v); +public: + /* Constructor + * ret: a new DCCPTCPlikeAgent + */ + DCCPTFRCAgent(); + + /* Destructor */ + virtual ~DCCPTFRCAgent(); + + /* Process a "function call" from OTCl + * + * Supported function call handled by this agent: + * weights ++.... - set the weights for avg li calc. + * example $dccp weights 8+1.0+1.0+1.0+1.0+0.8+0.6+0.4+0.2 + */ + int command(int argc, const char*const* argv); + + /* A timeout has occured + * arg: tno - id of timeout event + * Handles: Send timer - DCCP_TFRC_TIMER_SEND + * No feedback timer - DCCP_TFRC_TIMER_NO_FEEDBACK + */ + virtual void timeout(int tno); + +}; +#endif + + + + + + + + + diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/dccp/dccp_types.h ns-2.34_dccp/dccp/dccp_types.h --- ns-2.34_orig/dccp/dccp_types.h 1970-01-01 01:00:00.000000000 +0100 +++ ns-2.34_dccp/dccp/dccp_types.h 2010-03-08 15:09:55.797597400 +0100 @@ -0,0 +1,100 @@ +/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ + +/* Copyright (c) 2004 Nils-Erik Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dccp_types.h,v 1.12 2004/02/23 18:05:31 nilmat-8 Exp $ */ + +#ifndef ns_dccp_types_h +#define ns_dccp_types_h + +#define DCCP_NUM_PTYPES 8 + +enum dccp_packet_type { + DCCP_REQUEST = 0, DCCP_RESPONSE = 1, DCCP_DATA = 2, + DCCP_ACK = 3, DCCP_DATAACK = 4, DCCP_CLOSEREQ = 5, + DCCP_CLOSE = 6, DCCP_RESET = 7 +}; + +#define DCCP_IP_HDR_SIZE 20 +#define DCCP_GEN_HDR_SIZE 12+DCCP_IP_HDR_SIZE + +#define DCCP_REQ_HDR_SIZE 4+DCCP_GEN_HDR_SIZE +#define DCCP_RESP_HDR_SIZE 4+DCCP_GEN_HDR_SIZE +#define DCCP_DATA_HDR_SIZE 0+DCCP_GEN_HDR_SIZE +#define DCCP_ACK_HDR_SIZE 4+DCCP_GEN_HDR_SIZE +#define DCCP_DACK_HDR_SIZE 4+DCCP_GEN_HDR_SIZE +#define DCCP_CREQ_HDR_SIZE 4+DCCP_GEN_HDR_SIZE +#define DCCP_CLOSE_HDR_SIZE 4+DCCP_GEN_HDR_SIZE +#define DCCP_RESET_HDR_SIZE 8+DCCP_GEN_HDR_SIZE + +#define DCCP_NDP_LIMIT 8 +#define DCCP_CCVAL_LIMIT 16 + +#define DCCP_NUM_RESET_REASONS 17 + +enum dccp_reset_reason { + DCCP_RST_UNSPEC = 0, DCCP_RST_CLOSED = 1, + DCCP_RST_INVALID_PKT = 2, DCCP_RST_OPT_ERR = 3, + DCCP_RST_FEATURE_ERR = 4, DCCP_RST_CONN_REF = 5, + DCCP_RST_BAD_SNAME = 6, DCCP_RST_TOO_BUSY = 7, + DCCP_RST_BAD_INITC = 8, DCCP_RST_UNANSW_CHAL = 10, + DCCP_RST_FLESS_NEG = 11, DCCP_RST_AGG_PEN = 12, + DCCP_RST_NO_CONN = 13, DCCP_RST_ABORTED = 14, + DCCP_RST_EXT_SEQ = 15, DCCP_RST_MANDATORY = 16 +}; + +#define DCCP_NUM_STATES 7 + +enum dccp_state { DCCP_STATE_CLOSED, DCCP_STATE_LISTEN, + DCCP_STATE_RESPOND, DCCP_STATE_REQUEST, + DCCP_STATE_OPEN, DCCP_STATE_CLOSEREQ, + DCCP_STATE_CLOSING //, DCCP_STATE_TIMEWAIT +}; + +#define DCCP_NUM_FEAT_LOCS 2 + +enum dccp_feature_location { DCCP_FEAT_LOC_LOCAL = 0, + DCCP_FEAT_LOC_REMOTE = 1 }; + +enum dccp_feature_type { DCCP_FEAT_TYPE_SP = 0, + DCCP_FEAT_TYPE_NN = 1, + DCCP_FEAT_TYPE_UNKNOWN = 3}; + +#define DCCP_NUM_PACKET_STATES 4 + +enum dccp_packet_state { DCCP_PACKET_RECV = 0, DCCP_PACKET_ECN = 1, + DCCP_PACKET_RESERVED = 2, DCCP_PACKET_NOT_RECV = 3}; + +enum dccp_ecn_codepoint { ECN_ECT0 = 0, ECN_ECT1 = 1, + ECN_NOT_ECT = 2, ECN_CE = 3 }; + +#endif + + + + + diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/Makefile.in ns-2.34_dccp/Makefile.in --- ns-2.34_orig/Makefile.in 2010-02-15 16:05:04.109989683 +0100 +++ ns-2.34_dccp/Makefile.in 2010-03-08 15:09:55.337596753 +0100 @@ -328,6 +328,13 @@ wpan/p802_15_4sscs.o wpan/p802_15_4timer.o \ wpan/p802_15_4trace.o wpan/p802_15_4transac.o \ apps/pbc.o \ + dccp/dccp_sb.o \ + dccp/dccp_opt.o \ + dccp/dccp_ackv.o \ + dccp/dccp_packets.o \ + dccp/dccp.o \ + dccp/dccp_tcplike.o \ + dccp/dccp_tfrc.o \ @V_STLOBJ@ diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/queue/errmodel.cc ns-2.34_dccp/queue/errmodel.cc --- ns-2.34_orig/queue/errmodel.cc 2010-02-15 16:05:14.499989952 +0100 +++ ns-2.34_dccp/queue/errmodel.cc 2010-03-08 15:09:55.797597400 +0100 @@ -194,7 +194,9 @@ if (markecn_) { hdr_flags* hf = hdr_flags::access(p); - hf->ce() = 1; + /* --- Altered to support the codepoints in rfc3168 --- */ + hf->ce() = 1; //set ce codepoint + hf->ect() = 1; } else if (delay_pkt_) { // Delay the packet. Scheduler::instance().schedule(target_, p, delay_); diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/tcl/lib/ns-default.tcl ns-2.34_dccp/tcl/lib/ns-default.tcl --- ns-2.34_orig/tcl/lib/ns-default.tcl 2010-02-15 16:05:12.929990064 +0100 +++ ns-2.34_dccp/tcl/lib/ns-default.tcl 2010-03-08 15:09:55.747597262 +0100 @@ -1463,3 +1463,123 @@ Agent/PBC set periodicBroadcastVariance 0.1 Agent/PBC set modulationScheme 0 + +# DCCP +# A description of each parameter can be found in the agents' header files +# i.e. dccp/dccp.h dccp/dccp_tcplike.h dccp/dccp_tfrc.h + +Agent/DCCP set packetSize_ 500 + +Agent/DCCP set initial_rtx_to_ 3.0 +Agent/DCCP set max_rtx_to_ 75.0 +Agent/DCCP set resp_to_ 75.0 + +Agent/DCCP set sb_size_ 1000 +Agent/DCCP set opt_size_ 512 +Agent/DCCP set feat_size_ 24 +Agent/DCCP set ackv_size_ 20 + +Agent/DCCP set ccid_ 0 +Agent/DCCP set use_ecn_local_ 0 +Agent/DCCP set use_ecn_remote_ 0 +Agent/DCCP set ack_ratio_local_ 2 +Agent/DCCP set ack_ratio_remote_ 2 +Agent/DCCP set use_ackv_local_ 0 +Agent/DCCP set use_ackv_remote_ 0 +Agent/DCCP set q_scheme_ 0 +Agent/DCCP set q_local_ 0 +Agent/DCCP set q_remote_ 0 + +Agent/DCCP set snd_delay_ 0.0001 + +Agent/DCCP set nam_tracevar_ false +Agent/DCCP set trace_all_oneline_ false + +Agent/DCCP set allow_mult_neg_ 0 +Agent/DCCP set ndp_limit_ 8 +Agent/DCCP set ccval_limit_ 16 + +Agent/DCCP set cscov_ 0 + +Agent/DCCP set num_data_pkt_ 0 +Agent/DCCP set num_ack_pkt_ 0 +Agent/DCCP set num_dataack_pkt_ 0 + +Agent/DCCP/TCPlike set ccid_ 2 +Agent/DCCP/TCPlike set use_ecn_local_ 1 +Agent/DCCP/TCPlike set use_ecn_remote_ 1 +Agent/DCCP/TCPlike set use_ackv_local_ 1 +Agent/DCCP/TCPlike set use_ackv_remote_ 1 + +Agent/DCCP/TCPlike set initial_cwnd_ 3 +Agent/DCCP/TCPlike set cwnd_timeout_ 1 +Agent/DCCP/TCPlike set initial_ssthresh_ 65535 +Agent/DCCP/TCPlike set cwnd_ 2 +Agent/DCCP/TCPlike set cwnd_frac_ 0 +Agent/DCCP/TCPlike set ssthresh_ 20 +Agent/DCCP/TCPlike set pipe_ 0 + +Agent/DCCP/TCPlike set initial_rto_ 3.0 +Agent/DCCP/TCPlike set min_rto_ 1.0 +Agent/DCCP/TCPlike set rto_ 3.0 +Agent/DCCP/TCPlike set srtt_ -1.0 +Agent/DCCP/TCPlike set rttvar_ 0.0 +Agent/DCCP/TCPlike set rtt_sample_ 0.0 +Agent/DCCP/TCPlike set alpha_ 0.125 +Agent/DCCP/TCPlike set beta_ 0.25 +Agent/DCCP/TCPlike set k_ 4 +Agent/DCCP/TCPlike set g_ 0.01 +Agent/DCCP/TCPlike set num_dup_acks_ 3 + +Agent/DCCP/TCPlike set q_min_t_ 0.2 +Agent/DCCP/TCPlike set q_opt_ratio_ 1 +Agent/DCCP/TCPlike set dack_delay_ 0.2 + +Agent/DCCP/TCPlike set ackv_size_lim_ 10 + +Agent/DCCP/TFRC set ccid_ 3 +Agent/DCCP/TFRC set use_ecn_local_ 1 +Agent/DCCP/TFRC set use_ecn_remote_ 1 +Agent/DCCP/TFRC set use_ackv_local_ 1 +Agent/DCCP/TFRC set use_ackv_remote_ 1 +Agent/DCCP/TFRC set use_loss_rate_local_ 1 +Agent/DCCP/TFRC set use_loss_rate_remote_ 1 +Agent/DCCP/TFRC set rtt_scheme_local_ 0 +Agent/DCCP/TFRC set rtt_scheme_remote_ 0 + +Agent/DCCP/TFRC set num_dup_acks_ 3 +Agent/DCCP/TFRC set p_tol_ 0.05 +Agent/DCCP/TFRC set win_count_per_rtt_ 4 +Agent/DCCP/TFRC set max_wc_inc_ 5 + +Agent/DCCP/TFRC set s_use_osc_prev_ 1 + +Agent/DCCP/TFRC set s_x_ 500.0 +Agent/DCCP/TFRC set s_x_inst_ 500.0 +Agent/DCCP/TFRC set s_x_recv_ 0.0 +Agent/DCCP/TFRC set s_r_sample_ 0.0 +Agent/DCCP/TFRC set s_rtt_ 0.0 +Agent/DCCP/TFRC set s_r_sqmean_ 0.0 + +Agent/DCCP/TFRC set s_smallest_p_ 0.00001 +Agent/DCCP/TFRC set s_rtt_q_ 0.9 +Agent/DCCP/TFRC set s_rtt_q2_ 0.9 +Agent/DCCP/TFRC set s_t_mbi_ 64.0 +Agent/DCCP/TFRC set s_os_time_gran_ 0.01 + +Agent/DCCP/TFRC set s_s_ 500 +Agent/DCCP/TFRC set s_initial_x_ 500.0 +Agent/DCCP/TFRC set s_initial_rto_ 2.0 +Agent/DCCP/TFRC set s_x_ 500.0 +Agent/DCCP/TFRC set s_x_inst_ 500.0 +Agent/DCCP/TFRC set s_x_recv_ 0.0 +Agent/DCCP/TFRC set s_r_sample_ 0.0 +Agent/DCCP/TFRC set s_rtt_ 0.0 +Agent/DCCP/TFRC set s_r_sqmean_ 0.0 +Agent/DCCP/TFRC set s_p_ 0.0 +Agent/DCCP/TFRC set s_q_opt_ratio_ 1 + +Agent/DCCP/TFRC set r_s_ 500 +Agent/DCCP/TFRC set r_rtt_ 0.0 +Agent/DCCP/TFRC set r_p_ 0.0 +Agent/DCCP/TFRC set q_min_t_ 0.2 diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/tcl/lib/ns-packet.tcl ns-2.34_dccp/tcl/lib/ns-packet.tcl --- ns-2.34_orig/tcl/lib/ns-packet.tcl 2010-02-15 16:05:12.959990036 +0100 +++ ns-2.34_dccp/tcl/lib/ns-packet.tcl 2010-03-08 15:09:55.747597262 +0100 @@ -150,6 +150,15 @@ TFRC # TFRC, transport protocol TFRC_ACK # TFRC, transport protocol XCP # XCP, transport protocol + DCCP # DCCP, transport protocol + DCCP_ACK # DCCP, transport protocol + DCCP_RESET # DCCP, transport protocol + DCCP_REQ # DCCP, transport protocol + DCCP_RESP # DCCP, transport protocol + DCCP_DATA # DCCP, transport protocol + DCCP_DATAACK # DCCP, transport protocol + DCCP_CLOSE # DCCP, transport protocol + DCCP_CLOSEREQ # DCCP, transport protocol # Application-Layer Protocols: Message # a protocol to carry text messages Ping # Ping diff -NurbB --exclude='*.o' --exclude='*~' --exclude='*.orig' --exclude=nodes.log ns-2.34_orig/trace/cmu-trace.cc ns-2.34_dccp/trace/cmu-trace.cc --- ns-2.34_orig/trace/cmu-trace.cc 2010-02-15 16:05:14.639989725 +0100 +++ ns-2.34_dccp/trace/cmu-trace.cc 2010-03-08 15:09:55.807595527 +0100 @@ -1086,7 +1086,7 @@ // return; struct hdr_mac802_11 *mh = HDR_MAC802_11(p); - char ptype[11]; + char ptype[14]; strcpy(ptype, ((ch->ptype() == PT_MAC) ? ( (mh->dh_fc.fc_subtype == MAC_Subtype_RTS) ? "RTS" :