[Tinyos-2-commits] CVS: tinyos-2.x/support/sdk/c/blip/libtcp Makefile, NONE, 1.1 circ.c, NONE, 1.1 circ.h, NONE, 1.1 tcplib.c, NONE, 1.1 tcplib.h, NONE, 1.1 test_circ.c, NONE, 1.1 test_server.c, NONE, 1.1

sdhsdh sdhsdh at users.sourceforge.net
Mon Jan 19 16:28:18 PST 2009


Update of /cvsroot/tinyos/tinyos-2.x/support/sdk/c/blip/libtcp
In directory ddv4jf1.ch3.sourceforge.com:/tmp/cvs-serv6583/support/sdk/c/blip/libtcp

Added Files:
	Makefile circ.c circ.h tcplib.c tcplib.h test_circ.c 
	test_server.c 
Log Message:
 - initial commit of blip (berkeley low-power ip) stack


--- NEW FILE: Makefile ---

GCC=gcc
CFLAGS=-I../include -I../driver/ -DPC -g


all: test_client test_server

test_circ: test_circ.c circ.c
	$(GCC) -o $@ $^

test_client: test_client.c  tcplib.h tcplib.c circ.c
	$(GCC) -o $@ $< tcplib.c circ.c ../driver/tun_dev.c ../lib6lowpan/ip_malloc.c ../lib6lowpan/in_cksum.c $(CFLAGS)

test_server: test_server.c  tcplib.h tcplib.c circ.c
	$(GCC) -o $@ $< tcplib.c circ.c ../driver/tun_dev.c ../lib6lowpan/ip_malloc.c ../lib6lowpan/in_cksum.c $(CFLAGS)

clean:
	rm -rf test_server test_circ
--- NEW FILE: circ.c ---

#include <stdio.h>
#include <stdint.h>
// #include <assert.h>
#include <string.h>

#include "tcplib.h"

struct circ_buf {
  uint8_t  *map;
  uint16_t  map_len;
  uint8_t  *data_start;
  uint8_t  *data_head;
  uint16_t  data_len;
  uint32_t  head_seqno;
};

int circ_buf_init(void *data, int len, uint32_t seqno, int incl_map) {
  struct circ_buf *b = (struct circ_buf *)data;
  int bitmap_len = ((len - sizeof(struct circ_buf)) / 9);
  // int data_len   = bitmap_len * 8;

  // printf("circ_buf_init: len: %i data_len: %i bitmap_len: %i\n", len, data_len, bitmap_len);
  // assert(bitmap_len + data_len + sizeof(struct circ_buf) <= len);

  if (len < sizeof(struct circ_buf))
    return -1;

  if (incl_map) {
    b->map     = (uint8_t *)(b + 1);
    b->map_len = bitmap_len;
    b->data_start  = b->map + bitmap_len;
    b->data_len= bitmap_len * 8;
    memset(b->map, 0, bitmap_len * 9);
  } else {
    b->map = NULL;
    b->map_len = 0;
    b->data_start = (uint8_t *)(b + 1);
    b->data_len = len - sizeof(struct circ_buf);
    memset(b->data_start, 0, b->data_len);
  }
  b->data_head   = b->data_start;
  b->head_seqno = seqno;

  // printf("circ_buf_init: buf: %p data_start: %p data_head: %p data_len: %i\n",
  // b, b->data_start, b->data_head, b->data_len);

  return 0;
}

#define BIT_SET(off,map)       map[(off)/8] |= (1 << (7 - ((off) % 8)))
#define BIT_UNSET(off,map)     map[(off)/8] &= ~(1 << (7 - ((off) % 8)))
#define BIT_ISSET(off, map)    map[(off)/8] & 1 << (7 - (off) % 8)

static void bitmap_mark(struct circ_buf *b, uint8_t *data, int len) {
  int offset = data - b->data_start;
  if (b->map_len == 0) return;
  while (len-- > 0) {
    BIT_SET(offset, b->map);
    offset = (offset + 1) % b->data_len;
  }
}

/* return the sequence number of the first byte of data in the buffer;
   this is what the stack can ACK. */
uint32_t circ_get_seqno(void *buf) {
  struct circ_buf *b = (struct circ_buf *)buf;
  return b->head_seqno;
}

void circ_set_seqno(void *buf, uint32_t seqno) {
  struct circ_buf *b = (struct circ_buf *)buf;
  b->head_seqno = seqno;
}

uint16_t circ_get_window(void *buf) {
  struct circ_buf *b = (struct circ_buf *)buf;
  return b->data_len;
}

/* read as many contiguous bytes from the head of the buffer as
 *   possible, and update the internal data structures to shorten the
 *  buffer 
 * 
 * buf:  the circular buffer
 * data: a pointer which will be updated with the location of the data
 * return: the number of bytes available
 */
int circ_buf_read_head(void *buf, char **data) {
  struct circ_buf *b = (struct circ_buf *)buf;
  int off = b->data_head - b->data_start;
  int rlen = 0;
  *data = b->data_head;
  while (BIT_ISSET(off, b->map) && off < b->data_len) {
    BIT_UNSET(off, b->map);
    rlen++;
    b->head_seqno++;
    b->data_head ++;
    if (b->data_head == b->data_start + b->data_len)
      b->data_head = b->data_start;
    off++;
  }
  return rlen;
}


