X-Git-Url: http://git.meshlink.io/?p=utcp;a=blobdiff_plain;f=utcp.c;h=e64eb2445fed00d19dd477a6f88f2cb6a61797ee;hp=65885287f599631b045c563b2564ef3301a893d3;hb=8d8944f2e16b8d11e54cb79e1d45023e84aed37f;hpb=14892644247e46fc06f805d2653733bbdf45fb17 diff --git a/utcp.c b/utcp.c index 6588528..e64eb24 100644 --- a/utcp.c +++ b/utcp.c @@ -19,6 +19,7 @@ #define _GNU_SOURCE +#include #include #include #include @@ -29,152 +30,101 @@ #include #include -#define UTCP_INTERNAL -#include "utcp.h" - -#define PREP(l) char pkt[(l) + sizeof struct hdr]; struct hdr *hdr = &pkt; - -#define SYN 1 -#define ACK 2 -#define FIN 4 -#define RST 8 - -struct hdr { - uint16_t src; // Source port - uint16_t dst; // Destination port - uint32_t seq; // Sequence number - uint32_t ack; // Acknowledgement number - uint32_t wnd; // Window size - uint16_t ctl; // Flags (SYN, ACK, FIN, RST) - uint16_t aux; // other stuff -}; - -enum state { - CLOSED, - LISTEN, - SYN_SENT, - SYN_RECEIVED, - ESTABLISHED, - FIN_WAIT_1, - FIN_WAIT_2, - CLOSE_WAIT, - CLOSING, - LAST_ACK, - TIME_WAIT -}; - -const char *strstate[] = { - "CLOSED", - "LISTEN", - "SYN_SENT", - "SYN_RECEIVED", - "ESTABLISHED", - "FIN_WAIT_1", - "FIN_WAIT_2", - "CLOSE_WAIT", - "CLOSING", - "LAST_ACK", - "TIME_WAIT" -}; - -struct utcp_connection { - void *priv; - struct utcp *utcp; - bool reapable; - - uint16_t src; - uint16_t dst; - enum state state; - - // The following two structures form the TCB +#include "utcp_priv.h" - struct { - uint32_t una; - uint32_t nxt; - uint32_t wnd; - uint32_t iss; - } snd; - - struct { - uint32_t nxt; - uint32_t wnd; - uint32_t irs; - } rcv; - - utcp_recv_t recv; - - struct timeval conn_timeout; - struct timeval rtrx_timeout; - - char *sndbuf; - uint32_t sndbufsize; -}; +#ifndef EBADMSG +#define EBADMSG 104 +#endif -struct utcp { - void *priv; +#ifndef SHUT_RDWR +#define SHUT_RDWR 2 +#endif - utcp_accept_t accept; - utcp_pre_accept_t pre_accept; - utcp_send_t send; +#ifdef poll +#undef poll +#endif - uint16_t mtu; - int timeout; +#ifndef timersub +#define timersub(a, b, r) do {\ + (r)->tv_sec = (a)->tv_sec - (b)->tv_sec;\ + (r)->tv_usec = (a)->tv_usec - (b)->tv_usec;\ + if((r)->tv_usec < 0)\ + (r)->tv_sec--, (r)->tv_usec += 1000000;\ +} while (0) +#endif - struct utcp_connection **connections; - int nconnections; - int nallocated; -}; +#ifdef UTCP_DEBUG +#include -static void set_state(struct utcp_connection *c, enum state state) { - c->state = state; - if(state == ESTABLISHED) - timerclear(&c->conn_timeout); - fprintf(stderr, "%p new state: %s\n", c->utcp, strstate[state]); +static void debug(const char *format, ...) { + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); } static void print_packet(struct utcp *utcp, const char *dir, const void *pkt, size_t len) { struct hdr hdr; if(len < sizeof hdr) { - fprintf(stderr, "%p %s: short packet (%zu bytes)\n", utcp, dir, len); + debug("%p %s: short packet (%zu bytes)\n", utcp, dir, len); return; } 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) - fprintf(stderr, "SYN"); + debug("SYN"); if(hdr.ctl & RST) - fprintf(stderr, "RST"); + debug("RST"); if(hdr.ctl & FIN) - fprintf(stderr, "FIN"); + debug("FIN"); if(hdr.ctl & ACK) - fprintf(stderr, "ACK"); + debug("ACK"); if(len > sizeof hdr) { - fprintf(stderr, " data="); + debug(" data="); for(int i = sizeof hdr; i < len; i++) { const char *data = pkt; - fprintf(stderr, "%c", data[i] >= 32 ? data[i] : '.'); + debug("%c", data[i] >= 32 ? data[i] : '.'); } } - fprintf(stderr, "\n"); + debug("\n"); +} +#else +#define debug(...) +#define print_packet(...) +#endif + +static void set_state(struct utcp_connection *c, enum state state) { + c->state = state; + if(state == ESTABLISHED) + timerclear(&c->conn_timeout); + debug("%p new state: %s\n", c->utcp, strstate[state]); } static inline void list_connections(struct utcp *utcp) { - fprintf(stderr, "%p has %d connections:\n", utcp, utcp->nconnections); + debug("%p has %d connections:\n", utcp, utcp->nconnections); for(int i = 0; i < utcp->nconnections; i++) - fprintf(stderr, " %u -> %u state %s\n", utcp->connections[i]->src, utcp->connections[i]->dst, strstate[utcp->connections[i]->state]); + debug(" %u -> %u state %s\n", utcp->connections[i]->src, utcp->connections[i]->dst, strstate[utcp->connections[i]->state]); +} + +static int32_t seqdiff(uint32_t a, uint32_t b) { + return a - b; } // Connections are stored in a sorted list. // This gives O(log(N)) lookup time, O(N log(N)) insertion time and O(N) deletion time. static int compare(const void *va, const void *vb) { + assert(va && 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 && b); + assert(a->src && b->src); + int c = (int)a->src - (int)b->src; if(c) return c; @@ -196,13 +146,14 @@ static struct utcp_connection *find_connection(const struct utcp *utcp, uint16_t 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); + memmove(cp, cp + 1, (utcp->nconnections - i - 1) * sizeof *cp); utcp->nconnections--; + free(c->sndbuf); free(c); } @@ -232,16 +183,20 @@ static struct utcp_connection *allocate_connection(struct utcp *utcp, uint16_t s else utcp->nallocated *= 2; struct utcp_connection **new_array = realloc(utcp->connections, utcp->nallocated * sizeof *utcp->connections); - if(!new_array) { - errno = ENOMEM; + if(!new_array) return NULL; - } utcp->connections = new_array; } struct utcp_connection *c = calloc(1, sizeof *c); - if(!c) { - errno = ENOMEM; + if(!c) + return NULL; + + c->sndbufsize = DEFAULT_SNDBUFSIZE; + c->maxsndbufsize = DEFAULT_MAXSNDBUFSIZE; + c->sndbuf = malloc(c->sndbufsize); + if(!c->sndbuf) { + free(c); return NULL; } @@ -253,11 +208,9 @@ static struct utcp_connection *allocate_connection(struct utcp *utcp, uint16_t s c->snd.una = c->snd.iss; c->snd.nxt = c->snd.iss + 1; c->rcv.wnd = utcp->mtu; + c->snd.last = c->snd.nxt; + c->snd.cwnd = utcp->mtu; c->utcp = utcp; - c->sndbufsize = 65536; - c->sndbuf = malloc(c->sndbufsize); - if(!c->sndbuf) - c->sndbufsize = 0; // Add it to the sorted list of connections @@ -273,6 +226,7 @@ struct utcp_connection *utcp_connect(struct utcp *utcp, uint16_t dst, utcp_recv_ return NULL; c->recv = recv; + c->priv = priv; struct hdr hdr; @@ -280,8 +234,9 @@ struct utcp_connection *utcp_connect(struct utcp *utcp, uint16_t dst, utcp_recv_ hdr.dst = c->dst; hdr.seq = c->snd.iss; hdr.ack = 0; - hdr.ctl = SYN; hdr.wnd = c->rcv.wnd; + hdr.ctl = SYN; + hdr.aux = 0; set_state(c, SYN_SENT); @@ -296,19 +251,80 @@ struct utcp_connection *utcp_connect(struct utcp *utcp, uint16_t dst, utcp_recv_ void utcp_accept(struct utcp_connection *c, utcp_recv_t recv, void *priv) { if(c->reapable || c->state != SYN_RECEIVED) { - fprintf(stderr, "Error: accept() called on invalid connection %p in state %s\n", c, strstate[c->state]); + debug("Error: accept() called on invalid connection %p in state %s\n", c, strstate[c->state]); return; } - fprintf(stderr, "%p accepted, %p %p\n", c, recv, priv); + debug("%p accepted, %p %p\n", c, recv, priv); c->recv = recv; c->priv = priv; set_state(c, ESTABLISHED); } +static void ack(struct utcp_connection *c, bool sendatleastone) { + int32_t left = seqdiff(c->snd.last, c->snd.nxt); + int32_t cwndleft = c->snd.cwnd - seqdiff(c->snd.nxt, c->snd.una); + char *data = c->sndbuf + seqdiff(c->snd.nxt, c->snd.una); + + assert(left >= 0); + + if(cwndleft <= 0) + cwndleft = 0; + + if(cwndleft < left) + left = cwndleft; + + if(!left && !sendatleastone) + return; + + struct { + struct hdr hdr; + char data[]; + } *pkt; + + pkt = malloc(sizeof pkt->hdr + c->utcp->mtu); + if(!pkt->data) + return; + + pkt->hdr.src = c->src; + pkt->hdr.dst = c->dst; + pkt->hdr.ack = c->rcv.nxt; + pkt->hdr.wnd = c->snd.wnd; + pkt->hdr.ctl = ACK; + pkt->hdr.aux = 0; + + do { + uint32_t seglen = left > c->utcp->mtu ? c->utcp->mtu : left; + pkt->hdr.seq = c->snd.nxt; + + memcpy(pkt->data, data, seglen); + + c->snd.nxt += seglen; + data += seglen; + left -= seglen; + + if(c->state != ESTABLISHED && !left && seglen) { + switch(c->state) { + case FIN_WAIT_1: + case CLOSING: + seglen--; + pkt->hdr.ctl |= FIN; + break; + default: + break; + } + } + + print_packet(c->utcp, "send", pkt, sizeof pkt->hdr + seglen); + c->utcp->send(c->utcp, pkt, sizeof pkt->hdr + seglen); + } while(left); + + free(pkt); +} + ssize_t utcp_send(struct utcp_connection *c, const void *data, size_t len) { if(c->reapable) { - fprintf(stderr, "Error: send() called on closed connection %p\n", c); + debug("Error: send() called on closed connection %p\n", c); errno = EBADF; return -1; } @@ -318,7 +334,7 @@ ssize_t utcp_send(struct utcp_connection *c, const void *data, size_t len) { case LISTEN: case SYN_SENT: case SYN_RECEIVED: - fprintf(stderr, "Error: send() called on unconnected connection %p\n", c); + debug("Error: send() called on unconnected connection %p\n", c); errno = ENOTCONN; return -1; case ESTABLISHED: @@ -329,7 +345,7 @@ ssize_t utcp_send(struct utcp_connection *c, const void *data, size_t len) { case CLOSING: case LAST_ACK: case TIME_WAIT: - fprintf(stderr, "Error: send() called on closing connection %p\n", c); + debug("Error: send() called on closing connection %p\n", c); errno = EPIPE; return -1; } @@ -344,17 +360,31 @@ ssize_t utcp_send(struct utcp_connection *c, const void *data, size_t len) { return -1; } - uint32_t bufused = c->snd.nxt - c->snd.una; + uint32_t bufused = seqdiff(c->snd.nxt, c->snd.una); /* Check our send buffer. * - If it's big enough, just put the data in there. - * - If not, decide whether to enlarge. (TODO, now we just always enlarge) + * - If not, decide whether to enlarge if possible. * - Cap len so it doesn't overflow our buffer. */ - if(len > c->sndbufsize - bufused) { - c->sndbufsize *= 2; - c->sndbuf = realloc(c->sndbuf, c->sndbufsize); + if(len > c->sndbufsize - bufused && c->sndbufsize < c->maxsndbufsize) { + uint32_t newbufsize; + if(c->sndbufsize > c->maxsndbufsize / 2) + newbufsize = c->maxsndbufsize; + else + newbufsize = c->sndbufsize * 2; + if(bufused + len > newbufsize) { + if(bufused + len > c->maxsndbufsize) + newbufsize = c->maxsndbufsize; + else + newbufsize = bufused + len; + } + char *newbuf = realloc(c->sndbuf, newbufsize); + if(newbuf) { + c->sndbuf = newbuf; + c->sndbufsize = newbufsize; + } } if(len > c->sndbufsize - bufused) @@ -366,37 +396,9 @@ ssize_t utcp_send(struct utcp_connection *c, const void *data, size_t len) { } memcpy(c->sndbuf + bufused, data, len); + c->snd.last += len; - // Send segments - - struct { - struct hdr hdr; - char data[c->utcp->mtu]; - } pkt; - - pkt.hdr.src = c->src; - pkt.hdr.dst = c->dst; - pkt.hdr.ack = c->rcv.nxt; - pkt.hdr.wnd = c->snd.wnd; - pkt.hdr.ctl = ACK; - - uint32_t left = len; - - while(left) { - uint32_t seglen = left > c->utcp->mtu ? c->utcp->mtu : left; - pkt.hdr.seq = c->snd.nxt; - - memcpy(pkt.data, data, seglen); - - c->snd.nxt += seglen; - data += seglen; - left -= seglen; - - print_packet(c->utcp, "send", &pkt, sizeof pkt.hdr + seglen); - c->utcp->send(c->utcp, &pkt, sizeof pkt.hdr + seglen); - } - - fprintf(stderr, "len=%zu\n", len); + ack(c, false); return len; } @@ -406,11 +408,7 @@ static void swap_ports(struct hdr *hdr) { hdr->dst = tmp; } -static int32_t seqdiff(uint32_t a, uint32_t b) { - return a - b; -} - -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; @@ -497,7 +495,7 @@ int utcp_recv(struct utcp *utcp, const void *data, size_t len) { return 0; } - fprintf(stderr, "%p state %s\n", c->utcp, strstate[c->state]); + debug("%p state %s\n", c->utcp, strstate[c->state]); // In case this is for a CLOSED connection, ignore the packet. // TODO: make it so incoming packets can never match a CLOSED connection. @@ -555,7 +553,7 @@ int utcp_recv(struct utcp *utcp, const void *data, size_t len) { #endif if(!acceptable) { - fprintf(stderr, "Packet not acceptable, %u <= %u + %zu < %u\n", c->rcv.nxt, hdr.seq, len, c->rcv.nxt + c->rcv.wnd); + debug("Packet not acceptable, %u <= %u + %zu < %u\n", c->rcv.nxt, hdr.seq, len, c->rcv.nxt + c->rcv.wnd); // Ignore unacceptable RST packets. if(hdr.ctl & RST) return 0; @@ -569,7 +567,7 @@ int utcp_recv(struct utcp *utcp, const void *data, size_t len) { // ackno should not roll back, and it should also not be bigger than snd.nxt. if(hdr.ctl & ACK && (seqdiff(hdr.ack, c->snd.nxt) > 0 || seqdiff(hdr.ack, c->snd.una) < 0)) { - fprintf(stderr, "Packet ack seqno out of range, %u %u %u\n", hdr.ack, c->snd.una, c->snd.nxt); + debug("Packet ack seqno out of range, %u %u %u\n", hdr.ack, c->snd.una, c->snd.nxt); // Ignore unacceptable RST packets. if(hdr.ctl & RST) return 0; @@ -629,21 +627,70 @@ int utcp_recv(struct utcp *utcp, const void *data, size_t len) { // 3. Advance snd.una uint32_t advanced = seqdiff(hdr.ack, c->snd.una); - c->snd.una = hdr.ack; + uint32_t prevrcvnxt = c->rcv.nxt; if(advanced) { - fprintf(stderr, "%p advanced %u\n", utcp, advanced); + int32_t data_acked = advanced; + + switch(c->state) { + case SYN_SENT: + case SYN_RECEIVED: + data_acked--; + break; + // TODO: handle FIN as well. + default: + break; + } + + assert(data_acked >= 0); + + int32_t bufused = seqdiff(c->snd.last, c->snd.una); + assert(data_acked <= bufused); + // Make room in the send buffer. // TODO: try to avoid memmoving too much. Circular buffer? - uint32_t left = seqdiff(c->snd.nxt, hdr.ack); - if(left) - memmove(c->sndbuf, c->sndbuf + advanced, left); + uint32_t left = bufused - data_acked; + if(data_acked && left) + memmove(c->sndbuf, c->sndbuf + data_acked, left); + + c->snd.una = hdr.ack; + + c->dupack = 0; + c->snd.cwnd += utcp->mtu; + if(c->snd.cwnd > c->maxsndbufsize) + c->snd.cwnd = c->maxsndbufsize; + + // Check if we have sent a FIN that is now ACKed. + switch(c->state) { + case FIN_WAIT_1: + if(c->snd.una == c->snd.last) + set_state(c, FIN_WAIT_2); + break; + case CLOSING: + if(c->snd.una == c->snd.last) { + gettimeofday(&c->conn_timeout, NULL); + c->conn_timeout.tv_sec += 60; + set_state(c, TIME_WAIT); + } + break; + default: + break; + } + } else { + if(!len) { + c->dupack++; + if(c->dupack >= 3) { + debug("Triplicate ACK\n"); + //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); } @@ -717,10 +764,15 @@ int utcp_recv(struct utcp *utcp, const void *data, size_t len) { 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) @@ -747,6 +799,8 @@ int utcp_recv(struct utcp *utcp, const void *data, size_t len) { set_state(c, CLOSING); break; case FIN_WAIT_2: + gettimeofday(&c->conn_timeout, NULL); + c->conn_timeout.tv_sec += 60; set_state(c, TIME_WAIT); break; case CLOSE_WAIT: @@ -761,6 +815,7 @@ int utcp_recv(struct utcp *utcp, const void *data, size_t len) { // FIN counts as one sequence number c->rcv.nxt++; + len++; // Inform the application that the peer closed the connection. if(c->recv) { @@ -769,20 +824,14 @@ int utcp_recv(struct utcp *utcp, const void *data, size_t len) { } } - if(!len && !advanced) - return 0; - - if(!len && !(hdr.ctl & SYN) && !(hdr.ctl & FIN)) - return 0; + // Now we send something back if: + // - we advanced rcv.nxt (ie, we got some data that needs to be ACKed) + // -> sendatleastone = true + // - or we got an ack, so we should maybe send a bit more data + // -> sendatleastone = false ack: - hdr.src = c->src; - hdr.dst = c->dst; - hdr.seq = c->snd.nxt; - hdr.ack = c->rcv.nxt; - hdr.ctl = ACK; - print_packet(c->utcp, "send", &hdr, sizeof hdr); - utcp->send(utcp, &hdr, sizeof hdr); + ack(c, prevrcvnxt != c->rcv.nxt); return 0; reset: @@ -803,14 +852,14 @@ reset: } int utcp_shutdown(struct utcp_connection *c, int dir) { - fprintf(stderr, "%p shutdown %d\n", c->utcp, dir); + debug("%p shutdown %d\n", c ? c->utcp : NULL, dir); if(!c) { errno = EFAULT; return -1; } if(c->reapable) { - fprintf(stderr, "Error: shutdown() called on closed connection %p\n", c); + debug("Error: shutdown() called on closed connection %p\n", c); errno = EBADF; return -1; } @@ -842,21 +891,9 @@ int utcp_shutdown(struct utcp_connection *c, int dir) { return 0; } - // Send FIN + c->snd.last++; - struct hdr hdr; - - hdr.src = c->src; - hdr.dst = c->dst; - hdr.seq = c->snd.nxt; - hdr.ack = c->rcv.nxt; - hdr.wnd = c->snd.wnd; - hdr.ctl = FIN | ACK; - - c->snd.nxt += 1; - - print_packet(c->utcp, "send", &hdr, sizeof hdr); - c->utcp->send(c->utcp, &hdr, sizeof hdr); + ack(c, false); return 0; } @@ -874,7 +911,7 @@ int utcp_abort(struct utcp_connection *c) { } if(c->reapable) { - fprintf(stderr, "Error: abort() called on closed connection %p\n", c); + debug("Error: abort() called on closed connection %p\n", c); errno = EBADF; return -1; } @@ -925,11 +962,15 @@ static void retransmit(struct utcp_connection *c) { struct { struct hdr hdr; - char data[c->utcp->mtu]; - } pkt; + char data[]; + } *pkt; + + pkt = malloc(sizeof pkt->hdr + c->utcp->mtu); + if(!pkt) + return; - pkt.hdr.src = c->src; - pkt.hdr.dst = c->dst; + pkt->hdr.src = c->src; + pkt->hdr.dst = c->dst; switch(c->state) { case LISTEN: @@ -937,27 +978,27 @@ static void retransmit(struct utcp_connection *c) { break; case SYN_SENT: - pkt.hdr.seq = c->snd.iss; - pkt.hdr.ack = 0; - pkt.hdr.wnd = c->rcv.wnd; - pkt.hdr.ctl = SYN; - print_packet(c->utcp, "rtrx", &pkt, sizeof pkt.hdr); - utcp->send(utcp, &pkt, sizeof pkt.hdr); + pkt->hdr.seq = c->snd.iss; + pkt->hdr.ack = 0; + pkt->hdr.wnd = c->rcv.wnd; + pkt->hdr.ctl = SYN; + print_packet(c->utcp, "rtrx", pkt, sizeof pkt->hdr); + utcp->send(utcp, pkt, sizeof pkt->hdr); break; case SYN_RECEIVED: - pkt.hdr.seq = c->snd.nxt; - pkt.hdr.ack = c->rcv.nxt; - pkt.hdr.ctl = SYN | ACK; - print_packet(c->utcp, "rtrx", &pkt, sizeof pkt.hdr); - utcp->send(utcp, &pkt, sizeof pkt.hdr); + pkt->hdr.seq = c->snd.nxt; + pkt->hdr.ack = c->rcv.nxt; + pkt->hdr.ctl = SYN | ACK; + print_packet(c->utcp, "rtrx", pkt, sizeof pkt->hdr); + utcp->send(utcp, pkt, sizeof pkt->hdr); break; case ESTABLISHED: case FIN_WAIT_1: - pkt.hdr.seq = c->snd.una; - pkt.hdr.ack = c->rcv.nxt; - pkt.hdr.ctl = ACK; + pkt->hdr.seq = c->snd.una; + pkt->hdr.ack = c->rcv.nxt; + pkt->hdr.ctl = ACK; uint32_t len = seqdiff(c->snd.nxt, c->snd.una); if(c->state == FIN_WAIT_1) len--; @@ -965,17 +1006,19 @@ static void retransmit(struct utcp_connection *c) { len = utcp->mtu; else { if(c->state == FIN_WAIT_1) - pkt.hdr.ctl |= FIN; + pkt->hdr.ctl |= FIN; } - memcpy(pkt.data, c->sndbuf, len); - print_packet(c->utcp, "rtrx", &pkt, sizeof pkt.hdr + len); - utcp->send(utcp, &pkt, sizeof pkt.hdr + len); + memcpy(pkt->data, c->sndbuf, len); + print_packet(c->utcp, "rtrx", pkt, sizeof pkt->hdr + len); + utcp->send(utcp, pkt, sizeof pkt->hdr + len); break; default: // TODO: implement abort(); } + + free(pkt); } /* Handle timeouts. @@ -996,7 +1039,7 @@ int utcp_timeout(struct utcp *utcp) { if(c->state == CLOSED) { if(c->reapable) { - fprintf(stderr, "Reaping %p\n", c); + debug("Reaping %p\n", c); free_connection(c); i--; } @@ -1015,6 +1058,9 @@ int utcp_timeout(struct utcp *utcp) { retransmit(c); } + if(c->poll && c->sndbufsize < c->maxsndbufsize / 2 && (c->state == ESTABLISHED || c->state == CLOSE_WAIT)) + c->poll(c, c->maxsndbufsize - c->sndbufsize); + if(timerisset(&c->conn_timeout) && timercmp(&c->conn_timeout, &next, <)) next = c->conn_timeout; @@ -1059,13 +1105,71 @@ struct utcp *utcp_init(utcp_accept_t accept, utcp_pre_accept_t pre_accept, utcp_ void utcp_exit(struct utcp *utcp) { if(!utcp) return; - for(int i = 0; i < utcp->nconnections; i++) - free_connection(utcp->connections[i]); + for(int i = 0; i < utcp->nconnections; i++) { + if(!utcp->connections[i]->reapable) + debug("Warning, freeing unclosed connection %p\n", utcp->connections[i]); + free(utcp->connections[i]->sndbuf); + free(utcp->connections[i]); + } + free(utcp->connections); free(utcp); } -int utcp_set_connection_timeout(struct utcp *u, int timeout) { - int prev = u->timeout; +uint16_t utcp_get_mtu(struct utcp *utcp) { + return utcp->mtu; +} + +void utcp_set_mtu(struct utcp *utcp, uint16_t mtu) { + // TODO: handle overhead of the header + utcp->mtu = mtu; +} + +int utcp_get_user_timeout(struct utcp *u) { + return u->timeout; +} + +void utcp_set_user_timeout(struct utcp *u, int timeout) { u->timeout = timeout; - return prev; +} + +size_t utcp_get_sndbuf(struct utcp_connection *c) { + return c->maxsndbufsize; +} + +size_t utcp_get_sndbuf_free(struct utcp_connection *c) { + return c->maxsndbufsize - c->sndbufsize; +} + +void utcp_set_sndbuf(struct utcp_connection *c, size_t size) { + c->maxsndbufsize = size; + if(c->maxsndbufsize != size) + c->maxsndbufsize = -1; +} + +bool utcp_get_nodelay(struct utcp_connection *c) { + return c->nodelay; +} + +void utcp_set_nodelay(struct utcp_connection *c, bool nodelay) { + c->nodelay = nodelay; +} + +bool utcp_get_keepalive(struct utcp_connection *c) { + return c->keepalive; +} + +void utcp_set_keepalive(struct utcp_connection *c, bool keepalive) { + c->keepalive = keepalive; +} + +size_t utcp_get_outq(struct utcp_connection *c) { + return seqdiff(c->snd.nxt, c->snd.una); +} + +void utcp_set_recv_cb(struct utcp_connection *c, utcp_recv_t recv) { + c->recv = recv; +} + +void utcp_set_poll_cb(struct utcp_connection *c, utcp_poll_t poll) { + c->poll = poll; }