getpeer
[unix-history] / usr / src / sys / netinet / tcp_usrreq.c
index 5f4e32f..51a7a44 100644 (file)
@@ -1,4 +1,4 @@
-/*     tcp_usrreq.c    1.59    82/06/20        */
+/*     tcp_usrreq.c    1.81    83/07/25        */
 
 #include "../h/param.h"
 #include "../h/systm.h"
 
 #include "../h/param.h"
 #include "../h/systm.h"
@@ -6,38 +6,41 @@
 #include "../h/socket.h"
 #include "../h/socketvar.h"
 #include "../h/protosw.h"
 #include "../h/socket.h"
 #include "../h/socketvar.h"
 #include "../h/protosw.h"
-#include "../net/in.h"
-#include "../net/route.h"
-#include "../net/in_pcb.h"
-#include "../net/in_systm.h"
+#include "../h/errno.h"
+
 #include "../net/if.h"
 #include "../net/if.h"
-#include "../net/ip.h"
-#include "../net/ip_var.h"
-#include "../net/tcp.h"
-#include "../net/tcp_fsm.h"
-#include "../net/tcp_seq.h"
-#include "../net/tcp_timer.h"
-#include "../net/tcp_var.h"
-#include "../net/tcpip.h"
-#include "../net/tcp_debug.h"
-#include <errno.h>
+#include "../net/route.h"
+
+#include "../netinet/in.h"
+#include "../netinet/in_pcb.h"
+#include "../netinet/in_systm.h"
+#include "../netinet/ip.h"
+#include "../netinet/ip_var.h"
+#include "../netinet/tcp.h"
+#include "../netinet/tcp_fsm.h"
+#include "../netinet/tcp_seq.h"
+#include "../netinet/tcp_timer.h"
+#include "../netinet/tcp_var.h"
+#include "../netinet/tcpip.h"
+#include "../netinet/tcp_debug.h"
 
 /*
  * TCP protocol interface to socket abstraction.
  */
 extern char *tcpstates[];
 struct tcpcb *tcp_newtcpcb();
 
 /*
  * TCP protocol interface to socket abstraction.
  */
 extern char *tcpstates[];
 struct tcpcb *tcp_newtcpcb();
+int    tcpsenderrors;
 
 /*
  * Process a TCP user request for TCP tb.  If this is a send request
  * then m is the mbuf chain of send data.  If this is a timer expiration
  * (called from the software clock routine), then timertype tells which timer.
  */
 
 /*
  * Process a TCP user request for TCP tb.  If this is a send request
  * then m is the mbuf chain of send data.  If this is a timer expiration
  * (called from the software clock routine), then timertype tells which timer.
  */
-tcp_usrreq(so, req, m, addr)
+/*ARGSUSED*/
+tcp_usrreq(so, req, m, nam, rights)
        struct socket *so;
        int req;
        struct socket *so;
        int req;