static void get_ptr_off_1(struct circ_buf *b, uint32_t sseqno, int len,
                          uint8_t **writeptr, int *w_len) {
  uint8_t *endptr =  b->data_start + b->data_len;
  int offset;

  *writeptr = NULL;
  *w_len = 0;

  /* write up to either the end of the buffer */
  offset = sseqno - b->head_seqno;
  if (b->data_head + offset < endptr) {
    *w_len = len;
    *writeptr = b->data_head + offset;
    if (*writeptr + *w_len > endptr) {
      *w_len = endptr - *writeptr;
    }
  }
}

int circ_shorten_head(void *buf, uint32_t seqno) {
  struct circ_buf *b = (struct circ_buf *)buf;
  int offset = seqno - b->head_seqno;

  b->head_seqno = seqno;

  b->data_head += offset;
  while (b->data_head > b->data_start + b->data_len)
    b->data_head -= b->data_len;

  return 0;
}

int circ_buf_read(void *buf, uint32_t sseqno,
                  uint8_t *data, int len) {
  struct circ_buf *b = (struct circ_buf *)buf;
  uint8_t *readptr;
  int r_len, rc = 0;

  get_ptr_off_1(b, sseqno, len, &readptr, &r_len);
  memcpy(data, readptr, r_len);
  data += r_len;
  rc += r_len;
  
  if (r_len != len) {
    readptr = b->data_start;
    r_len = min(len - r_len, b->data_head - b->data_start);
    memcpy(data, readptr, r_len);
    rc += r_len;
  }
  return rc;
}

int circ_buf_write(void *buf, uint32_t sseqno,
                   uint8_t *data, int len) {
  struct circ_buf *b = (struct circ_buf *)buf;
  uint8_t *writeptr;
  int w_len;
  
  /* we can't write any bytes since we're trying to write too far
     ahead  */
  // printf("circ_buf_write: sseqno: %i head_seqno: %i len: %i\n",
  // sseqno, b->head_seqno, len);

  if (sseqno > b->head_seqno + b->data_len)
    return -1;

  if (sseqno < b->head_seqno) {
    /* old data, but already received */
    if (sseqno < b->head_seqno - len) return -1;
    /* a segment which overlaps with data we've already received */
    data += (b->head_seqno - sseqno);
    len  -= (b->head_seqno - sseqno);
    sseqno = b->head_seqno;
  }
  if (len == 0) return 0;

  // printf("circ_buf_write: buf: %p data_start: %p data_head: %p data_len: %i\n",
  // b, b->data_start, b->data_head, b->data_len);
  get_ptr_off_1(b, sseqno, len, &writeptr, &w_len);
  memcpy(writeptr, data, w_len);
  data += w_len;
  bitmap_mark(b, writeptr, w_len);

  if (w_len != len) {
    writeptr = b->data_start;
    w_len = min(len - w_len, b->data_head - b->data_start);
    memcpy(writeptr, data, w_len);
    bitmap_mark(b, writeptr, w_len);
    // printf("circ_buf_write (2): write: %p len: %i\n", writeptr, w_len);
  }
  return 0;
}

#ifdef PC             
void circ_buf_dump(void *buf) {
  struct circ_buf *b = (struct circ_buf *)buf;
  int i;
/*   printf("circ buf: %p\n\tmap: %p\n\tmap_len: %i\n\tdata_start: %p\n\t" */
/*          "data_head: %p\n\tdata_len: %i\n\thead_seqno: %i\n",  */
/*          b, b->map, */
/*          b->map_len, b->data_start, b->data_head, b->data_len, b->head_seqno); */

  for (i = 1; i <= b->data_len; i++) {
    if (BIT_ISSET(i-1, b->map))
      putc('x',stdout);
    else
      putc('_',stdout);
    if (i % 80 == 0 || i == b->data_len) {
      putc('\n',stdout);
    }
  }
}
#endif

--- NEW FILE: circ.h ---
#ifndef __CIRC_H_
#define __CIRC_H_

#include <stdint.h>

int circ_buf_init(void *data, int len, uint32_t seqno, int inc_map);


int circ_buf_write(void *buf, uint32_t sseqno,
                   uint8_t *data, int len);


int circ_buf_read(void *buf, uint32_t sseqno,
                  uint8_t *data, int len);


int circ_shorten_head(void *buf, uint32_t seqno);

/* read from the head of the buffer, moving the data pointer forward */
int circ_buf_read_head(void *buf, char **data);

void circ_buf_dump(void *buf);

void circ_set_seqno(void *buf, uint32_t seqno);

#endif

--- NEW FILE: tcplib.c ---

/* A nonblocking library-based implementation of TCP
 *
 * There are some things like timers which need to be handled
 * externally with callbacks.
 *
 */

#include <stdio.h>
#include <string.h>
#include "ip_malloc.h"
#include "in_cksum.h"
#include "6lowpan.h"
#include "ip.h"
#include "tcplib.h"
#include "circ.h"

static struct tcplib_sock *conns = NULL;

#define ONE_SEGMENT(X)  ((X)->mss)

uint16_t alloc_local_port() {
  return 32012;
}

static inline void conn_add_once(struct tcplib_sock *sock) {
  struct tcplib_sock *iter;

  for (iter = conns; iter != NULL; iter = iter->next) {
    if (iter == sock) break;
  }
  if (iter == NULL) {
    sock->next = conns;
    conns = sock;
  }

}
static int isInaddrAny(struct in6_addr *addr) {
  int i;
  for (i = 0; i < 8; i++)
    if (addr->s6_addr16[i] != 0) break;
  if (i != 8) return 0;
  return 1;
}

