uint32_t realoffset = buf->offset + offset;
- if(buf->size - buf->offset < offset) {
+ if(buf->size - buf->offset <= offset) {
// The offset wrapped
realoffset -= buf->size;
}
uint32_t realoffset = buf->offset + offset;
- if(buf->size - buf->offset < offset) {
+ if(buf->size - buf->offset <= offset) {
// The offset wrapped
realoffset -= buf->size;
}
}
// Copy data from the buffer without removing it.
-static ssize_t buffer_call(struct buffer *buf, utcp_recv_t cb, void *arg, size_t offset, size_t len) {
+static ssize_t buffer_call(struct utcp_connection *c, struct buffer *buf, size_t offset, size_t len) {
+ if(!c->recv) {
+ return len;
+ }
+
// Ensure we don't copy more than is actually stored in the buffer
if(offset >= buf->used) {
return 0;
uint32_t realoffset = buf->offset + offset;
- if(buf->size - buf->offset < offset) {
+ if(buf->size - buf->offset <= offset) {
// The offset wrapped
realoffset -= buf->size;
}
if(buf->size - realoffset < len) {
// The data is wrapped
- ssize_t rx1 = cb(arg, buf->data + realoffset, buf->size - realoffset);
+ ssize_t rx1 = c->recv(c, buf->data + realoffset, buf->size - realoffset);
if(rx1 < buf->size - realoffset) {
return rx1;
}
- ssize_t rx2 = cb(arg, buf->data, len - (buf->size - realoffset));
+ // The channel might have been closed by the previous callback
+ if(!c->recv) {
+ return len;
+ }
+
+ ssize_t rx2 = c->recv(c, buf->data, len - (buf->size - realoffset));
if(rx2 < 0) {
return rx2;
return rx1 + rx2;
}
} else {
- return cb(arg, buf->data + realoffset, len);
+ return c->recv(c, buf->data + realoffset, len);
}
}
len = buf->used;
}
- if(buf->size - buf->offset < len) {
+ if(buf->size - buf->offset <= len) {
buf->offset -= buf->size;
}
}
static uint32_t buffer_free(const struct buffer *buf) {
- return buf->maxsize - buf->used;
+ return buf->maxsize > buf->used ? buf->maxsize - buf->used : 0;
}
// Connections are stored in a sorted list.
if(!is_reliable(c)) {
c->snd.una = c->snd.nxt = c->snd.last;
buffer_discard(&c->sndbuf, c->sndbuf.used);
- c->do_poll = true;
}
if(is_reliable(c) && !timespec_isset(&c->rtrx_timeout)) {
struct {
struct hdr hdr;
uint8_t data[];
- } *pkt;
-
- pkt = malloc(c->utcp->mtu);
-
- if(!pkt) {
- return;
- }
+ } *pkt = c->utcp->pkt;
pkt->hdr.src = c->src;
pkt->hdr.dst = c->dst;
default:
break;
}
-
- free(pkt);
}
static void retransmit(struct utcp_connection *c) {
struct utcp *utcp = c->utcp;
+ if(utcp->retransmit) {
+ utcp->retransmit(c);
+ }
+
struct {
struct hdr hdr;
uint8_t data[];
size_t offset = len;
len = c->sacks[0].offset + c->sacks[0].len;
size_t remainder = len - offset;
- ssize_t rxd = buffer_call(&c->rcvbuf, c->recv, c, offset, remainder);
+
+ ssize_t rxd = buffer_call(c, &c->rcvbuf, offset, remainder);
if(rxd != (ssize_t)remainder) {
// TODO: handle the application not accepting all data.
static void handle_unreliable(struct utcp_connection *c, const struct hdr *hdr, const void *data, size_t len) {
// Fast path for unfragmented packets
if(!hdr->wnd && !(hdr->ctl & MF)) {
- c->recv(c, data, len);
+ if(c->recv) {
+ c->recv(c, data, len);
+ }
+
c->rcv.nxt = hdr->seq + len;
return;
}
// Send the packet if it's the final fragment
if(!(hdr->ctl & MF)) {
- buffer_call(&c->rcvbuf, c->recv, c, 0, hdr->wnd + len);
+ buffer_call(c, &c->rcvbuf, 0, hdr->wnd + len);
}
c->rcv.nxt = hdr->seq + len;
print_packet(c, "send", &pkt, sizeof(hdr));
utcp->send(utcp, &pkt, sizeof(hdr));
}
+
+ start_retransmit_timer(c);
} else {
// No, we don't want your packets, send a RST back
len = 1;
if(data_acked) {
buffer_discard(&c->sndbuf, data_acked);
- c->do_poll = true;
+
+ if(is_reliable(c)) {
+ c->do_poll = true;
+ }
}
// Also advance snd.nxt if possible
}
c->rcv.irs = hdr.seq;
- c->rcv.nxt = hdr.seq;
+ c->rcv.nxt = hdr.seq + 1;
if(c->shut_wr) {
c->snd.last++;
set_state(c, ESTABLISHED);
}
- // TODO: notify application of this somehow.
break;
case SYN_RECEIVED:
case CLOSING:
case LAST_ACK:
case TIME_WAIT:
- // Ehm, no. We should never receive a second SYN.
- return 0;
+ // This could be a retransmission. Ignore the SYN flag, but send an ACK back.
+ break;
default:
#ifdef UTCP_DEBUG
#endif
return 0;
}
-
- // SYN counts as one sequence number
- c->rcv.nxt++;
}
// 6. Process new data
return NULL;
}
+ utcp_set_mtu(utcp, DEFAULT_MTU);
+
+ if(!utcp->pkt) {
+ free(utcp);
+ return NULL;
+ }
+
if(!CLOCK_GRANULARITY) {
struct timespec res;
clock_getres(UTCP_CLOCK, &res);
utcp->pre_accept = pre_accept;
utcp->send = send;
utcp->priv = priv;
- utcp_set_mtu(utcp, DEFAULT_MTU);
utcp->timeout = DEFAULT_USER_TIMEOUT; // sec
return utcp;
c->sndbuf.maxsize = -1;
}
- c->do_poll = buffer_free(&c->sndbuf);
+ c->do_poll = is_reliable(c) && buffer_free(&c->sndbuf);
}
size_t utcp_get_rcvbuf(struct utcp_connection *c) {
void utcp_set_poll_cb(struct utcp_connection *c, utcp_poll_t poll) {
if(c) {
c->poll = poll;
- c->do_poll = buffer_free(&c->sndbuf);
+ c->do_poll = is_reliable(c) && buffer_free(&c->sndbuf);
}
}
}
}
+void utcp_set_retransmit_cb(struct utcp *utcp, utcp_retransmit_t retransmit) {
+ utcp->retransmit = retransmit;
+}
+
void utcp_set_clock_granularity(long granularity) {
CLOCK_GRANULARITY = granularity;
}