-       struct mbuf *m;
-       caddr_t addr;
+       struct mbuf *m, *nam, *rights;
 {
        register struct inpcb *inp = sotoinpcb(so);
        register struct tcpcb *tp;
 {
        register struct inpcb *inp = sotoinpcb(so);
        register struct tcpcb *tp;
@@ -45,20 +48,14 @@ tcp_usrreq(so, req, m, addr)
        int error = 0;
        int ostate;
 
        int error = 0;
        int ostate;
 
+       if (rights && rights->m_len) {
+               splx(s);
+               return (EINVAL);
+       }
        /*
         * When a TCP is attached to a socket, then there will be
         * a (struct inpcb) pointed at by the socket, and this
         * structure will point at a subsidary (struct tcpcb).
        /*
         * When a TCP is attached to a socket, then there will be
         * a (struct inpcb) pointed at by the socket, and this
         * structure will point at a subsidary (struct tcpcb).
-        * The normal sequence of events is:
-        *      PRU_ATTACH              creating these structures
-        *      PRU_CONNECT             connecting to a remote peer
-        *      (PRU_SEND|PRU_RCVD)*    exchanging data
-        *      PRU_DISCONNECT          disconnecting from remote peer
-        *      PRU_DETACH              deleting the structures
-        * With the operations from PRU_CONNECT through PRU_DISCONNECT
-        * possible repeated several times.
-        *
-        * MULTIPLE CONNECTS ARE NOT YET IMPLEMENTED.
         */
        if (inp == 0 && req != PRU_ATTACH) {
                splx(s);
         */
        if (inp == 0 && req != PRU_ATTACH) {
                splx(s);
@@ -66,28 +63,28 @@ tcp_usrreq(so, req, m, addr)
        }
        if (inp) {
                tp = intotcpcb(inp);
        }
        if (inp) {
                tp = intotcpcb(inp);
+               /* WHAT IF TP IS 0? */
 #ifdef KPROF
                tcp_acounts[tp->t_state][req]++;
 #endif
                ostate = tp->t_state;
 #ifdef KPROF
                tcp_acounts[tp->t_state][req]++;
 #endif
                ostate = tp->t_state;
-       }
+       } else
+               ostate = 0;
        switch (req) {
 
        /*
         * TCP attaches to socket via PRU_ATTACH, reserving space,
        switch (req) {
 
        /*
         * TCP attaches to socket via PRU_ATTACH, reserving space,
-        * and internet and TCP control blocks.
-        * If the socket is to receive connections,
-        * then the LISTEN state is entered.
+        * and an internet control block.
         */
        case PRU_ATTACH:
                if (inp) {
                        error = EISCONN;
                        break;
                }
         */
        case PRU_ATTACH:
                if (inp) {
                        error = EISCONN;
                        break;
                }
-               error = tcp_attach(so, (struct sockaddr *)addr);
+               error = tcp_attach(so);
                if (error)
                        break;
                if (error)
                        break;
-               if ((so->so_options & SO_DONTLINGER) == 0)
+               if ((so->so_options & SO_LINGER) && so->so_linger == 0)
                        so->so_linger = TCP_LINGERTIME;
                tp = sototcpcb(so);
                break;
                        so->so_linger = TCP_LINGERTIME;
                tp = sototcpcb(so);
                break;
@@ -101,11 +98,28 @@ tcp_usrreq(so, req, m, addr)
         */
        case PRU_DETACH:
                if (tp->t_state > TCPS_LISTEN)
         */
        case PRU_DETACH:
                if (tp->t_state > TCPS_LISTEN)
-                       tcp_disconnect(tp);
-               else {
-                       tcp_close(tp);
-                       tp = 0;
-               }
+                       tp = tcp_disconnect(tp);
+               else
+                       tp = tcp_close(tp);
+               break;
+
+       /*
+        * Give the socket an address.
+        */
+       case PRU_BIND:
+               error = in_pcbbind(inp, nam);
+               if (error)
+                       break;
+               break;
+
+       /*
+        * Prepare to accept connections.
+        */
+       case PRU_LISTEN:
+               if (inp->inp_lport == 0)
+                       error = in_pcbbind(inp, (struct mbuf *)0);
+               if (error == 0)
+                       tp->t_state = TCPS_LISTEN;
                break;
 
        /*
                break;
 
        /*
@@ -116,7 +130,12 @@ tcp_usrreq(so, req, m, addr)
         * Send initial segment on connection.
         */
        case PRU_CONNECT:
         * Send initial segment on connection.
         */
        case PRU_CONNECT:
-               error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
+               if (inp->inp_lport == 0) {
+                       error = in_pcbbind(inp, (struct mbuf *)0);
+                       if (error)
+                               break;
+               }
+               error = in_pcbconnect(inp, nam);
                if (error)
                        break;
                tp->t_template = tcp_template(tp);
                if (error)
                        break;
                tp->t_template = tcp_template(tp);
@@ -133,6 +152,13 @@ tcp_usrreq(so, req, m, addr)
                error = tcp_output(tp);
                break;
 
                error = tcp_output(tp);
                break;
 
+       /*
+        * Create a TCP connection between two sockets.
+        */
+       case PRU_CONNECT2:
+               error = EOPNOTSUPP;
+               break;
+
        /*
         * Initiate disconnect from peer.
         * If connection never passed embryonic stage, just drop;
        /*
         * Initiate disconnect from peer.
         * If connection never passed embryonic stage, just drop;
@@ -145,7 +171,7 @@ tcp_usrreq(so, req, m, addr)
         * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB.
         */
        case PRU_DISCONNECT:
         * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB.
         */
        case PRU_DISCONNECT:
-               tcp_disconnect(tp);
+               tp = tcp_disconnect(tp);
                break;
 
        /*
                break;
 
        /*
@@ -154,24 +180,23 @@ tcp_usrreq(so, req, m, addr)
         * of the peer, storing through addr.
         */
        case PRU_ACCEPT: {
         * of the peer, storing through addr.
         */
        case PRU_ACCEPT: {
-               struct sockaddr_in *sin = (struct sockaddr_in *)addr;
+               struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
 
 
-               if (sin) {
-                       bzero((caddr_t)sin, sizeof (*sin));
-                       sin->sin_family = AF_INET;
-                       sin->sin_port = inp->inp_fport;
-                       sin->sin_addr = inp->inp_faddr;
-               }
-               }
+               nam->m_len = sizeof (struct sockaddr_in);
+               sin->sin_family = AF_INET;
+               sin->sin_port = inp->inp_fport;
+               sin->sin_addr = inp->inp_faddr;
                break;
                break;
+               }
 
        /*
         * Mark the connection as being incapable of further output.
         */
        case PRU_SHUTDOWN:
                socantsendmore(so);
 
        /*
         * Mark the connection as being incapable of further output.
         */
        case PRU_SHUTDOWN:
                socantsendmore(so);
-               tcp_usrclosed(tp);
-               error = tcp_output(tp);
+               tp = tcp_usrclosed(tp);
+               if (tp)
+                       error = tcp_output(tp);
                break;
 
        /*
                break;
 
        /*
@@ -192,13 +217,18 @@ tcp_usrreq(so, req, m, addr)
                        tp->snd_end = tp->snd_una + so->so_snd.sb_cc;
 #endif
                error = tcp_output(tp);
                        tp->snd_end = tp->snd_una + so->so_snd.sb_cc;
 #endif
                error = tcp_output(tp);
+               if (error) {            /* XXX fix to use other path */
+                       if (error == ENOBUFS)           /* XXX */
+                               error = 0;              /* XXX */
+                       tcpsenderrors++;
+               }
                break;
 
        /*
         * Abort the TCP.
         */
        case PRU_ABORT:
                break;
 
        /*
         * Abort the TCP.
         */
        case PRU_ABORT:
-               tcp_drop(tp, ECONNABORTED);
+               tp = tcp_drop(tp, ECONNABORTED);
                break;
 
 /* SOME AS YET UNIMPLEMENTED HOOKS */
                break;
 
 /* SOME AS YET UNIMPLEMENTED HOOKS */
@@ -221,39 +251,29 @@ tcp_usrreq(so, req, m, addr)
                        error = EWOULDBLOCK;
                        break;
                }
                        error = EWOULDBLOCK;
                        break;
                }