static struct tcplib_sock *conn_lookup(struct ip6_hdr *iph, 
                                       struct tcp_hdr *tcph) {
  struct tcplib_sock *iter;
  printfUART("looking up conns...\n");
  for (iter = conns; iter != NULL; iter = iter->next) {
    printf("conn lport: %i\n", ntohs(iter->l_ep.sin6_port));
    if (((memcmp(iph->ip6_dst.s6_addr, iter->l_ep.sin6_addr.s6_addr, 16) == 0) ||
         isInaddrAny(&iter->l_ep.sin6_addr)) &&
        tcph->dstport == iter->l_ep.sin6_port &&
        (iter->r_ep.sin6_port == 0 ||
         (memcmp(&iph->ip6_src, &iter->r_ep.sin6_addr, 16) == 0 &&
          tcph->srcport == iter->r_ep.sin6_port)))
      return iter;
  }
  return NULL;
}

static int conn_checkport(uint16_t port) {
  struct tcplib_sock *iter;

  for (iter = conns; iter != NULL; iter = iter->next) {
    if (iter->l_ep.sin6_port == port)
      return -1;
  }
  return 0;
}

struct tcp_hdr *find_tcp_hdr(struct split_ip_msg *msg) {
  if (msg->hdr.nxt_hdr == IANA_TCP) {
    return (struct tcp_hdr *)((msg->headers == NULL) ? msg->data :
                              msg->headers->hdr.data);
  }
}

static struct split_ip_msg *get_ipmsg(int plen) {
  struct split_ip_msg *msg = 
    (struct split_ip_msg *)ip_malloc(sizeof(struct split_ip_msg) + sizeof(struct tcp_hdr) + plen);
  if (msg == NULL) return NULL;
  memset(msg, 0, sizeof(struct split_ip_msg) + sizeof(struct tcp_hdr));
  msg->hdr.nxt_hdr = IANA_TCP;
  msg->hdr.plen = htons(sizeof(struct tcp_hdr) + plen);

  msg->headers = NULL;
  msg->data = (void *)(msg + 1);
  msg->data_len = sizeof(struct tcp_hdr) + plen;

  return msg;
}

static void __tcplib_send(struct tcplib_sock *sock,
                          struct split_ip_msg *msg) {
  struct tcp_hdr *tcph = find_tcp_hdr(msg);
  memcpy(&msg->hdr.ip6_dst, &sock->r_ep.sin6_addr, 16);

  sock->flags &= ~TCP_ACKPENDING;

  // sock->ackno = ntohl(tcph->ackno);

  tcph->srcport = sock->l_ep.sin6_port;
  tcph->dstport = sock->r_ep.sin6_port;
  tcph->offset = sizeof(struct tcp_hdr) * 4;
  tcph->window = htons(circ_get_window(sock->rx_buf));
  tcph->chksum = 0;
  tcph->urgent = 0;

  tcplib_send_out(msg, tcph);
}

static void tcplib_send_ack(struct tcplib_sock *sock, int fin_seqno, uint8_t flags) {
  struct split_ip_msg *msg = get_ipmsg(0);
      
  if (msg != NULL) {
    struct tcp_hdr *tcp_rep = (struct tcp_hdr *)(msg + 1);
    tcp_rep->flags = flags;

    tcp_rep->seqno = htonl(sock->seqno);
    tcp_rep->ackno = htonl(circ_get_seqno(sock->rx_buf) + 
                           (fin_seqno ? 1 : 0));
    __tcplib_send(sock, msg);
    ip_free(msg);
  }
}

static void tcplib_send_rst(struct ip6_hdr *iph, struct tcp_hdr *tcph) {
  struct split_ip_msg *msg = get_ipmsg(0);
      
  if (msg != NULL) {
    struct tcp_hdr *tcp_rep = (struct tcp_hdr *)(msg + 1);

    memcpy(&msg->hdr.ip6_dst, &iph->ip6_src, 16);

    tcp_rep->flags = TCP_FLAG_RST;

    tcp_rep->ackno = tcph->seqno + 1;
    tcp_rep->seqno = tcph->ackno;;

    tcp_rep->srcport = tcph->dstport;
    tcp_rep->dstport = tcph->srcport;
    tcp_rep->offset = sizeof(struct tcp_hdr) * 4;
    tcp_rep->window = 0;
    tcp_rep->chksum = 0;
    tcp_rep->urgent = 0;

    tcplib_send_out(msg, tcp_rep);

    ip_free(msg);
    
  }  
}

/* send all the data in the tx buffer, starting at sseqno */
static int tcplib_output(struct tcplib_sock *sock, uint32_t sseqno) {
  // the output size is the minimum of the advertised window and the
  // conjestion window.  of course, if we have less data we send even
  // less.
  int seg_size = min(sock->seqno - sseqno, sock->r_wind);
  seg_size = min(seg_size, sock->cwnd);
  while (seg_size > 0 && sock->seqno > sseqno) {
    // printf("sending seg_size: %i\n", seg_size);
    struct split_ip_msg *msg = get_ipmsg(seg_size);
    struct tcp_hdr *tcph;
    uint8_t *data;
    if (msg == NULL) return -1;
    tcph = (struct tcp_hdr *)(msg + 1);
    data = (uint8_t *)(tcph + 1);

    tcph->flags = TCP_FLAG_ACK;
    tcph->seqno = htonl(sseqno);
    tcph->ackno = htonl(circ_get_seqno(sock->rx_buf));

    circ_buf_read(sock->tx_buf, sseqno, data, seg_size);
    __tcplib_send(sock, msg);
    ip_free(msg);

    sseqno += seg_size;
    seg_size = min(sock->seqno - sseqno, sock->mss);
  }
  return 0;
}

