new signals
[unix-history] / usr / src / sys / netinet / tcp_usrreq.c
index 7dd4605..ebed585 100644 (file)
@@ -1,26 +1,47 @@
-/*     tcp_usrreq.c    1.66    82/10/17        */
-
-#include "../h/param.h"
-#include "../h/systm.h"
-#include "../h/mbuf.h"
-#include "../h/socket.h"
-#include "../h/socketvar.h"
-#include "../h/protosw.h"
-#include "../netinet/in.h"
-#include "../net/route.h"
-#include "../netinet/in_pcb.h"
-#include "../netinet/in_systm.h"
+/*
+ * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)tcp_usrreq.c        7.12 (Berkeley) %G%
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "malloc.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "socketvar.h"
+#include "protosw.h"
+#include "errno.h"
+#include "stat.h"
+
 #include "../net/if.h"
 #include "../net/if.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"
-#include <errno.h>
+#include "../net/route.h"
+
+#include "in.h"
+#include "in_pcb.h"
+#include "in_systm.h"
+#include "ip.h"
+#include "ip_var.h"
+#include "tcp.h"
+#include "tcp_fsm.h"
+#include "tcp_seq.h"
+#include "tcp_timer.h"
+#include "tcp_var.h"
+#include "tcpip.h"
+#include "tcp_debug.h"
 
 /*
  * TCP protocol interface to socket abstraction.
 
 /*
  * TCP protocol interface to socket abstraction.
@@ -34,18 +55,30 @@ struct      tcpcb *tcp_newtcpcb();
  * (called from the software clock routine), then timertype tells which timer.
  */
 /*ARGSUSED*/
  * (called from the software clock routine), then timertype tells which timer.
  */
 /*ARGSUSED*/
-tcp_usrreq(so, req, m, nam, opt)
+tcp_usrreq(so, req, m, nam, rights)
        struct socket *so;
        int req;
        struct socket *so;
        int req;
