+ debug("%p accepted, %p %p\n", c, recv, priv);
+ c->recv = recv;
+ c->priv = priv;
+ set_state(c, ESTABLISHED);
+}
+
+static void ack(struct utcp_connection *c, bool sendatleastone) {
+ int32_t left = seqdiff(c->snd.last, c->snd.nxt);
+ int32_t cwndleft = min(c->snd.cwnd, c->snd.wnd) - seqdiff(c->snd.nxt, c->snd.una);
+
+ assert(left >= 0);
+
+ if(cwndleft <= 0) {
+ left = 0;
+ } else if(cwndleft < left) {
+ left = cwndleft;
+
+ if(!sendatleastone || cwndleft > c->utcp->mtu) {
+ left -= left % c->utcp->mtu;
+ }
+ }
+
+ debug("cwndleft = %d, left = %d\n", cwndleft, left);
+
+ if(!left && !sendatleastone) {
+ return;
+ }
+
+ struct {
+ struct hdr hdr;
+ uint8_t data[];
+ } *pkt;
+
+ pkt = malloc(sizeof(pkt->hdr) + c->utcp->mtu);
+
+ if(!pkt) {
+ return;
+ }
+
+ pkt->hdr.src = c->src;
+ pkt->hdr.dst = c->dst;
+ pkt->hdr.ack = c->rcv.nxt;
+ pkt->hdr.wnd = c->rcvbuf.maxsize;
+ pkt->hdr.ctl = ACK;
+ pkt->hdr.aux = 0;
+
+ do {
+ uint32_t seglen = left > c->utcp->mtu ? c->utcp->mtu : left;
+ pkt->hdr.seq = c->snd.nxt;
+
+ buffer_copy(&c->sndbuf, pkt->data, seqdiff(c->snd.nxt, c->snd.una), seglen);
+
+ c->snd.nxt += seglen;
+ left -= seglen;
+
+ if(seglen && fin_wanted(c, c->snd.nxt)) {
+ seglen--;
+ pkt->hdr.ctl |= FIN;
+ }
+
+ if(!c->rtt_start.tv_sec) {
+ // Start RTT measurement
+ gettimeofday(&c->rtt_start, NULL);
+ c->rtt_seq = pkt->hdr.seq + seglen;
+ debug("Starting RTT measurement, expecting ack %u\n", c->rtt_seq);
+ }
+
+ print_packet(c->utcp, "send", pkt, sizeof(pkt->hdr) + seglen);
+ c->utcp->send(c->utcp, pkt, sizeof(pkt->hdr) + seglen);
+ } while(left);
+
+ free(pkt);
+}
+
+ssize_t utcp_send(struct utcp_connection *c, const void *data, size_t len) {
+ if(c->reapable) {
+ debug("Error: send() called on closed connection %p\n", c);
+ errno = EBADF;
+ return -1;
+ }
+
+ switch(c->state) {
+ case CLOSED:
+ case LISTEN:
+ debug("Error: send() called on unconnected connection %p\n", c);
+ errno = ENOTCONN;
+ return -1;
+
+ case SYN_SENT:
+ case SYN_RECEIVED:
+ case ESTABLISHED:
+ case CLOSE_WAIT:
+ break;
+
+ case FIN_WAIT_1:
+ case FIN_WAIT_2:
+ case CLOSING:
+ case LAST_ACK:
+ case TIME_WAIT:
+ debug("Error: send() called on closing connection %p\n", c);
+ errno = EPIPE;
+ return -1;
+ }
+
+ // Exit early if we have nothing to send.
+
+ if(!len) {
+ return 0;
+ }
+
+ if(!data) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ // Check if we need to be able to buffer all data
+
+ if(c->flags & UTCP_NO_PARTIAL) {
+ if(len > buffer_free(&c->sndbuf)) {
+ if(len > c->sndbuf.maxsize) {
+ errno = EMSGSIZE;
+ return -1;
+ } else {
+ errno = EWOULDBLOCK;
+ return 0;
+ }
+ }
+ }
+
+ // Add data to send buffer.
+
+ if(is_reliable(c) || (c->state != SYN_SENT && c->state != SYN_RECEIVED)) {
+ len = buffer_put(&c->sndbuf, data, len);
+ } else {
+ return 0;
+ }
+
+ if(len <= 0) {
+ if(is_reliable(c)) {
+ errno = EWOULDBLOCK;
+ return 0;
+ } else {
+ return len;
+ }
+ }
+
+ c->snd.last += len;
+
+ // Don't send anything yet if the connection has not fully established yet
+
+ if(c->state == SYN_SENT || c->state == SYN_RECEIVED) {
+ return len;
+ }
+
+ ack(c, false);
+
+ 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);
+ }
+
+ if(is_reliable(c) && !timerisset(&c->conn_timeout)) {
+ gettimeofday(&c->conn_timeout, NULL);
+ c->conn_timeout.tv_sec += c->utcp->timeout;
+ }
+
+ return len;
+}
+
+static void swap_ports(struct hdr *hdr) {
+ uint16_t tmp = hdr->src;
+ hdr->src = hdr->dst;
+ hdr->dst = tmp;
+}
+
+static void fast_retransmit(struct utcp_connection *c) {
+ if(c->state == CLOSED || c->snd.last == c->snd.una) {
+ debug("fast_retransmit() called but nothing to retransmit!\n");
+ return;
+ }
+
+ struct utcp *utcp = c->utcp;
+
+ struct {
+ struct hdr hdr;
+ uint8_t data[];
+ } *pkt;
+
+ pkt = malloc(sizeof(pkt->hdr) + c->utcp->mtu);
+
+ if(!pkt) {
+ return;
+ }
+
+ pkt->hdr.src = c->src;
+ pkt->hdr.dst = c->dst;
+ pkt->hdr.wnd = c->rcvbuf.maxsize;
+ pkt->hdr.aux = 0;
+
+ switch(c->state) {
+ 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 = min(seqdiff(c->snd.last, c->snd.una), utcp->mtu);
+
+ if(fin_wanted(c, c->snd.una + len)) {
+ len--;
+ pkt->hdr.ctl |= FIN;
+ }
+
+ 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:
+ break;
+ }
+
+ free(pkt);
+}
+
+static void retransmit(struct utcp_connection *c) {
+ if(c->state == CLOSED || c->snd.last == c->snd.una) {
+ debug("Retransmit() called but nothing to retransmit!\n");
+ stop_retransmit_timer(c);
+ return;
+ }
+
+ struct utcp *utcp = c->utcp;
+
+ struct {
+ struct hdr hdr;
+ uint8_t data[];
+ } *pkt;
+
+ pkt = malloc(sizeof(pkt->hdr) + c->utcp->mtu);
+
+ if(!pkt) {
+ return;
+ }
+
+ pkt->hdr.src = c->src;
+ pkt->hdr.dst = c->dst;
+ pkt->hdr.wnd = c->rcvbuf.maxsize;
+ pkt->hdr.aux = 0;
+
+ switch(c->state) {
+ case SYN_SENT:
+ // Send our SYN again
+ pkt->hdr.seq = c->snd.iss;
+ pkt->hdr.ack = 0;
+ pkt->hdr.ctl = SYN;
+ pkt->hdr.aux = 0x0101;
+ pkt->data[0] = 1;
+ pkt->data[1] = 0;
+ pkt->data[2] = 0;
+ pkt->data[3] = c->flags & 0x7;
+ print_packet(c->utcp, "rtrx", pkt, sizeof(pkt->hdr) + 4);
+ utcp->send(utcp, pkt, sizeof(pkt->hdr) + 4);
+ break;
+
+ case SYN_RECEIVED:
+ // Send SYNACK again
+ pkt->hdr.seq = c->snd.nxt;
+ pkt->hdr.ack = c->rcv.nxt;
+ pkt->hdr.ctl = SYN | ACK;
+ print_packet(c->utcp, "rtrx", pkt, sizeof(pkt->hdr));
+ utcp->send(utcp, pkt, sizeof(pkt->hdr));
+ break;
+
+ 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.last, c->snd.una);
+
+ if(len > utcp->mtu) {
+ len = utcp->mtu;
+ }
+
+ if(fin_wanted(c, c->snd.una + len)) {
+ len--;
+ pkt->hdr.ctl |= FIN;
+ }
+
+ c->snd.nxt = c->snd.una + len;
+
+ // RFC 5681 slow start after timeout
+ c->snd.ssthresh = max(c->snd.cwnd / 2, utcp->mtu * 2); // eq. 4
+ c->snd.cwnd = utcp->mtu;
+ debug_cwnd(c);
+
+ 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;
+
+ 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
+ stop_retransmit_timer(c);
+ goto cleanup;
+ }
+
+ start_retransmit_timer(c);
+ utcp->rto *= 2;