X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=utcp.c;h=9f668edd37256e214db12347722c48a79dd8f4cf;hb=f4c52312544ee99bcce46ebcfc0c8e6ac6356fd5;hp=454a497428dd0e698db2bdeae92189fda41eb0fb;hpb=f60a19752749bf1d28031e78c46f8924325ffcdd;p=utcp diff --git a/utcp.c b/utcp.c index 454a497..9f668ed 100644 --- a/utcp.c +++ b/utcp.c @@ -19,6 +19,7 @@ #define _GNU_SOURCE +#include #include #include #include @@ -49,7 +50,7 @@ static void print_packet(struct utcp *utcp, const char *dir, const void *pkt, si } 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) @@ -97,8 +98,9 @@ static int32_t seqdiff(uint32_t a, uint32_t b) { 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; @@ -120,13 +122,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); utcp->nconnections--; + free(c->sndbuf); free(c); } @@ -206,8 +209,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); @@ -233,10 +237,12 @@ void utcp_accept(struct utcp_connection *c, utcp_recv_t recv, void *priv) { } static void ack(struct utcp_connection *c, bool sendatleastone) { - uint32_t left = seqdiff(c->snd.last, c->snd.nxt); + 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; @@ -256,6 +262,7 @@ static void ack(struct utcp_connection *c, bool sendatleastone) { 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; @@ -267,6 +274,18 @@ static void ack(struct utcp_connection *c, bool sendatleastone) { 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); @@ -324,6 +343,12 @@ ssize_t utcp_send(struct utcp_connection *c, const void *data, size_t len) { 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; @@ -352,7 +377,7 @@ static void swap_ports(struct hdr *hdr) { 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; @@ -584,12 +609,31 @@ int utcp_recv(struct utcp *utcp, const void *data, size_t len) { c->snd.cwnd += utcp->mtu; if(c->snd.cwnd > c->maxsndbufsize) c->snd.cwnd = c->maxsndbufsize; + debug("%p increasing cwnd to %u\n", utcp, c->snd.cwnd); + + // 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"); - abort(); + //TODO: Resend one packet and go to fast recovery mode. See RFC 6582. + //abort(); } } } @@ -597,7 +641,7 @@ int utcp_recv(struct utcp *utcp, const void *data, size_t len) { // 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); } @@ -671,10 +715,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) @@ -701,6 +750,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: @@ -715,6 +766,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) { @@ -751,7 +803,7 @@ reset: } 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; @@ -790,21 +842,9 @@ int utcp_shutdown(struct utcp_connection *c, int dir) { return 0; } - // Send FIN - - struct hdr hdr; + c->snd.last++; - 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; } @@ -1007,8 +1047,13 @@ 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); }