int tcplib_init_sock(struct tcplib_sock *sock) {
  memset(sock, 0, sizeof(struct tcplib_sock) - sizeof(struct tcplib_sock *));
  sock->mss = 100;
  sock->cwnd = ONE_SEGMENT(sock);
  sock->ssthresh = 0xffff;
  conn_add_once(sock);
  return 0;
}

/* called when a new segment arrives. */
/* deliver as much data to the app as possible, and update the ack
 * number of the socket to reflect how much was delivered 
 */
static void add_data(struct tcplib_sock *sock, struct tcp_hdr *tcph, int len) {
  char *ptr;
  int payload_len;
  ptr = ((uint8_t *)tcph) + (tcph->offset / 4);
  payload_len = len - (tcph->offset / 4);
  // TODO : SDH : optimize out the extra copy for in-sequence data
  circ_buf_write(sock->rx_buf, ntohl(tcph->seqno),
                 ptr, payload_len);
  // now try to deliver any data ahead of the ack pointer that's in
  // the buffer

  /* if we wrapped around the buffer, we'll actually recieve twice.  */
  while ((payload_len = circ_buf_read_head(sock->rx_buf, &ptr)) > 0) {
    sock->ops.recvfrom(sock, ptr, payload_len);
  }
}

static void reset_ssthresh(struct tcplib_sock *conn) {
  uint16_t new_ssthresh = min(conn->cwnd, conn->r_wind) / 2;
  if (new_ssthresh < 2 * ONE_SEGMENT(conn))
    new_ssthresh = 2 * ONE_SEGMENT(conn);
  conn->ssthresh = new_ssthresh;
}

