handles new uba stuff and fix page freeing problem
[unix-history] / usr / src / sys / netinet / in_pcb.c
index f0ecb11..977454b 100644 (file)
@@ -1,4 +1,4 @@
-/* in_pcb.c 4.6 81/11/20 */
+/* in_pcb.c 4.15 81/12/21 */
 
 #include "../h/param.h"
 #include "../h/systm.h"
 
 #include "../h/param.h"
 #include "../h/systm.h"
@@ -7,17 +7,51 @@
 #include "../h/mbuf.h"
 #include "../h/socket.h"
 #include "../h/socketvar.h"
 #include "../h/mbuf.h"
 #include "../h/socket.h"
 #include "../h/socketvar.h"
-#include "../net/inet.h"
-#include "../net/inet_systm.h"
+#include "../net/in.h"
+#include "../net/in_systm.h"
 #include "../net/if.h"
 #include "../net/if.h"
-#include "../net/inet_pcb.h"
+#include "../net/in_pcb.h"
+
+/*
+ * Routines to manage internet protocol control blocks.
+ *
+ * At PRU_ATTACH time a protocol control block is allocated in
+ * in_pcballoc() and inserted on a doubly-linked list of such blocks
+ * for the protocol.  A port address is either requested (and verified
+ * to not be in use) or assigned at this time.  We also allocate
+ * space in the socket sockbuf structures here, although this is
+ * not a clearly correct place to put this function.
+ *
+ * A connectionless protocol will have its protocol control block
+ * removed at PRU_DETACH time, when the socket will be freed (freeing
+ * the space reserved) and the block will be removed from the list of
+ * blocks for its protocol.
+ *
+ * A connection-based protocol may be connected to a remote peer at
+ * PRU_CONNECT time through the routine in_pcbconnect().  In the normal
+ * case a PRU_DISCONNECT occurs causing a in_pcbdisconnect().
+ * It is also possible that higher-level routines will opt out of the
+ * relationship with the connection before the connection shut down
+ * is complete.  This often occurs in protocols like TCP where we must
+ * hold on to the protocol control block for a unreasonably long time
+ * after the connection is used up to avoid races in later connection
+ * establishment.  To handle this we allow higher-level routines to
+ * disassociate themselves from the socket, marking it SS_USERGONE while
+ * the disconnect is in progress.  We notice that this has happened
+ * when the disconnect is complete, and perform the PRU_DETACH operation,
+ * freeing the socket.
+ *
+ * TODO:
+ *     use hashing
+ */
+struct in_addr zeroin_addr;
 
 /*
  * Allocate a protocol control block, space
  * for send and receive data, and local host information.
  * Return error.  If no error make socket point at pcb.
  */
 
 /*
  * Allocate a protocol control block, space
  * for send and receive data, and local host information.
  * Return error.  If no error make socket point at pcb.
  */
-in_pcballoc(so, head, sndcc, rcvcc, sin)
+in_pcbattach(so, head, sndcc, rcvcc, sin)
        struct socket *so;
        struct inpcb *head;
        int sndcc, rcvcc;
        struct socket *so;
        struct inpcb *head;
        int sndcc, rcvcc;
@@ -26,49 +60,44 @@ in_pcballoc(so, head, sndcc, rcvcc, sin)
        struct mbuf *m;
        register struct inpcb *inp;
        struct ifnet *ifp;
        struct mbuf *m;
        register struct inpcb *inp;
        struct ifnet *ifp;
-       u_long lport;
+       u_short lport;
 
 
+COUNT(IN_PCBATTACH);
        if (sin) {
                if (sin->sin_family != AF_INET)
                        return (EAFNOSUPPORT);
        if (sin) {
                if (sin->sin_family != AF_INET)
                        return (EAFNOSUPPORT);
+               if (ifnet && sin->sin_addr.s_addr == 0)
+                       sin->sin_addr = ifnet->if_addr;
                ifp = if_ifwithaddr(sin->sin_addr);
                ifp = if_ifwithaddr(sin->sin_addr);
-               if (ifp == 0)
-                       return (EADDRNOTAVAIL);
                lport = sin->sin_port;
                lport = sin->sin_port;
-               if (lport) {
-                       inp = head->inp_next;
-                       for (; inp != head; inp = inp->inp_next) 
-                               if (inp->inp_laddr.s_addr ==
-                                   sin->sin_addr.s_addr &&
-                                   inp->inp_lport == lport &&
-                                   inp->inp_faddr.s_addr == 0)
-                                       return (EADDRINUSE);
-               }
+               if (lport &&
+                   in_pcblookup(head, zeroin_addr, 0, sin->sin_addr, lport))
+                       return (EADDRINUSE);
        } else {
        } else {
-               ifp = if_ifwithaddr(ifnet->if_addr);
+               ifp = ifnet;
                lport = 0;
        }
                lport = 0;
        }
-       m = m_getclr(M_WAIT);
+       if (ifp == 0)
+               return (EADDRNOTAVAIL);
+       m = m_getclr(0);
        if (m == 0)
        if (m == 0)
-               return (0);
+               return (ENOBUFS);
        if (sbreserve(&so->so_snd, sndcc) == 0)
                goto bad;
        if (sbreserve(&so->so_rcv, rcvcc) == 0)
                goto bad2;
        inp = mtod(m, struct inpcb *);
        if (sbreserve(&so->so_snd, sndcc) == 0)
                goto bad;
        if (sbreserve(&so->so_rcv, rcvcc) == 0)
                goto bad2;
        inp = mtod(m, struct inpcb *);