+               m->m_len = 1;
                *mtod(m, caddr_t) = tp->t_iobc;
                break;
 
        case PRU_SENDOOB:
                *mtod(m, caddr_t) = tp->t_iobc;
                break;
 
        case PRU_SENDOOB:
-#ifdef TCPTRUEOOB
-               if (tp->t_flags & TF_DOOOB) {
-                       tp->t_oobseq++;
-                       tp->t_oobc = *mtod(m, caddr_t);
-                       tp->t_oobmark = tp->snd_una + so->so_snd.sb_cc;
-printf("sendoob seq now %x oobc %x\n", tp->t_oobseq, tp->t_oobc);
-                       tp->t_oobflags |= TCPOOB_NEEDACK;
-                       /* what to do ...? */
-                       if (error = tcp_output(tp))
-                               break;
-               }
-#endif
                if (sbspace(&so->so_snd) < -512) {
                if (sbspace(&so->so_snd) < -512) {
+                       m_freem(m);
                        error = ENOBUFS;
                        break;
                }
                tp->snd_up = tp->snd_una + so->so_snd.sb_cc + 1;
                sbappend(&so->so_snd, m);
                        error = ENOBUFS;
                        break;
                }
                tp->snd_up = tp->snd_una + so->so_snd.sb_cc + 1;
                sbappend(&so->so_snd, m);
-#ifdef notdef
-               if (tp->t_flags & TF_PUSH)
-                       tp->snd_end = tp->snd_una + so->so_snd.sb_cc;
-#endif
                tp->t_force = 1;
                error = tcp_output(tp);
                tp->t_force = 0;
                break;
 
        case PRU_SOCKADDR:
                tp->t_force = 1;
                error = tcp_output(tp);
                tp->t_force = 0;
                break;
 
        case PRU_SOCKADDR:
-               in_setsockaddr((struct sockaddr_in *)addr, inp);
+               in_setsockaddr(inp, nam);
+               break;
+
+       case PRU_PEERADDR:
+               in_setpeeraddr(inp, nam);
                break;
 
        /*
                break;
 
        /*
@@ -261,8 +281,8 @@ printf("sendoob seq now %x oobc %x\n", tp->t_oobseq, tp->t_oobc);
         * routine for tracing's sake.
         */
        case PRU_SLOWTIMO:
         * routine for tracing's sake.
         */
        case PRU_SLOWTIMO:
-               tcp_timers(tp, (int)addr);
-               req |= (int)addr << 8;          /* for debug's sake */
+               tp = tcp_timers(tp, (int)nam);
+               req |= (int)nam << 8;           /* for debug's sake */
                break;
 
        default:
                break;
 
        default:
