+ switch (s->state) {
+
+ case _SYN_SENT:
+ /* active open not supported */
+ if (tcp->flags != (TCP_FLAG_SYN | TCP_FLAG_ACK)) {
+ do_reset(s);
+ __pktbuf_free(pkt);
+ return;
+ }
+ s->state = _ESTABLISHED;
+ s->ack = ntohl(tcp->seqnum) + 1;
+ s->seq = ntohl(tcp->acknum);
+ __timer_cancel(&s->timer);
+ send_ack(s);
+ break;
+
+ case _LISTEN:
+ if (tcp->flags & TCP_FLAG_SYN) {
+ s->state = _SYN_RCVD;
+ s->ack = ntohl(tcp->seqnum) + 1;
+ s->his_port = ntohs(tcp->src_port);
+ memcpy(s->his_addr.ip_addr, r->ip_addr, sizeof(ip_addr_t));
+ s->data_bytes = 0;
+
+ BSPLOG(bsp_log("SYN from %d.%d.%d.%d:%d (seq %x)\n",
+ s->his_addr.ip_addr[0],s->his_addr.ip_addr[1],
+ s->his_addr.ip_addr[2],s->his_addr.ip_addr[3],
+ s->his_port, ntohl(tcp->seqnum)));
+
+ tcp_send(s, TCP_FLAG_SYN | TCP_FLAG_ACK, 0);
+ }
+ else
+ send_reset(pkt, r);
+ break;
+
+ case _SYN_RCVD:
+ BSPLOG(bsp_log("_SYN_RCVD timer cancel.\n"));
+ __timer_cancel(&s->timer);
+
+ /* go back to _LISTEN state if reset */
+ if (tcp->flags & TCP_FLAG_RST) {
+ s->state = _LISTEN;
+
+ BSPLOG(bsp_log("_SYN_RCVD --> _LISTEN\n"));
+
+ } else if (tcp->flags & TCP_FLAG_SYN) {
+ /* apparently our SYN/ACK was lost? */
+ tcp_send(s, 0, 1);
+
+ BSPLOG(bsp_log("retransmitting SYN/ACK\n"));
+
+ } else if ((tcp->flags & TCP_FLAG_ACK) &&
+ ntohl(tcp->acknum) == (s->seq + 1)) {
+ /* we've established the connection */
+ s->state = _ESTABLISHED;
+ s->seq++;
+
+ BSPLOG(bsp_log("ACK received - connection established\n"));
+ }
+ break;
+
+ case _ESTABLISHED:
+ case _CLOSE_WAIT:
+ ack = s->ack; /* save original ack */
+ if (tcp->flags & TCP_FLAG_ACK)
+ handle_ack(s, pkt);
+
+ queued = handle_data(s, pkt);
+
+ if ((tcp->flags & TCP_FLAG_FIN) &&
+ ntohl(tcp->seqnum) == s->ack) {
+
+ BSPLOG(bsp_log("FIN received - going to _CLOSE_WAIT\n"));
+
+ s->ack++;
+ s->state = _CLOSE_WAIT;
+ }
+ /*
+ * Send an ack if neccessary.
+ */
+ if (s->ack != ack || pkt->pkt_bytes > (tcp->hdr_len << 2))
+ send_ack(s);
+ break;
+
+ case _LAST_ACK:
+ if (tcp->flags & TCP_FLAG_ACK) {
+ handle_ack(s, pkt);
+ if (ntohl(tcp->acknum) == (s->seq + 1)) {
+ BSPLOG(bsp_log("_LAST_ACK --> _CLOSED\n"));
+ s->state = _CLOSED;
+ unlink_socket(s);
+ }
+ }
+ break;
+
+ case _FIN_WAIT_1:
+ if (tcp->flags & TCP_FLAG_ACK) {
+ handle_ack(s, pkt);
+ if (ntohl(tcp->acknum) == (s->seq + 1)) {
+ /* got ACK for FIN packet */
+ s->seq++;
+ if (tcp->flags & TCP_FLAG_FIN) {
+ BSPLOG(bsp_log("_FIN_WAIT_1 --> _TIME_WAIT\n"));
+ s->ack++;
+ s->state = _TIME_WAIT;
+ send_ack(s);
+ } else {
+ s->state = _FIN_WAIT_2;
+ BSPLOG(bsp_log("_FIN_WAIT_1 --> _FIN_WAIT_2\n"));
+ }
+ break; /* All done for now */
+ }
+ }
+ /* At this point, no ACK for FIN has been seen, so check for
+ simultaneous close */
+ if (tcp->flags & TCP_FLAG_FIN) {
+ BSPLOG(bsp_log("_FIN_WAIT_1 --> _CLOSING\n"));
+ __timer_cancel(&s->timer);
+ s->ack++;
+ s->state = _CLOSING;
+ /* FIN is resent so the timeout and retry for this packet
+ will also take care of timeout and resend of the
+ previously sent FIN (which got us to FIN_WAIT_1). While
+ not technically correct, resending FIN only causes a
+ duplicate FIN (same sequence number) which should be
+ ignored by the other end. */
+ tcp_send(s, TCP_FLAG_FIN | TCP_FLAG_ACK, 0);
+ }
+ break;
+
+ case _FIN_WAIT_2:
+ queued = handle_data(s, pkt);
+ if (tcp->flags & TCP_FLAG_FIN) {
+ BSPLOG(bsp_log("_FIN_WAIT_2 --> _TIME_WAIT\n"));
+ s->ack++;
+ s->state = _TIME_WAIT;
+ send_ack(s);
+ }
+ break;
+
+ case _CLOSING:
+ if (tcp->flags & TCP_FLAG_ACK) {
+ handle_ack(s, pkt);
+ if (ntohl(tcp->acknum) == (s->seq + 1)) {
+ /* got ACK for FIN packet */
+ BSPLOG(bsp_log("_CLOSING --> _TIME_WAIT\n"));
+ __timer_cancel(&s->timer);
+ s->state = _TIME_WAIT;
+ }
+ }
+ break;
+
+ case _TIME_WAIT:
+ BSPLOG(bsp_log("_TIME_WAIT resend.\n"));
+ if (tcp->flags & TCP_FLAG_FIN)
+ tcp_send(s, 0, 1); /* just resend ack */
+ break;