+       inp->inp_head = head;
        inp->inp_laddr = ifp->if_addr;
        inp->inp_laddr = ifp->if_addr;
-       if (lport)
-               goto gotport;
-again:
-       if (head->inp_lport++ < 1024)
-               head->inp_lport = 1024;
-       for (inp = head->inp_next; inp != head; inp = inp->inp_next)
-               if (inp->inp_lport == head->inp_lport)
-                       goto again;
-       lport = head->inp_lport;
-gotport:
+       if (lport == 0)
+               do {
+                       if (head->inp_lport++ < 1024)
+                               head->inp_lport = 1024;
+                       lport = htons(head->inp_lport);
+               } while (in_pcblookup(head, zeroin_addr, 0, inp->inp_laddr, lport));
        inp->inp_lport = lport;
        inp->inp_lport = lport;
-       insque(head, inp);
+       inp->inp_socket = so;
+       insque(inp, head);
        so->so_pcb = (caddr_t)inp;
        sin = (struct sockaddr_in *)&so->so_addr;
        sin->sin_family = AF_INET;
        so->so_pcb = (caddr_t)inp;
        sin = (struct sockaddr_in *)&so->so_addr;
        sin->sin_family = AF_INET;
@@ -82,33 +111,63 @@ bad:
        return (ENOBUFS);
 }
 
        return (ENOBUFS);
 }
 
-in_pcbsetpeer(inp, sin)
+in_pcbconnect(inp, sin)
        struct inpcb *inp;
        struct sockaddr_in *sin;
 {
        struct inpcb *inp;
        struct sockaddr_in *sin;
 {
+       struct inpcb *xp;
 
 
+COUNT(IN_PCBCONNECT);
        if (sin->sin_family != AF_INET)
                return (EAFNOSUPPORT);
        if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
                return (EADDRNOTAVAIL);
        if (sin->sin_family != AF_INET)
                return (EAFNOSUPPORT);
        if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
                return (EADDRNOTAVAIL);
-       /* should check not already in use... */
+       xp = in_pcblookup(inp->inp_head, sin->sin_addr, sin->sin_port, inp->inp_laddr, inp->inp_lport);
+       if (xp->inp_faddr.s_addr)
+               return (EADDRINUSE);
        inp->inp_faddr = sin->sin_addr;
        inp->inp_fport = sin->sin_port;
        return (0);
 }
 
        inp->inp_faddr = sin->sin_addr;
        inp->inp_fport = sin->sin_port;
        return (0);
 }
 
-in_pcbfree(inp)
+in_pcbconnaddr(inp, sp)
+       struct inpcb *inp;
+       struct sockaddr *sp;
+{
+       register struct sockaddr_in *sin = (struct sockaddr_in *)sp;
+
+       sin->sin_family = AF_INET;
+       sin->sin_port = inp->inp_fport;
+       sin->sin_addr = inp->inp_faddr;
+}
+
+in_pcbdisconnect(inp)
+       struct inpcb *inp;
+{
+
+COUNT(IN_PCBDISCONNECT);
+       inp->inp_faddr.s_addr = 0;
+       if (inp->inp_socket->so_state & SS_USERGONE)
+               in_pcbdetach(inp);
+}
+
+in_pcbdetach(inp)
        struct inpcb *inp;
 {
        struct socket *so = inp->inp_socket;
 
        struct inpcb *inp;
 {
        struct socket *so = inp->inp_socket;
 
-       if (so->so_state & SS_USERGONE)
-               sofree(so);
-       else
-               so->so_pcb = 0;
+       so->so_pcb = 0;
+       sofree(so);
+       remque(inp);
        (void) m_free(dtom(inp));
 }
 
        (void) m_free(dtom(inp));
 }
 
+/*
+ * Look for a control block to accept a segment.
+ * First choice is an exact address match.
+ * Second choice is a match of local address, with
+ * unspecified foreign address.
+ */
 struct inpcb *
 in_pcblookup(head, faddr, fport, laddr, lport)
        struct inpcb *head;
 struct inpcb *
 in_pcblookup(head, faddr, fport, laddr, lport)
        struct inpcb *head;
@@ -116,19 +175,19 @@ in_pcblookup(head, faddr, fport, laddr, lport)
        u_short fport, lport;
 {
        register struct inpcb *inp;
        u_short fport, lport;
 {
        register struct inpcb *inp;
+       struct inpcb *match = 0;
 
 
-       for (inp = head->inp_next; inp != head; inp = inp->inp_next)
+       for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
+               if (inp->inp_laddr.s_addr != laddr.s_addr ||
+                   inp->inp_lport != lport)
+                       continue;
+               if (inp->inp_faddr.s_addr == 0) {
+                       match = inp;
+                       continue;
+               }
                if (inp->inp_faddr.s_addr == faddr.s_addr &&
                if (inp->inp_faddr.s_addr == faddr.s_addr &&
-                   inp->inp_fport == fport &&
-                   inp->inp_laddr.s_addr == laddr.s_addr &&
-                   inp->inp_lport == lport)
-                       return (inp);
-       for (inp = head->inp_next; inp != head; inp = inp->inp_next)
-               if ((inp->inp_faddr.s_addr == faddr.s_addr ||
-                    inp->inp_faddr.s_addr == 0) &&
-                   (inp->inp_fport == fport || inp->inp_fport == 0) &&
-                    inp->inp_laddr.s_addr == laddr.s_addr &&
-                   (inp->inp_lport == lport || inp->inp_lport == 0))
+                   inp->inp_fport == fport)
                        return (inp);
                        return (inp);
-       return (0);
+       }
+       return (match);
 }
 }