@@ -281,54 +301,59 @@ int       tcp_recvspace = 1024*2;
  * internet protocol control block, tcp control block,
  * bufer space, and entering LISTEN state if to accept connections.
  */
  * internet protocol control block, tcp control block,
  * bufer space, and entering LISTEN state if to accept connections.
  */
-tcp_attach(so, sa)
+tcp_attach(so)
        struct socket *so;
        struct socket *so;
-       struct sockaddr *sa;
 {
        register struct tcpcb *tp;
        struct inpcb *inp;
        int error;
 
 {
        register struct tcpcb *tp;
        struct inpcb *inp;
        int error;
 
-       error = in_pcbattach(so, &tcb,
-           tcp_sendspace, tcp_recvspace, (struct sockaddr_in *)sa);
+       error = soreserve(so, tcp_sendspace, tcp_recvspace);
        if (error)
        if (error)
-               return (error);
-       inp = (struct inpcb *)so->so_pcb;
+               goto bad;
+       error = in_pcballoc(so, &tcb);
+       if (error)
+               goto bad;
+       inp = sotoinpcb(so);
        tp = tcp_newtcpcb(inp);
        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;
+       if (tp == 0) {
+               error = ENOBUFS;
+               goto bad2;
+       }
+       tp->t_state = TCPS_CLOSED;
        return (0);
        return (0);
+bad2:
+       in_pcbdetach(inp);
+bad:
+       return (error);
 }
 
 /*
  * Initiate (or continue) disconnect.
  * If embryonic state, just send reset (once).
 }
 
 /*
  * Initiate (or continue) disconnect.
  * If embryonic state, just send reset (once).
- * If not in ``let data drain'' option, just drop.
+ * If in ``let data drain'' option and linger null, 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).
  */
  * Otherwise (hard), mark socket disconnecting and drop
  * current input data; switch states based on user close, and
  * send segment to peer (with FIN).
  */
+struct tcpcb *
 tcp_disconnect(tp)
 tcp_disconnect(tp)
-       struct tcpcb *tp;
+       register struct tcpcb *tp;
 {
        struct socket *so = tp->t_inpcb->inp_socket;
 
        if (tp->t_state < TCPS_ESTABLISHED)
 {
        struct socket *so = tp->t_inpcb->inp_socket;
 
        if (tp->t_state < TCPS_ESTABLISHED)
-               tcp_close(tp);
-       else if (so->so_linger == 0)
-               tcp_drop(tp, 0);
+               tp = tcp_close(tp);
+       else if ((so->so_options & SO_LINGER) && so->so_linger == 0)
+               tp = tcp_drop(tp, 0);
        else {
                soisdisconnecting(so);
                sbflush(&so->so_rcv);
        else {
                soisdisconnecting(so);
                sbflush(&so->so_rcv);
-               tcp_usrclosed(tp);
-               (void) tcp_output(tp);
+               tp = tcp_usrclosed(tp);
+               if (tp)
+                       (void) tcp_output(tp);
        }
        }
+       return (tp);
 }
 
 /*
 }
 
 /*
@@ -341,16 +366,18 @@ tcp_disconnect(tp)
  * for peer to send FIN or not respond to keep-alives, etc.
  * We can let the user exit from the close as soon as the FIN is acked.
  */
  * for peer to send FIN or not respond to keep-alives, etc.
  * We can let the user exit from the close as soon as the FIN is acked.
  */
+struct tcpcb *
 tcp_usrclosed(tp)
 tcp_usrclosed(tp)
-       struct tcpcb *tp;
+       register struct tcpcb *tp;
 {
 
        switch (tp->t_state) {
 
 {
 
        switch (tp->t_state) {
 
+       case TCPS_CLOSED:
        case TCPS_LISTEN:
        case TCPS_SYN_SENT:
                tp->t_state = TCPS_CLOSED;
        case TCPS_LISTEN:
        case TCPS_SYN_SENT:
                tp->t_state = TCPS_CLOSED;
-               tcp_close(tp);
+               tp = tcp_close(tp);
                break;
 
        case TCPS_SYN_RECEIVED:
                break;
 
        case TCPS_SYN_RECEIVED:
@@ -362,6 +389,7 @@ tcp_usrclosed(tp)
                tp->t_state = TCPS_LAST_ACK;
                break;
        }
                tp->t_state = TCPS_LAST_ACK;
                break;
        }
-       if (tp->t_state >= TCPS_FIN_WAIT_2)
+       if (tp && tp->t_state >= TCPS_FIN_WAIT_2)
                soisdisconnected(tp->t_inpcb->inp_socket);
                soisdisconnected(tp->t_inpcb->inp_socket);
+       return (tp);
 }
 }