int tcplib_process(struct ip6_hdr *iph, void *payload) {
  int rc = 0;
  struct tcp_hdr *tcph;
  struct tcplib_sock *this_conn;
  //   uint8_t *ptr;
  int payload_len;
  int len = ntohs(iph->plen) + sizeof(struct ip6_hdr);;
  tcph = (struct tcp_hdr *)payload;

  // printf("tcplib_process\n");

  /* malformed ip packet?  could happen I supppose... */
/*   if (len < sizeof(struct ip6_hdr) || */
/*       len != ntohs(iph->plen) + sizeof(struct ip6_hdr)) { */
/*     fprintf(stderr, "tcplib_process: warn: length mismatch\n"); */
/*     return -1; */
/*   } */

  /* if there's no local */
  this_conn = conn_lookup(iph, tcph);
  if (this_conn != NULL) {
    if (tcph->flags & TCP_FLAG_RST) {
      /* Really hose this connection if we get a RST packet.
       * still TODO: RST generation for unbound ports */
      printf("connection reset by peer\n");
          
      if (this_conn->ops.closed)
        this_conn->ops.close_done(this_conn);
      tcplib_init_sock(this_conn);
      return 0;
    }
    // always get window updates from new segments
    // TODO : this should be after we detect out-of-sequence ACK
    // numbers!
    this_conn->r_wind = ntohs(tcph->window);

    switch (this_conn->state) {
    case TCP_LAST_ACK:
      if (tcph->flags & TCP_FLAG_ACK && 
          ntohl(tcph->ackno) == this_conn->seqno + 1) {
        // printf("closing connection\n");
        this_conn->state = TCP_CLOSED;
        this_conn->ops.close_done(this_conn);
        break;
      }

    case TCP_SYN_SENT:
      if (tcph->flags & (TCP_FLAG_SYN | TCP_FLAG_ACK)) {
        // got a syn-ack
        // send the ACK 
        this_conn->state = TCP_ESTABLISHED;
        circ_set_seqno(this_conn->rx_buf, ntohl(tcph->seqno) + 1);
        // skip the LISTEN processing
        // this will also generate an ACK
        goto ESTABLISHED;
      } else if (this_conn->flags & TCP_FLAG_SYN) {
      // otherwise the state machine says we're in a simultaneous open, so continue doen
        this_conn->state = TCP_SYN_RCVD;
      } else {
        printf("sending RST on bad data in state SYN_SENT\n");
        tcplib_send_rst(iph, tcph);
        break;
      }
    case TCP_SYN_RCVD:
    case TCP_LISTEN:
      /* not connected. */
      if (tcph->flags & TCP_FLAG_SYN) {
        struct tcplib_sock *new_sock;

        if (this_conn->state == TCP_LISTEN) {
          memcpy(&this_conn->r_ep.sin6_addr, &iph->ip6_src, 16);
          this_conn->r_ep.sin6_port = tcph->srcport;
          new_sock = tcplib_accept(this_conn, &this_conn->r_ep);
          if (new_sock != this_conn) {
            memset(this_conn->r_ep.sin6_addr.s6_addr, 0, 16);
            this_conn->r_ep.sin6_port = 0;
            if (new_sock != NULL) {
              memcpy(&new_sock->r_ep.sin6_addr, &iph->ip6_src, 16);
              new_sock->r_ep.sin6_port = tcph->srcport;
              conn_add_once(new_sock);
            }
          }
          if (new_sock == NULL) {
            tcplib_send_rst(iph, tcph);
            break;
          }
          memcpy(&new_sock->l_ep.sin6_addr, &iph->ip6_dst, 16);
          new_sock->l_ep.sin6_port = tcph->dstport;

          circ_buf_init(new_sock->rx_buf, new_sock->rx_buf_len, 
                        ntohl(tcph->seqno) + 1, 1);
          circ_buf_init(new_sock->tx_buf, new_sock->tx_buf_len,
                        0xcafebabe + 1, 0);
        } else {
          /* recieved a SYN retransmission. */
          new_sock = this_conn;
        }

        if (new_sock != NULL) {
          new_sock->seqno = 0xcafebabe + 1;
          new_sock->state = TCP_SYN_RCVD;
          tcplib_send_ack(new_sock, 0, TCP_FLAG_ACK | TCP_FLAG_SYN);
          new_sock->seqno++;
        } else {
          memset(&this_conn->r_ep, 0, sizeof(struct sockaddr_in6));
        }
      } else if (this_conn->state == TCP_LISTEN) {
        printf("sending RST on out-of-sequence data\n");
        tcplib_send_rst(iph, tcph);
        break;
      }
      /* this is SYN_RECVd */
      if (tcph->flags & TCP_FLAG_ACK) {
        // printf("recv ack, in state TCP_SYN_RCVD\n");
        this_conn->state = TCP_ESTABLISHED;
      } 
      /* fall through to handle any data. */
      
    case TCP_CLOSE_WAIT:
    case TCP_ESTABLISHED:
    ESTABLISHED:
      // ptr = ((uint8_t *)(iph + 1)) + (tcph->offset / 4);
      payload_len = len - sizeof(struct ip6_hdr) - (tcph->offset / 4);
      // printf("recv data len: %i\n", payload_len);

      /* ack any data in this packet */
      if (this_conn->state == TCP_ESTABLISHED) {
        if (payload_len > 0)
          this_conn->flags ++;


        // receive side sequence check and add data
        // printf("seqno: %i ackno: %i\n", ntohl(tcph->seqno), ntohl(tcph->ackno));


        // send side recieve sequence check and congestion window updates.
        if (ntohl(tcph->ackno) > circ_get_seqno(this_conn->tx_buf)) {
          // new data is being ACKed
          // or we haven't sent anything new
          if (this_conn->cwnd <= this_conn->ssthresh) {
            // in slow start; increase the cwnd by one segment
            this_conn->cwnd += ONE_SEGMENT(this_conn);
            // printf("in slow start\n");
          } else {
            // in congestion avoidance
            this_conn->cwnd += (ONE_SEGMENT(this_conn) * ONE_SEGMENT(this_conn)) / this_conn->cwnd;
            // printf("in congestion avoidence\n");
          }
          // printf("ACK new data: cwnd: %i ssthresh: %i\n", this_conn->cwnd, this_conn->ssthresh);
          // reset the duplicate ack counter
          UNSET_ACK_COUNT(this_conn->flags);
          // truncates the ack buffer 
          circ_shorten_head(this_conn->tx_buf, ntohl(tcph->ackno));
          // printf("ack_count: %i\n", GET_ACK_COUNT(this_conn->flags));
        } else if (this_conn->seqno > circ_get_seqno(this_conn->tx_buf)) {
          // this is a duplicate ACK
          //  - increase the counter of the number of duplicate ACKs
          //  - if we get to three duplicate ACK's, start resending at
          //    the ACK number because this probably means we lost a segment

          INCR_ACK_COUNT(this_conn->flags);
          // printf("ack_count: %i\n", GET_ACK_COUNT(this_conn->flags));
          // printf("dup ack count: %i\n", GET_ACK_COUNT(this_conn->flags));
          // a "dup ack count" of 2 is really 3 total acks because we start with zero
          if (GET_ACK_COUNT(this_conn->flags) == 2) {
            UNSET_ACK_COUNT(this_conn->flags);
            printf("detected multiple duplicate ACKs-- doing fast retransmit [%u, %u]\n",
                   circ_get_seqno(this_conn->tx_buf),
                   this_conn->seqno);

            // this is our detection of a "duplicate ack" event.
            // we are going to reset ssthresh and retransmit the data.
            reset_ssthresh(this_conn);
            tcplib_output(this_conn, circ_get_seqno(this_conn->tx_buf));
            this_conn->timer.retx = 6;
            
          }
        } else if (ntohl(tcph->seqno) != circ_get_seqno(this_conn->rx_buf)) {
          printf("==> received out-of-sequence data!\n");
          tcplib_send_ack(this_conn, 0, TCP_FLAG_ACK);
        }
        add_data(this_conn, tcph, len - sizeof(struct ip6_hdr));


        // printf("tx seqno: %i ackno: %i\n", circ_get_seqno(this_conn->tx_buf),
        // ntohl(tcph->ackno));


        // reset the retransmission timer
        if (this_conn->timer.retx == 0)
          this_conn->timer.retx = 6;
      }
      if ((payload_len > 0 && (this_conn->flags & TCP_ACKPENDING) >= 2) 
          || tcph->flags & TCP_FLAG_FIN) {
        ///|| ntohl(tcph->seqno) != circ_get_seqno(this_conn->rx_buf)) {
        tcplib_send_ack(this_conn, (payload_len == 0 && tcph->flags & TCP_FLAG_FIN), TCP_FLAG_ACK);
        /* only close the connection if we've gotten all the data */
        if (this_conn->state == TCP_ESTABLISHED 
            && tcph->flags & TCP_FLAG_FIN
            && ntohl(tcph->seqno)  == circ_get_seqno(this_conn->rx_buf)) {
          this_conn->state = TCP_CLOSE_WAIT;
          this_conn->ops.closed(this_conn);
        }
      }
      break;
    case TCP_CLOSED:
    default:
      rc = -1;
      // printf("sending RST\n");
      // tcplib_send_ack(this_conn, 0, TCP_FLAG_ACK | TCP_FLAG_RST);
    }
  } else {
    /* this_conn was NULL */
    /* TODO : SDH : send ICMP error */
  }
  return rc;
}


