RECEIVE PACKET
--------------
-- Drop invalid packets:
- - Invalid flags or state
- - ACK always set
- - hdr.seq not within our receive window
- - hdr.ack ahead of snd.nxt
-- Handle RST packets
-- Advance snd.una?
- - reset conn timer if so
- - remove ACKed data from send buffer
-- If snd.una == snd.nxt, clear rtrx and conn timer
-- Process state changes due to SYN
-- Send new data to application
-- Process state changes due to FIN
+1 Drop invalid packets:
+ a Invalid flags or state
+ b ACK always set
+ c hdr.seq not within our receive window
+ d hdr.ack ahead of snd.nxt or behind snd.una
+2 Handle RST packets
+3 Advance snd.una?
+ a reset conn timer if so
+ b check if our SYN or FIN has been acked
+ c check if any data been acked
+ - remove ACKed data from send buffer
+ - increase cwnd
+ d no advance? NewReno
+4 If snd.una == snd.nxt, clear rtrx and conn timer
+5 Process state changes due to SYN
+6 Send new data to application
+7 Process state changes due to FIN
CONGESTION AVOIDANCE
--------------------
struct utcp *b;
struct utcp_connection *c;
-int do_recv(struct utcp_connection *x, const void *data, size_t len) {
+ssize_t do_recv(struct utcp_connection *x, const void *data, size_t len) {
if(!len) {
if(errno)
fprintf(stderr, "%p Error: %s\n", x->utcp, strerror(errno));
}
if(x == c)
- write(0, data, len);
+ return write(0, data, len);
else
- utcp_send(x, data, len);
-
- return 0;
+ return utcp_send(x, data, len);
}
bool do_pre_accept(struct utcp *utcp, uint16_t port) {
utcp_accept(c, do_recv, NULL);
}
-int do_send(struct utcp *utcp, const void *data, size_t len) {
+ssize_t do_send(struct utcp *utcp, const void *data, size_t len) {
static int count = 0;
if(++count > 1000) {
fprintf(stderr, "Too many packets!\n");
double dropin;
double dropout;
-int do_recv(struct utcp_connection *c, const void *data, size_t len) {
+ssize_t do_recv(struct utcp_connection *c, const void *data, size_t len) {
if(!data || !len) {
if(errno) {
fprintf(stderr, "Error: %s\n", strerror(errno));
dir &= ~2;
fprintf(stderr, "Connection closed by peer\n");
}
- return 0;
+ return -1;
}
return write(1, data, len);
}
c = nc;
}
-int do_send(struct utcp *utcp, const void *data, size_t len) {
+ssize_t do_send(struct utcp *utcp, const void *data, size_t len) {
int s = *(int *)utcp->priv;
if(drand48() >= dropout)
return send(s, data, len, MSG_DONTWAIT);
else
- return 0;
+ return len;
}
int main(int argc, char *argv[]) {
if(!u)
return 1;
+ utcp_set_mtu(u, 1300);
utcp_set_user_timeout(u, 10);
if(!server)
{.fd = s, .events = POLLIN | POLLERR | POLLHUP},
};
- char buf[1024];
+ char buf[102400];
int timeout = utcp_timeout(u);
while(dir) {
#define _GNU_SOURCE
+#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
}
memcpy(&hdr, pkt, sizeof hdr);
- fprintf (stderr, "%p %s: src=%u dst=%u seq=%u ack=%u wnd=%u ctl=", utcp, dir, hdr.src, hdr.dst, hdr.seq, hdr.ack, hdr.wnd);
+ fprintf (stderr, "%p %s: len=%zu, src=%u dst=%u seq=%u ack=%u wnd=%u ctl=", utcp, dir, len, hdr.src, hdr.dst, hdr.seq, hdr.ack, hdr.wnd);
if(hdr.ctl & SYN)
debug("SYN");
if(hdr.ctl & RST)
static int compare(const void *va, const void *vb) {
const struct utcp_connection *a = *(struct utcp_connection **)va;
const struct utcp_connection *b = *(struct utcp_connection **)vb;
- if(!a->src || !b->src)
- abort();
+
+ assert(a->src && b->src);
+
int c = (int)a->src - (int)b->src;
if(c)
return c;
static void free_connection(struct utcp_connection *c) {
struct utcp *utcp = c->utcp;
struct utcp_connection **cp = bsearch(&c, utcp->connections, utcp->nconnections, sizeof *utcp->connections, compare);
- if(!cp)
- abort();
+
+ assert(cp);
int i = cp - utcp->connections;
memmove(cp + i, cp + i + 1, (utcp->nconnections - i - 1) * sizeof *cp);
int32_t cwndleft = c->snd.cwnd - seqdiff(c->snd.nxt, c->snd.una);
char *data = c->sndbuf + seqdiff(c->snd.nxt, c->snd.una);
- fprintf(stderr, "ack, left=%d, cwndleft=%d, sendatleastone=%d\n", left, cwndleft, sendatleastone);
- if(left < 0)
- abort();
+ assert(left >= 0);
if(cwndleft <= 0)
cwndleft = 0;
hdr->dst = tmp;
}
-int utcp_recv(struct utcp *utcp, const void *data, size_t len) {
+ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) {
if(!utcp) {
errno = EFAULT;
return -1;
c->dupack++;
if(c->dupack >= 3) {
debug("Triplicate ACK\n");
- abort();
+ //TODO: Resend one packet and go to fast recovery mode. See RFC 6582.
+ //abort();
}
}
}
// 4. Update timers
if(advanced) {
- timerclear(&c->conn_timeout); // It should be set anew in utcp_timeout() if c->snd.una != c->snd.nxt.
+ timerclear(&c->conn_timeout); // It will be set anew in utcp_timeout() if c->snd.una != c->snd.nxt.
if(c->snd.una == c->snd.nxt)
timerclear(&c->rtrx_timeout);
}
abort();
}
- int rxd;
+ ssize_t rxd;
if(c->recv) {
rxd = c->recv(c, data, len);
+ if(rxd != len) {
+ // TODO: once we have a receive buffer, handle the application not accepting all data.
+ fprintf(stderr, "c->recv(%p, %p, %zu) returned %zd\n", c, data, len, rxd);
+ abort();
+ }
if(rxd < 0)
rxd = 0;
else if(rxd > len)
}
int utcp_shutdown(struct utcp_connection *c, int dir) {
- debug("%p shutdown %d\n", c->utcp, dir);
+ debug("%p shutdown %d\n", c ? c->utcp : NULL, dir);
if(!c) {
errno = EFAULT;
return -1;
typedef bool (*utcp_pre_accept_t)(struct utcp *utcp, uint16_t port);
typedef void (*utcp_accept_t)(struct utcp_connection *utcp_connection, uint16_t port);
-typedef int (*utcp_send_t)(struct utcp *utcp, const void *data, size_t len);
-typedef int (*utcp_recv_t)(struct utcp_connection *connection, const void *data, size_t len);
+typedef ssize_t (*utcp_send_t)(struct utcp *utcp, const void *data, size_t len);
+typedef ssize_t (*utcp_recv_t)(struct utcp_connection *connection, const void *data, size_t len);
extern struct utcp *utcp_init(utcp_accept_t accept, utcp_pre_accept_t pre_accept, utcp_send_t send, void *priv);
extern void utcp_exit(struct utcp *utcp);
extern struct utcp_connection *utcp_connect(struct utcp *utcp, uint16_t port, utcp_recv_t recv, void *priv);
extern void utcp_accept(struct utcp_connection *utcp, utcp_recv_t recv, void *priv);
extern ssize_t utcp_send(struct utcp_connection *connection, const void *data, size_t len);
-extern int utcp_recv(struct utcp *utcp, const void *data, size_t len);
+extern ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len);
extern int utcp_close(struct utcp_connection *connection);
extern int utcp_abort(struct utcp_connection *connection);
extern int utcp_shutdown(struct utcp_connection *connection, int how);
// Send buffer
char *sndbuf;
- uint32_t sndbufused;
uint32_t sndbufsize;
uint32_t maxsndbufsize;