+ if(hdr.ctl & ACK)
+ return 0;
+ // As far as the application is concerned, the connection has already been closed.
+ // If it has called utcp_close() already, we can immediately free this connection.
+ if(c->reapable) {
+ free_connection(c);
+ return 0;
+ }
+ // Otherwise, immediately move to the CLOSED state.
+ set_state(c, CLOSED);
+ return 0;
+ default:
+ abort();
+ }
+ }
+
+ // 3. Advance snd.una
+
+ uint32_t advanced = seqdiff(hdr.ack, c->snd.una);
+ c->snd.una = hdr.ack;
+
+ if(advanced) {
+ debug("%p advanced %u\n", utcp, advanced);
+ // 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);
+ c->dupack = 0;
+ 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);
+ }