+/*
+ * Attach TCP protocol to socket, allocating
+ * internet protocol control block, tcp control block,
+ * bufer space, and entering LISTEN state if to accept connections.
+ */
+tcp_attach(so, sa)
+ struct socket *so;
+ struct sockaddr *sa;
+{
+ register struct tcpcb *tp;
+ struct inpcb *inp;
+ int error;
+
+ error = in_pcbattach(so, &tcb, 2048, 2048, (struct sockaddr_in *)sa);
+ if (error)
+ return (error);
+ inp = (struct inpcb *)so->so_pcb;
+ tp = tcp_newtcpcb(inp);
+ if (so->so_options & SO_ACCEPTCONN) {
+ if (tp == 0) {
+ in_pcbdetach(inp);
+ return (ENOBUFS);
+ }
+ tp->t_state = TCPS_LISTEN;
+ } else
+ tp->t_state = TCPS_CLOSED;
+ return (0);
+}
+
+/*
+ * Initiate (or continue) disconnect.
+ * If embryonic state, just send reset (once).
+ * If not in ``let data drain'' option, just drop.
+ * Otherwise (hard), mark socket disconnecting and drop
+ * current input data; switch states based on user close, and
+ * send segment to peer (with FIN).
+ */
+tcp_disconnect(tp)
+ struct tcpcb *tp;
+{
+ struct socket *so = tp->t_inpcb->inp_socket;
+
+ if (tp->t_state < TCPS_ESTABLISHED)
+ tcp_close(tp);
+ else if ((so->so_options & SO_LETDATADRAIN) == 0)
+ tcp_drop(tp, 0);
+ else {
+ soisdisconnecting(so);
+ sbflush(&so->so_rcv);
+ tcp_usrclosed(tp);
+ (void) tcp_output(tp);
+ }
+}
+
+/*
+ * User issued close, and wish to trail through shutdown states:
+ * if never received SYN, just forget it. If got a SYN from peer,
+ * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
+ * If already got a FIN from peer, then almost done; go to LAST_ACK
+ * state. In all other cases, have already sent FIN to peer (e.g.
+ * after PRU_SHUTDOWN), and just have to play tedious game waiting
+ * for peer to send FIN or not respond to keep-alives, etc.
+ */