X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=utcp.c;h=7624aa576d39c8753c810cea4c4f8643c377d3d4;hb=5d6a4bd71c683042495038e9679cbf7c80d816fd;hp=21fbea6c4eea4df28380098bdff43edb33441775;hpb=a521d405a461b3384cd70daf95a2219d72db3eb1;p=utcp diff --git a/utcp.c b/utcp.c index 21fbea6..7624aa5 100644 --- a/utcp.c +++ b/utcp.c @@ -103,6 +103,19 @@ static void set_state(struct utcp_connection *c, enum state state) { debug("%p new state: %s\n", c->utcp, strstate[state]); } +static bool fin_wanted(struct utcp_connection *c, uint32_t seq) { + if(seq != c->snd.last) + return false; + switch(c->state) { + case FIN_WAIT_1: + case CLOSING: + case LAST_ACK: + return true; + default: + return false; + } +} + 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++) @@ -366,16 +379,9 @@ static void ack(struct utcp_connection *c, bool sendatleastone) { c->snd.nxt += seglen; left -= seglen; - if(c->state != ESTABLISHED && seglen && c->snd.nxt == c->snd.last) { - switch(c->state) { - case FIN_WAIT_1: - case CLOSING: - seglen--; - pkt->hdr.ctl |= FIN; - break; - default: - break; - } + if(seglen && fin_wanted(c, c->snd.nxt)) { + seglen--; + pkt->hdr.ctl |= FIN; } print_packet(c->utcp, "send", pkt, sizeof pkt->hdr + seglen); @@ -459,11 +465,8 @@ static void retransmit(struct utcp_connection *c) { pkt->hdr.dst = c->dst; switch(c->state) { - case LISTEN: - // TODO: this should not happen - break; - case SYN_SENT: + // Send our SYN again pkt->hdr.seq = c->snd.iss; pkt->hdr.ack = 0; pkt->hdr.wnd = c->rcv.wnd; @@ -473,6 +476,7 @@ static void retransmit(struct utcp_connection *c) { break; case SYN_RECEIVED: + // Send SYNACK again pkt->hdr.seq = c->snd.nxt; pkt->hdr.ack = c->rcv.nxt; pkt->hdr.ctl = SYN | ACK; @@ -482,26 +486,36 @@ static void retransmit(struct utcp_connection *c) { case ESTABLISHED: case FIN_WAIT_1: + case CLOSE_WAIT: + case CLOSING: + case LAST_ACK: + // Send unacked data again. 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--; + uint32_t len = seqdiff(c->snd.last, c->snd.una); if(len > utcp->mtu) len = utcp->mtu; - else { - if(c->state == FIN_WAIT_1) - pkt->hdr.ctl |= FIN; + if(fin_wanted(c, c->snd.una + len)) { + len--; + pkt->hdr.ctl |= FIN; } + c->snd.nxt = c->snd.una + len; buffer_copy(&c->sndbuf, pkt->data, 0, len); print_packet(c->utcp, "rtrx", pkt, sizeof pkt->hdr + len); utcp->send(utcp, pkt, sizeof pkt->hdr + len); break; - default: - // TODO: implement + case CLOSED: + case LISTEN: + case TIME_WAIT: + case FIN_WAIT_2: + // We shouldn't need to retransmit anything in this state. +#ifdef UTCP_DEBUG abort(); +#endif + timerclear(&c->rtrx_timeout); + break; } free(pkt); @@ -623,7 +637,10 @@ ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) { case TIME_WAIT: break; default: +#ifdef UTCP_DEBUG abort(); +#endif + break; } // 1b. Drop packets with a sequence number not in our receive window. @@ -723,7 +740,10 @@ ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) { set_state(c, CLOSED); return 0; default: +#ifdef UTCP_DEBUG abort(); +#endif + break; } } @@ -822,7 +842,10 @@ ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) { // Ehm, no. We should never receive a second SYN. goto reset; default: +#ifdef UTCP_DEBUG abort(); +#endif + return 0; } // SYN counts as one sequence number @@ -852,7 +875,10 @@ ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) { case SYN_SENT: case SYN_RECEIVED: // This should never happen. +#ifdef UTCP_DEBUG abort(); +#endif + return 0; case ESTABLISHED: case FIN_WAIT_1: case FIN_WAIT_2: @@ -864,7 +890,10 @@ ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) { // Ehm no, We should never receive more data after a FIN. goto reset; default: +#ifdef UTCP_DEBUG abort(); +#endif + return 0; } ssize_t rxd; @@ -893,7 +922,10 @@ ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) { case SYN_SENT: case SYN_RECEIVED: // This should never happen. +#ifdef UTCP_DEBUG abort(); +#endif + break; case ESTABLISHED: set_state(c, CLOSE_WAIT); break; @@ -912,7 +944,10 @@ ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) { // Ehm, no. We should never receive a second FIN. goto reset; default: +#ifdef UTCP_DEBUG abort(); +#endif + break; } // FIN counts as one sequence number @@ -954,7 +989,7 @@ reset: } int utcp_shutdown(struct utcp_connection *c, int dir) { - debug("%p shutdown %d at %u\n", c ? c->utcp : NULL, dir, c->snd.last); + debug("%p shutdown %d at %u\n", c ? c->utcp : NULL, dir, c ? c->snd.last : 0); if(!c) { errno = EFAULT; return -1; @@ -966,13 +1001,26 @@ int utcp_shutdown(struct utcp_connection *c, int dir) { return -1; } - // TODO: handle dir - // TODO: check that repeated calls with the same parameters should have no effect + if(!(dir == UTCP_SHUT_RD || dir == UTCP_SHUT_WR || dir == UTCP_SHUT_RDWR)) { + errno = EINVAL; + return -1; + } + + // TCP does not have a provision for stopping incoming packets. + // The best we can do is to just ignore them. + if(dir == UTCP_SHUT_RD || dir == UTCP_SHUT_RDWR) + c->recv = NULL; + + // The rest of the code deals with shutting down writes. + if(dir == UTCP_SHUT_RD) + return 0; switch(c->state) { case CLOSED: - return 0; case LISTEN: + errno = ENOTCONN; + return -1; + case SYN_SENT: set_state(c, CLOSED); return 0; @@ -1120,6 +1168,17 @@ struct timeval utcp_timeout(struct utcp *utcp) { return diff; } +bool utcp_is_active(struct utcp *utcp) { + if(!utcp) + return false; + + for(int i = 0; i < utcp->nconnections; i++) + if(utcp->connections[i]->state != CLOSED && utcp->connections[i]->state != TIME_WAIT) + return true; + + return false; +} + struct utcp *utcp_init(utcp_accept_t accept, utcp_pre_accept_t pre_accept, utcp_send_t send, void *priv) { struct utcp *utcp = calloc(1, sizeof *utcp); if(!utcp) @@ -1222,3 +1281,10 @@ void utcp_set_poll_cb(struct utcp_connection *c, utcp_poll_t poll) { if(c) c->poll = poll; } + +void utcp_set_accept_cb(struct utcp *utcp, utcp_accept_t accept, utcp_pre_accept_t pre_accept) { + if(utcp) { + utcp->accept = accept; + utcp->pre_accept = pre_accept; + } +}