/* bind the socket to a local address */
int tcplib_bind(struct tcplib_sock *sock,
                struct sockaddr_in6 *addr) {
  /* not using an already-bound port */
  /* TODO : SDH : check local address */
  if (conn_checkport(addr->sin6_port))
    return -1;
  
  memcpy(&sock->l_ep, addr, sizeof(struct sockaddr_in6));
  /* passive open */
  sock->state = TCP_LISTEN;
}

/* connect the socket to a remote endpoint */
int tcplib_connect(struct tcplib_sock *sock,
                   struct sockaddr_in6 *serv_addr) {
  if (sock->rx_buf == NULL || sock->tx_buf == NULL)
    return -1;

  switch (sock->state) {
  case TCP_CLOSED:
    // passive open; need to set up the local endpoint.
    memset(&sock->l_ep, 0, sizeof(struct sockaddr_in6));
    sock->l_ep.sin6_port = htons(alloc_local_port());
    break;
  case TCP_LISTEN:
    // we got here by calling bind, so we're cool.
    break;
  default:
    return -1;
  }
  circ_buf_init(sock->rx_buf, sock->rx_buf_len, 
                0, 1);
  circ_buf_init(sock->tx_buf, sock->tx_buf_len,
                0xcafebabe + 1, 0);

  sock->seqno = 0xcafebabe;
  memcpy(&sock->r_ep, serv_addr, sizeof(struct sockaddr_in6));
  tcplib_send_ack(sock, 0, TCP_FLAG_SYN);
  sock->state = TCP_SYN_SENT;
  sock->seqno++;
  return 0;
}


int tcplib_send(struct tcplib_sock *sock, void *data, int len) {
  /* have enough tx buffer left? */
  if (sock->state != TCP_ESTABLISHED)
    return -1;
  if (sock->seqno - circ_get_seqno(sock->tx_buf) + len > circ_get_window(sock->tx_buf))
    return -1;

  circ_buf_write(sock->tx_buf, sock->seqno, data, len);

  sock->seqno += len;
  // printf("tcplib_output from send\n");
  tcplib_output(sock, sock->seqno - len);
  
  // 3 seconds
  if (sock->timer.retx == 0)
    sock->timer.retx = 6;

  return 0;
}

void tcplib_retx_expire(struct tcplib_sock *sock) {
  // printf("retransmission timer expired!\n");
  if (sock->state == TCP_ESTABLISHED &&
      circ_get_seqno(sock->tx_buf) != sock->seqno) {
    printf("retransmitting [%u, %u]\n", circ_get_seqno(sock->tx_buf),
           sock->seqno);
    reset_ssthresh(sock);
    // restart slow start
    sock->cwnd = ONE_SEGMENT(sock);
    // printf("tcplib_output from timer\n");
    tcplib_output(sock, circ_get_seqno(sock->tx_buf));
    sock->timer.retx = 6;
  } else if (sock->state == TCP_LAST_ACK) {
    //     printf("resending FIN\n");
    tcplib_send_ack(sock, 1, TCP_FLAG_ACK | TCP_FLAG_FIN);
    sock->timer.retx = 6;
  }
}

int tcplib_close(struct tcplib_sock *sock) {
  int rc = -1;

  switch (sock->state) {
    /* passive close */
  case TCP_CLOSE_WAIT:
    tcplib_send_ack(sock, 1, TCP_FLAG_ACK | TCP_FLAG_FIN);
    sock->timer.retx = 6;
    sock->state = TCP_LAST_ACK;
    break;
    /* active close */
  case TCP_ESTABLISHED:
    // kick off the close
    
    break;
  case TCP_SYN_SENT:
    sock->state = TCP_CLOSED;
    break;
  default:
    /* this is meaningless in other states */
    rc = -1;
  }
  return rc;
}

int tcplib_timer_process() {
  struct tcplib_sock *iter;
  for (iter = conns; iter != NULL; iter = iter->next) {
    if (iter->timer.retx > 0 && (--iter->timer.retx) == 0)
      tcplib_retx_expire(iter);
    if (iter->flags & TCP_ACKPENDING) {
      // printf("sending delayed ACK\n");
      tcplib_send_ack(iter, 0, TCP_FLAG_ACK);
    }
  }
  return 0;
}

--- NEW FILE: tcplib.h ---
#ifndef TCPLIB_H_
#define TCPLIB_H_

// #include <netinet/in.h>
#include "ip.h"

#define min(X,Y) (((X) > (Y)) ? (Y) : (X))
#ifndef PC
#define printf(X, args ...) dbg("stdout", X, ## args)
#define fprintf(X, Y, args ...) dbg("fprintf", Y, ## args)
#endif

typedef enum {
  TCP_CLOSED = 0,
  TCP_LISTEN,
  TCP_SYN_RCVD,
  TCP_SYN_SENT,
  TCP_ESTABLISHED,
  TCP_CLOSE_WAIT,
  TCP_LAST_ACK,
  TCP_FIN_WAIT_1,
  TCP_FIN_WAIT_2,
  TCP_CLOSING,
  TCP_TIME_WAIT,
} tcplib_sock_state_t;

enum {
  TCP_ACKPENDING  = 0x3,
  TCP_DUPACKS     = 0x3c,
  TCP_DUPACKS_OFF = 2,
};

#define GET_ACK_COUNT(X)    (((X) & TCP_DUPACKS) >> TCP_DUPACKS_OFF)
#define UNSET_ACK_COUNT(X)  ((X) &= ~TCP_DUPACKS)
#define INCR_ACK_COUNT(X)   ((X) += 1 << TCP_DUPACKS_OFF)
#define ACK_COUNT_IS_3(X)   (((X) & TCP_DUPACKS) == TCP_DUPACKS)

