+ 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);
+ uint32_t prevrcvnxt = c->rcv.nxt;
+
+ if(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 = 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);
+ }