-       struct mbuf *m, *nam;
-       struct socketopt *opt;
+       struct mbuf *m, *nam, *rights;
 {
 {
-       register struct inpcb *inp = sotoinpcb(so);
+       register struct inpcb *inp;
        register struct tcpcb *tp;
        register struct tcpcb *tp;
-       int s = splnet();
+       int s;
        int error = 0;
        int ostate;
 
        int error = 0;
        int ostate;
 
+#if BSD>=43
+       if (req == PRU_CONTROL)
+               return (in_control(so, (int)m, (caddr_t)nam,
+                       (struct ifnet *)rights));
+#else
+       if (req == PRU_CONTROL)
+               return(EOPNOTSUPP);
+#endif
+       if (rights && rights->m_len)
+               return (EINVAL);
+
+       s = splnet();
+       inp = sotoinpcb(so);
        /*
         * When a TCP is attached to a socket, then there will be
         * a (struct inpcb) pointed at by the socket, and this
        /*
         * When a TCP is attached to a socket, then there will be
         * a (struct inpcb) pointed at by the socket, and this
@@ -75,10 +108,10 @@ tcp_usrreq(so, req, m, nam, opt)
                        error = EISCONN;
                        break;
                }
                        error = EISCONN;
                        break;
                }
-               error = tcp_attach(so, nam);
+               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;
@@ -92,11 +125,9 @@ tcp_usrreq(so, req, m, nam, opt)
         */
        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;
 
        /*
                break;
 
        /*
@@ -141,13 +172,21 @@ tcp_usrreq(so, req, m, nam, opt)
                        break;
                }
                soisconnecting(so);
                        break;
                }
                soisconnecting(so);
+               tcpstat.tcps_connattempt++;
                tp->t_state = TCPS_SYN_SENT;
                tp->t_state = TCPS_SYN_SENT;
-               tp->t_timer[TCPT_KEEP] = TCPTV_KEEP;
+               tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
                tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2;
                tcp_sendseqinit(tp);
                error = tcp_output(tp);
                break;
 
                tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2;
                tcp_sendseqinit(tp);
                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;
@@ -160,7 +199,7 @@ tcp_usrreq(so, req, m, nam, opt)
         * 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;
 
        /*
@@ -173,6 +212,7 @@ tcp_usrreq(so, req, m, nam, opt)
 
                nam->m_len = sizeof (struct sockaddr_in);
                sin->sin_family = AF_INET;
 
                nam->m_len = sizeof (struct sockaddr_in);
                sin->sin_family = AF_INET;
+               sin->sin_len = sizeof(*sin);
                sin->sin_port = inp->inp_fport;
                sin->sin_addr = inp->inp_faddr;
                break;
                sin->sin_port = inp->inp_fport;
                sin->sin_addr = inp->inp_faddr;
                break;
@@ -183,8 +223,9 @@ tcp_usrreq(so, req, m, nam, opt)
         */
        case PRU_SHUTDOWN:
                socantsendmore(so);
         */
        case PRU_SHUTDOWN:
                socantsendmore(so);
-               tcp_usrclosed(tp);
-               error = tcp_output(tp);
+               tp = tcp_usrclosed(tp);
+               if (tp)
+                       error = tcp_output(tp);
                break;
 
        /*
                break;
 
        /*
@@ -200,10 +241,6 @@ tcp_usrreq(so, req, m, nam, opt)
         */
        case PRU_SEND:
                sbappend(&so->so_snd, m);
         */
        case PRU_SEND:
                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
                error = tcp_output(tp);
                break;
 
                error = tcp_output(tp);
                break;
 
@@ -211,22 +248,21 @@ tcp_usrreq(so, req, m, nam, opt)
         * Abort the TCP.
         */
        case PRU_ABORT:
         * Abort the TCP.
         */
        case PRU_ABORT:
-               tcp_drop(tp, ECONNABORTED);
-               break;
-
-/* SOME AS YET UNIMPLEMENTED HOOKS */
-       case PRU_CONTROL:
-               error = EOPNOTSUPP;
+               tp = tcp_drop(tp, ECONNABORTED);
                break;
 
        case PRU_SENSE:
                break;
 
        case PRU_SENSE:
-               error = EOPNOTSUPP;
-               break;
-/* END UNIMPLEMENTED HOOKS */
+               ((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat;
+               (void) splx(s);
+               return (0);
 
        case PRU_RCVOOB:
 
        case PRU_RCVOOB:
-               if (so->so_oobmark == 0 &&
-                   (so->so_state & SS_RCVATMARK) == 0) {
+               if ((so->so_oobmark == 0 &&
+                   (so->so_state & SS_RCVATMARK) == 0) ||
+#ifdef SO_OOBINLINE
+                   so->so_options & SO_OOBINLINE ||
+#endif
+                   tp->t_oobflags & TCPOOB_HADDATA) {
                        error = EINVAL;
                        break;
                }
                        error = EINVAL;
                        break;
                }
@@ -236,15 +272,26 @@ tcp_usrreq(so, req, m, nam, opt)
                }
                m->m_len = 1;
                *mtod(m, caddr_t) = tp->t_iobc;
                }
                m->m_len = 1;
                *mtod(m, caddr_t) = tp->t_iobc;
+               if (((int)nam & MSG_PEEK) == 0)
+                       tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA);
                break;
 
        case PRU_SENDOOB:
                if (sbspace(&so->so_snd) < -512) {
                break;
 
        case PRU_SENDOOB:
                if (sbspace(&so->so_snd) < -512) {
+                       m_freem(m);
                        error = ENOBUFS;
                        break;
                }
                        error = ENOBUFS;
                        break;
                }
-               tp->snd_up = tp->snd_una + so->so_snd.sb_cc + 1;
+               /*
+                * According to RFC961 (Assigned Protocols),
+                * the urgent pointer points to the last octet
+                * of urgent data.  We continue, however,
+                * to consider it to indicate the first octet
+                * of data past the urgent section.
+                * Otherwise, snd_up should be one lower.
+                */
                sbappend(&so->so_snd, m);
                sbappend(&so->so_snd, m);
+               tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
                tp->t_force = 1;
                error = tcp_output(tp);
                tp->t_force = 0;
                tp->t_force = 1;
                error = tcp_output(tp);
                tp->t_force = 0;
@@ -254,12 +301,16 @@ tcp_usrreq(so, req, m, nam, opt)
                in_setsockaddr(inp, nam);
                break;
 
                in_setsockaddr(inp, nam);
                break;
 
+       case PRU_PEERADDR:
+               in_setpeeraddr(inp, nam);
+               break;
+
        /*
         * TCP slow timer went off; going through this
         * routine for tracing's sake.
         */
        case PRU_SLOWTIMO:
        /*
         * TCP slow timer went off; going through this
         * routine for tracing's sake.
         */
        case PRU_SLOWTIMO:
-               tcp_timers(tp, (int)nam);
+               tp = tcp_timers(tp, (int)nam);
                req |= (int)nam << 8;           /* for debug's sake */
                break;
 
                req |= (int)nam << 8;           /* for debug's sake */
                break;
 
@@ -272,8 +323,69 @@ tcp_usrreq(so, req, m, nam, opt)
        return (error);
 }
 
        return (error);
 }
 