struct tcplib_sock {
  uint8_t flags;
  
  /* local and remote endpoints */
  struct sockaddr_in6 l_ep;
  struct sockaddr_in6 r_ep;

  /* current connection state */
  tcplib_sock_state_t state;

  /* a buffer allocated for data on this connection */
  void    *rx_buf;
  uint16_t rx_buf_len;

  void    *tx_buf;
  uint16_t tx_buf_len;

  /* max segment size, or default if
     we didn't bother to pull it out
     of the options field */
  uint16_t mss;
  /* the window the other end is
     reporting */
  uint16_t r_wind;
  uint16_t cwnd;
  uint16_t ssthresh;

  // the current next sequence number for ourgoing data.
  // the ack number is stored in the receive buffer.
  uint32_t seqno;
  // uint32_t ackno;

  struct {
    int8_t retx;
  } timer;

  /* callbacks for this connection */
  struct {
    /* a previous connection request has finished */
    void (*connect_done)(struct tcplib_sock *sock, int error);

    /* a callback to signal new data is ready */
    void (*recvfrom)(struct tcplib_sock *sock, void *data, int len);

    /* the connection was closed by the other party */
    void (*closed)(struct tcplib_sock *sock);

    /* you called close(); we've finished closing the socket. */
    void (*close_done)(struct tcplib_sock *sock);
  } ops;

  /* this needs to be at the end so
     we can call init() on a socket
     without blowing away the linked
     list */
  struct tcplib_sock *next;
};

/* EVENTS 
 * ------------------------------------------------------------ 
 *
 * calls generated by tcplib that must be dealt with elsewhere in the
 * program.
 */


/* called when a new connection request is recieved on a socket which
 * is LISTENing.
 *
 *
 * return 0 if it wants to accept the connection and allocated a
 * buffer for it; -1 otherwise.
 */
struct tcplib_sock *tcplib_accept(struct tcplib_sock *conn,
                                  struct sockaddr_in6 *from);

/* a call-out point for tcplib to send a message */
void tcplib_send_out(struct split_ip_msg *msg, struct tcp_hdr *tcph);

/* upcall for new data; may be dispatched all the way out to a
 * handler. 
 *
 * Returns: 0 on success,
 * -1 otherwise.  The error may be safely ignored.
 */
int tcplib_process(struct ip6_hdr *ip_packet, void *payload);  

/*
 * should be called every 500ms to increment all the tcp timers
 */
int tcplib_timer_process();

/* Just fill in the fields of the socket.
 *
 * If you perform a send on a socket in this state, an ephemeral port
 * will be allocated to it.
 *
 * This must be called once on any socket that might be sent on, or
 * might have bind() called.
 */
int tcplib_init_sock(struct tcplib_sock *sock);

/* bind the socket to a local address */
int tcplib_bind(struct tcplib_sock *sock,
                struct sockaddr_in6 *addr);

/* connect the socket to a remote endpoint */
int tcplib_connect(struct tcplib_sock *sock,
                   struct sockaddr_in6 *serv_addr);


/* send data on an open socket.
 *
 * returns: 0 on success
 *   other errors
 *     - no local buffer is available,
 *     
 */
int tcplib_send(struct tcplib_sock *sock,
                 void *data, int len);

int tcplib_close(struct tcplib_sock *sock);


#endif

--- NEW FILE: test_circ.c ---

#include <stdio.h>
#include "circ.h"

void do_head_read(void *buf) {
  char *read_data;
  int i, data_len;
  data_len = circ_buf_read_head(buf, (void **)&read_data);
  printf("buf_read_head: %i\n", data_len);
  for (i = 0; i < data_len; i++)
    putc(((char *)read_data)[i], stdout);
  putc('\n', stdout);
}

void do_read(void *buf, uint32_t sseqno) {
  char data[20];
  int data_len, i;
  data_len = circ_buf_read(buf, sseqno, data, 20);

  printf("buf_read: %i\n", data_len);
  for (i = 0; i < data_len; i++)
    putc(((char *)data)[i], stdout);
  putc('\n', stdout);

}

int main(int argc, char **argv) {
  char buf[200];
  char data[20], readbuf[30];
  int i = 20, data_len;
  char *read_data;
  if (circ_buf_init(buf, 200, 0, 1) < 0)
    printf("cir_buf_init: error\n");

  for (i=0;i<20;i++)
    data[i] = 'a' + i;

  if (circ_buf_write(buf, 0, data, 20) < 0)
    printf("circ_buf_write: error\n");

  if (circ_buf_write(buf, 10, data, 20) < 0)
    printf("circ_buf_write: error\n");


  if (circ_buf_write(buf, 50, data, 20) < 0)
    printf("circ_buf_write: error\n");

  circ_buf_dump(buf);

  do_head_read(buf);
  circ_buf_dump(buf);

  if (circ_buf_write(buf, 30, data, 20) < 0)
    printf("circ_buf_write: error\n");

  circ_buf_dump(buf);

  if (circ_buf_write(buf, 70, data, 20) < 0)
    printf("circ_buf_write: error\n");

  circ_buf_dump(buf);

  do_read(buf, 50);
/*   do_head_read(buf); */
/*   circ_buf_dump(buf); */

/*   if (circ_buf_write(buf, 90, data, 20) < 0) */
/*     printf("circ_buf_write: error\n"); */
/*   if (circ_buf_write(buf, 110, data, 20) < 0) */
/*     printf("circ_buf_write: error\n"); */
/*   if (circ_buf_write(buf, 130, data, 20) < 0) */
/*     printf("circ_buf_write: error\n"); */

/*   circ_buf_dump(buf); */
/*   do_head_read(buf); */
/*   do_head_read(buf); */
/*   circ_buf_dump(buf); */
}

