X-Git-Url: http://git.meshlink.io/?p=utcp;a=blobdiff_plain;f=utcp.c;h=c650ab700ad150fa876b0944a6872ea39298f352;hp=57b3157ced701ce46ae824e2e57c491d03129500;hb=5ded633782709710c42648c71154d6062e83b7a6;hpb=c66102829d1fce09cdf8dcba94b14255448a2395 diff --git a/utcp.c b/utcp.c index 57b3157..c650ab7 100644 --- a/utcp.c +++ b/utcp.c @@ -49,7 +49,7 @@ (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;\ + (r)->tv_sec--, (r)->tv_usec += USEC_PER_SEC;\ } while (0) #endif @@ -70,12 +70,12 @@ static void debug(const char *format, ...) { static void print_packet(struct utcp *utcp, const char *dir, const void *pkt, size_t len) { struct hdr hdr; if(len < sizeof hdr) { - debug("%p %s: short packet (%zu bytes)\n", utcp, dir, len); + debug("%p %s: short packet (%lu bytes)\n", utcp, dir, (unsigned long)len); return; } memcpy(&hdr, pkt, sizeof hdr); - 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); + debug("%p %s: len=%lu, src=%u dst=%u seq=%u ack=%u wnd=%u ctl=", utcp, dir, (unsigned long)len, hdr.src, hdr.dst, hdr.seq, hdr.ack, hdr.wnd); if(hdr.ctl & SYN) debug("SYN"); if(hdr.ctl & RST) @@ -87,27 +87,17 @@ static void print_packet(struct utcp *utcp, const char *dir, const void *pkt, si if(len > sizeof hdr) { uint32_t datalen = len - sizeof hdr; - uint8_t *str = malloc((datalen << 1) + 7); - if(!str) { - debug("out of memory"); - return; - } - memcpy(str, " data=", 6); - uint8_t *strptr = str + 6; - const uint8_t *data = pkt; - const uint8_t *dataend = data + datalen; - - while(data != dataend) { - *strptr = (*data >> 4) > 9? (*data >> 4) + 55 : (*data >> 4) + 48; - ++strptr; - *strptr = (*data & 0xf) > 9? (*data & 0xf) + 55 : (*data & 0xf) + 48; - ++strptr; - ++data; + const uint8_t *data = (uint8_t *)pkt + sizeof hdr; + char str[datalen * 2 + 1]; + char *p = str; + + for(uint32_t i = 0; i < datalen; i++) { + *p++ = "0123456789ABCDEF"[data[i] >> 4]; + *p++ = "0123456789ABCDEF"[data[i] & 15]; } - *strptr = 0; + *p = 0; - debug(str); - free(str); + debug(" data=%s", str); } debug("\n"); @@ -137,6 +127,10 @@ static bool fin_wanted(struct utcp_connection *c, uint32_t seq) { } } +static bool is_reliable(struct utcp_connection *c) { + return c->flags & UTCP_RELIABLE; +} + static inline void list_connections(struct utcp *utcp) { debug("%p has %d connections:\n", utcp, utcp->nconnections); for(int i = 0; i < utcp->nconnections; i++) @@ -152,16 +146,12 @@ static int32_t seqdiff(uint32_t a, uint32_t b) { // Store data into the buffer static ssize_t buffer_put_at(struct buffer *buf, size_t offset, const void *data, size_t len) { - if(buf->maxsize <= buf->used) - return 0; - - debug("buffer_put_at %zu %zu %zu\n", buf->used, offset, len); + debug("buffer_put_at %lu %lu %lu\n", (unsigned long)buf->used, (unsigned long)offset, (unsigned long)len); size_t required = offset + len; if(required > buf->maxsize) { if(offset >= buf->maxsize) return 0; - abort(); len = buf->maxsize - offset; required = buf->maxsize; } @@ -173,7 +163,7 @@ static ssize_t buffer_put_at(struct buffer *buf, size_t offset, const void *data } else { do { newsize *= 2; - } while(newsize < buf->used + len); + } while(newsize < required); } if(newsize > buf->maxsize) newsize = buf->maxsize; @@ -392,28 +382,38 @@ static void stop_retransmit_timer(struct utcp_connection *c) { debug("timeout cleared\n"); } -struct utcp_connection *utcp_connect(struct utcp *utcp, uint16_t dst, utcp_recv_t recv, void *priv) { +struct utcp_connection *utcp_connect_ex(struct utcp *utcp, uint16_t dst, utcp_recv_t recv, void *priv, uint32_t flags) { struct utcp_connection *c = allocate_connection(utcp, 0, dst); if(!c) return NULL; + assert((flags & ~0xf) == 0); + + c->flags = flags; c->recv = recv; c->priv = priv; - struct hdr hdr; - - hdr.src = c->src; - hdr.dst = c->dst; - hdr.seq = c->snd.iss; - hdr.ack = 0; - hdr.wnd = c->rcv.wnd; - hdr.ctl = SYN; - hdr.aux = 0; + struct { + struct hdr hdr; + uint8_t init[4]; + } pkt; + + pkt.hdr.src = c->src; + pkt.hdr.dst = c->dst; + pkt.hdr.seq = c->snd.iss; + pkt.hdr.ack = 0; + pkt.hdr.wnd = c->rcv.wnd; + pkt.hdr.ctl = SYN; + pkt.hdr.aux = 0x0101; + pkt.init[0] = 1; + pkt.init[1] = 0; + pkt.init[2] = 0; + pkt.init[3] = flags & 0x7; set_state(c, SYN_SENT); - print_packet(utcp, "send", &hdr, sizeof hdr); - utcp->send(utcp, &hdr, sizeof hdr); + print_packet(utcp, "send", &pkt, sizeof pkt); + utcp->send(utcp, &pkt, sizeof pkt); gettimeofday(&c->conn_timeout, NULL); c->conn_timeout.tv_sec += utcp->timeout; @@ -421,6 +421,10 @@ struct utcp_connection *utcp_connect(struct utcp *utcp, uint16_t dst, utcp_recv_ return c; } +struct utcp_connection *utcp_connect(struct utcp *utcp, uint16_t dst, utcp_recv_t recv, void *priv) { + return utcp_connect_ex(utcp, dst, recv, priv, UTCP_TCP); +} + void utcp_accept(struct utcp_connection *c, utcp_recv_t recv, void *priv) { if(c->reapable || c->state != SYN_RECEIVED) { debug("Error: accept() called on invalid connection %p in state %s\n", c, strstate[c->state]); @@ -521,7 +525,7 @@ ssize_t utcp_send(struct utcp_connection *c, const void *data, size_t len) { return -1; } - // Add data to send buffer + // Exit early if we have nothing to send. if(!len) return 0; @@ -531,6 +535,8 @@ ssize_t utcp_send(struct utcp_connection *c, const void *data, size_t len) { return -1; } + // Add data to send buffer. + len = buffer_put(&c->sndbuf, data, len); if(len <= 0) { errno = EWOULDBLOCK; @@ -539,7 +545,11 @@ ssize_t utcp_send(struct utcp_connection *c, const void *data, size_t len) { c->snd.last += len; ack(c, false); - if(!timerisset(&c->rtrx_timeout)) + if(!is_reliable(c)) { + c->snd.una = c->snd.nxt = c->snd.last; + buffer_get(&c->sndbuf, NULL, c->sndbuf.used); + } + if(is_reliable(c) && !timerisset(&c->rtrx_timeout)) start_retransmit_timer(c); return len; } @@ -656,9 +666,12 @@ cleanup: * - the SACK entry is completely before ^, in that case delete it. */ static void sack_consume(struct utcp_connection *c, size_t len) { - debug("sack_consume %zu\n", len); - if(len > c->rcvbuf.used) - abort(); + debug("sack_consume %lu\n", (unsigned long)len); + if(len > c->rcvbuf.used) { + debug("All SACK entries consumed"); + c->sacks[0].len = 0; + return; + } buffer_get(&c->rcvbuf, NULL, len); @@ -706,6 +719,8 @@ static void handle_out_of_order(struct utcp_connection *c, uint32_t offset, cons memmove(&c->sacks[i + 1], &c->sacks[i], (NSACKS - i - 1) * sizeof c->sacks[i]); c->sacks[i].offset = offset; c->sacks[i].len = rxd; + } else { + debug("SACK entries full, dropping packet\n"); } break; } else { // merge @@ -730,7 +745,7 @@ static void handle_out_of_order(struct utcp_connection *c, uint32_t offset, cons static void handle_in_order(struct utcp_connection *c, const void *data, size_t len) { // Check if we can process out-of-order data now. if(c->sacks[0].len && len >= c->sacks[0].offset) { // TODO: handle overlap with second SACK - debug("incoming packet len %zu connected with SACK at %u\n", len, c->sacks[0].offset); + debug("incoming packet len %lu connected with SACK at %u\n", (unsigned long)len, c->sacks[0].offset); buffer_put_at(&c->rcvbuf, 0, data, len); // TODO: handle return value len = max(len, c->sacks[0].offset + c->sacks[0].len); data = c->rcvbuf.data; @@ -752,6 +767,12 @@ static void handle_in_order(struct utcp_connection *c, const void *data, size_t static void handle_incoming_data(struct utcp_connection *c, uint32_t seq, const void *data, size_t len) { + if(!is_reliable(c)) { + c->recv(c, data, len); + c->rcv.nxt = seq + len; + return; + } + uint32_t offset = seqdiff(seq, c->rcv.nxt); if(offset + len > c->rcvbuf.maxsize) abort(); @@ -828,6 +849,19 @@ ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) { goto reset; } + // Parse auxilliary information + if(hdr.aux) { + if(hdr.aux != 0x0101 || len < 4 || ((uint8_t *)data)[0] != 1) { + len = 1; + goto reset; + } + c->flags = ((uint8_t *)data)[3] & 0x7; + data += 4; + len -= 4; + } else { + c->flags = UTCP_TCP; + } + // Return SYN+ACK, go to SYN_RECEIVED state c->snd.wnd = hdr.wnd; c->rcv.irs = hdr.seq; @@ -855,8 +889,10 @@ ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) { // In case this is for a CLOSED connection, ignore the packet. // TODO: make it so incoming packets can never match a CLOSED connection. - if(c->state == CLOSED) + if(c->state == CLOSED) { + debug("Got packet for closed connection\n"); return 0; + } // It is for an existing connection. @@ -897,24 +933,24 @@ ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) { // cut already accepted front overlapping if(rcv_offset < 0) { - acceptable = rcv_offset + len >= 0; + acceptable = len > -rcv_offset; if(acceptable) { data -= rcv_offset; len += rcv_offset; + hdr.seq -= rcv_offset; } + } else { + acceptable = seqdiff(hdr.seq, c->rcv.nxt) >= 0 && seqdiff(hdr.seq, c->rcv.nxt) + len <= c->rcvbuf.maxsize; } - - acceptable = seqdiff(hdr.seq, c->rcv.nxt) >= 0 && seqdiff(hdr.seq, c->rcv.nxt) + len <= c->rcvbuf.maxsize; } if(!acceptable) { - debug("Packet not acceptable, %u <= %u + %zu < %u\n", c->rcv.nxt, hdr.seq, len, c->rcv.nxt + c->rcvbuf.maxsize); + debug("Packet not acceptable, %u <= %u + %lu < %u\n", c->rcv.nxt, hdr.seq, (unsigned long)len, c->rcv.nxt + c->rcvbuf.maxsize); // Ignore unacceptable RST packets. if(hdr.ctl & RST) return 0; - // Otherwise, send an ACK back in the hope things improve. - ack(c, true); - return 0; + // Otherwise, continue processing. + len = 0; } c->snd.wnd = hdr.wnd; // TODO: move below @@ -1052,7 +1088,7 @@ ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) { break; } } else { - if(!len) { + if(!len && is_reliable(c)) { c->dupack++; if(c->dupack == 3) { debug("Triplicate ACK\n"); @@ -1072,7 +1108,7 @@ ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) { 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.last) stop_retransmit_timer(c); - else + else if(is_reliable(c)) start_retransmit_timer(c); } @@ -1292,7 +1328,7 @@ int utcp_shutdown(struct utcp_connection *c, int dir) { } int utcp_close(struct utcp_connection *c) { - if(utcp_shutdown(c, SHUT_RDWR)) + if(utcp_shutdown(c, SHUT_RDWR) && errno != ENOTCONN) return -1; c->recv = NULL; c->poll = NULL; @@ -1432,8 +1468,8 @@ struct utcp *utcp_init(utcp_accept_t accept, utcp_pre_accept_t pre_accept, utcp_ utcp->send = send; utcp->priv = priv; utcp->mtu = DEFAULT_MTU; - utcp->timeout = DEFAULT_USER_TIMEOUT; // s - utcp->rto = START_RTO; // us + utcp->timeout = DEFAULT_USER_TIMEOUT; // sec + utcp->rto = START_RTO; // usec return utcp; }