-int    tcp_sendspace = 1024*2;
-int    tcp_recvspace = 1024*2;
+#if BSD>=43
+tcp_ctloutput(op, so, level, optname, mp)
+       int op;
+       struct socket *so;
+       int level, optname;
+       struct mbuf **mp;
+{
+       int error = 0;
+       struct inpcb *inp = sotoinpcb(so);
+       register struct tcpcb *tp = intotcpcb(inp);
+       register struct mbuf *m;
+
+       if (level != IPPROTO_TCP)
+               return (ip_ctloutput(op, so, level, optname, mp));
+
+       switch (op) {
+
+       case PRCO_SETOPT:
+               m = *mp;
+               switch (optname) {
+
+               case TCP_NODELAY:
+                       if (m == NULL || m->m_len < sizeof (int))
+                               error = EINVAL;
+                       else if (*mtod(m, int *))
+                               tp->t_flags |= TF_NODELAY;
+                       else
+                               tp->t_flags &= ~TF_NODELAY;
+                       break;
+
+               case TCP_MAXSEG:        /* not yet */
+               default:
+                       error = EINVAL;
+                       break;
+               }
+               if (m)
+                       (void) m_free(m);
+               break;
+
+       case PRCO_GETOPT:
+               *mp = m = m_get(M_WAIT, MT_SOOPTS);
+               m->m_len = sizeof(int);
+
+               switch (optname) {
+               case TCP_NODELAY:
+                       *mtod(m, int *) = tp->t_flags & TF_NODELAY;
+                       break;
+               case TCP_MAXSEG:
+                       *mtod(m, int *) = tp->t_maxseg;
+                       break;
+               default:
+                       error = EINVAL;
+                       break;
+               }
+               break;
+       }
+       return (error);
+}
+#endif
+
+u_long tcp_sendspace = 1024*4;
+u_long tcp_recvspace = 1024*4;
+
 /*
  * Attach TCP protocol to socket, allocating
  * internet protocol control block, tcp control block,
 /*
  * Attach TCP protocol to socket, allocating
  * internet protocol control block, tcp control block,
@@ -286,49 +398,54 @@ tcp_attach(so)
        struct inpcb *inp;
        int error;
 
        struct inpcb *inp;
        int error;
 
-       error = in_pcbreserve(so, tcp_sendspace, tcp_recvspace);
-       if (error)
-               goto bad;
+       if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
+               error = soreserve(so, tcp_sendspace, tcp_recvspace);
+               if (error)
+                       return (error);
+       }
        error = in_pcballoc(so, &tcb);
        if (error)
        error = in_pcballoc(so, &tcb);
        if (error)
-               goto bad;
+               return (error);
        inp = sotoinpcb(so);
        tp = tcp_newtcpcb(inp);
        if (tp == 0) {
        inp = sotoinpcb(so);
        tp = tcp_newtcpcb(inp);
        if (tp == 0) {
-               error = ENOBUFS;
-               goto bad2;
+               int nofd = so->so_state & SS_NOFDREF;   /* XXX */
+
+               so->so_state &= ~SS_NOFDREF;    /* don't free the socket yet */
+               in_pcbdetach(inp);
+               so->so_state |= nofd;
+               return (ENOBUFS);
        }
        tp->t_state = TCPS_CLOSED;
        return (0);
        }
        tp->t_state = TCPS_CLOSED;
        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 +458,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 +481,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);
 }
 }