--- NEW FILE: test_server.c ---

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if.h>
#include <signal.h>
#include <string.h>


#include "ip.h"
#include "tcplib.h"
#include "tun_dev.h"
#include "ip_malloc.h"


#define BUFSZ 1024
#define LOSS_RATE_RECPR 100

int sock = 0;
uint8_t iface_addr[16] = {0x20, 0x01, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
                          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};

struct sockaddr_in6 laddr;


void printBuf(uint8_t *buf, uint16_t len) {
  int i;
  // print("len: %i: ", len);
  for (i = 1; i <= len; i++) {
    printf(" 0x%02x", buf[i-1]);
    // if (i % 16 == 0) printf("\n");
  }
  printf("\n");
}

void print_split_msg(struct split_ip_msg *msg) {
  int i;
  printf("src_addr: ");
  for (i = 0; i < 16; i++) printf("0x%x ", msg->hdr.src_addr[i]);
  printf("\ndst_addr: ");
  for (i = 0; i < 16; i++) printf("0x%x ", msg->hdr.dst_addr[i]);
  printf("\nplen: %i hlim: %i\n", ntohs(msg->hdr.plen), msg->hdr.hlim);

  printBuf(msg->data, msg->data_len);
}

void rx(struct tcplib_sock *sock, void *data, int len) {
  // printBuf(data, len);
  if (tcplib_send(sock, data, len) < 0)
    printf("tcplib_send: fail\n");
}

void cl(struct tcplib_sock *sock) {
  printf("remote conn closed\n");
  tcplib_close(sock);
}

void cd(struct tcplib_sock *sock) {
  printf("local close done\n");
  free(sock->rx_buf);
  free(sock->tx_buf);
  tcplib_init_sock(sock);
/*   printf("rebinding...\n"); */
}

/* called when a new connection request is received: not 
 *
 * return: a tcplib_struc, with the ops table filled in and send and
 * receive buffers allocated.
 */

struct tcplib_sock *tcplib_accept(struct tcplib_sock *conn,
                                  struct sockaddr_in6 *from) {
  printf("tcplib_accept\n");
  conn->rx_buf = malloc(BUFSZ);
  conn->rx_buf_len = BUFSZ;
  
  conn->tx_buf = malloc(BUFSZ);
  conn->tx_buf_len = BUFSZ;

  conn->ops.recvfrom = rx;
  conn->ops.closed = cl;
  conn->ops.close_done = cd;

  return conn;
}

void tcplib_send_out(struct split_ip_msg *msg, struct tcp_hdr *tcph) {
  uint8_t buf[8192];
  if (sock <= 0) return;

  memcpy(msg->hdr.src_addr, iface_addr, 16);
  msg->hdr.src_addr[15] = 2;
  msg->hdr.hlim = 64;

  memset(msg->hdr.vlfc, 0, 4);
  msg->hdr.vlfc[0] = 6 << 4;

  tcph->chksum = msg_cksum(msg, IANA_TCP);
  
  // print_split_msg(msg);
  if (rand() % LOSS_RATE_RECPR == 0) {
    printf("dropping packet on write\n");
  } else {
    tun_write(sock, msg);
  }
}

/* practice accepting connections and transfering data */
int main(int argg, char **argv) {
  char buf[8192], dev[IFNAMSIZ];
  uint8_t *payload;
  int len, i, flags;

  ip_malloc_init();

  payload = buf + sizeof(struct tun_pi);
  dev[0] = 0;
  if ((sock = tun_open(dev)) < 0) 
    exit(1);

  if (tun_setup(dev, iface_addr) < 0)
    exit(1);

  /* tun_setup turns on non-blocking IO.  Turn it off. */
  flags = fcntl(sock, F_GETFL);
  flags &= ~O_NONBLOCK;
  fcntl(sock,F_SETFL, flags);

  struct tcplib_sock srv_sock;
  tcplib_init_sock(&srv_sock);
  memcpy(laddr.sin6_addr.s6_addr, iface_addr, 16);
  laddr.sin6_addr.s6_addr[15] = 2;
  laddr.sin6_port = htons(atoi(argv[1]));

  tcplib_bind(&srv_sock, &laddr);

  fd_set fds;
  struct timeval timeout;
  FD_ZERO(&fds);
  FD_SET(sock, &fds);

  timeout.tv_sec = 0;
  timeout.tv_usec = 500000;

  while (select(sock + 1, &fds, NULL, NULL, &timeout) >= 0) {
    if (FD_ISSET(sock, &fds)) {
      if ((len = read(sock, buf, 8192)) <= 0) break;
      struct ip6_hdr *iph = (struct ip6_hdr *)payload;
      if (iph->nxt_hdr == IANA_TCP) {
        if (rand() % LOSS_RATE_RECPR == 0) {
          printf("dropping packet on rx\n");
        } else {
          if (tcplib_process(payload, len - sizeof(struct tun_pi)))
            printf("TCPLIB_PROCESS: ERROR!\n");
        }
      }
    } else {
      timeout.tv_sec = 0;
      timeout.tv_usec = 500000;
      tcplib_timer_process();
    }
    if (srv_sock.state == TCP_CLOSED) {
      tcplib_bind(&srv_sock, &laddr);
    }

    FD_ZERO(&fds);
    FD_SET(sock, &fds);
  }
  tun_close(sock, dev);
}



More information about the Tinyos-2-commits mailing list