1 /* in_pcb.c 4.12 81/12/03 */ 2 3 #include "../h/param.h" 4 #include "../h/systm.h" 5 #include "../h/dir.h" 6 #include "../h/user.h" 7 #include "../h/mbuf.h" 8 #include "../h/socket.h" 9 #include "../h/socketvar.h" 10 #include "../net/in.h" 11 #include "../net/in_systm.h" 12 #include "../net/if.h" 13 #include "../net/in_pcb.h" 14 15 /* 16 * Routines to manage internet protocol control blocks. 17 * 18 * At PRU_ATTACH time a protocol control block is allocated in 19 * in_pcballoc() and inserted on a doubly-linked list of such blocks 20 * for the protocol. A port address is either requested (and verified 21 * to not be in use) or assigned at this time. We also allocate 22 * space in the socket sockbuf structures here, although this is 23 * not a clearly correct place to put this function. 24 * 25 * A connectionless protocol will have its protocol control block 26 * removed at PRU_DETACH time, when the socket will be freed (freeing 27 * the space reserved) and the block will be removed from the list of 28 * blocks for its protocol. 29 * 30 * A connection-based protocol may be connected to a remote peer at 31 * PRU_CONNECT time through the routine in_pcbconnect(). In the normal 32 * case a PRU_DISCONNECT occurs causing a in_pcbdisconnect(). 33 * It is also possible that higher-level routines will opt out of the 34 * relationship with the connection before the connection shut down 35 * is complete. This often occurs in protocols like TCP where we must 36 * hold on to the protocol control block for a unreasonably long time 37 * after the connection is used up to avoid races in later connection 38 * establishment. To handle this we allow higher-level routines to 39 * disassociate themselves from the socket, marking it SS_USERGONE while 40 * the disconnect is in progress. We notice that this has happened 41 * when the disconnect is complete, and perform the PRU_DETACH operation, 42 * freeing the socket. 43 * 44 * TODO: 45 * use hashing 46 */ 47 48 /* 49 * Allocate a protocol control block, space 50 * for send and receive data, and local host information. 51 * Return error. If no error make socket point at pcb. 52 */ 53 in_pcbattach(so, head, sndcc, rcvcc, sin) 54 struct socket *so; 55 struct inpcb *head; 56 int sndcc, rcvcc; 57 struct sockaddr_in *sin; 58 { 59 struct mbuf *m; 60 register struct inpcb *inp, *xp; 61 struct ifnet *ifp; 62 u_long lport; 63 64 COUNT(IN_PCBATTACH); 65 if (sin) { 66 if (sin->sin_family != AF_INET) 67 return (EAFNOSUPPORT); 68 ifp = if_ifwithaddr(sin->sin_addr); 69 if (ifp == 0) 70 return (EADDRNOTAVAIL); 71 lport = sin->sin_port; 72 if (lport && 73 in_pcblookup(head, 0, 0, sin->sin_addr.s_addr, lport)) 74 return (EADDRINUSE); 75 } else { 76 ifp = if_ifwithaddr(ifnet->if_addr); 77 lport = 0; 78 } 79 m = m_getclr(0); 80 if (m == 0) 81 return (ENOBUFS); 82 if (sbreserve(&so->so_snd, sndcc) == 0) 83 goto bad; 84 if (sbreserve(&so->so_rcv, rcvcc) == 0) 85 goto bad2; 86 inp = mtod(m, struct inpcb *); 87 inp->inp_head = head; 88 inp->inp_laddr = ifp->if_addr; 89 if (lport == 0) 90 do { 91 if (head->inp_lport++ < 1024) 92 head->inp_lport = 1024; 93 lport = htons(head->inp_lport); 94 } while (in_pcblookup(head, 0, 0, inp->inp_laddr, lport)); 95 inp->inp_lport = lport; 96 inp->inp_socket = so; 97 insque(inp, head); 98 so->so_pcb = (caddr_t)inp; 99 sin = (struct sockaddr_in *)&so->so_addr; 100 sin->sin_family = AF_INET; 101 sin->sin_addr = inp->inp_laddr; 102 sin->sin_port = inp->inp_lport; 103 return (0); 104 bad2: 105 sbrelease(&so->so_snd); 106 bad: 107 (void) m_free(m); 108 return (ENOBUFS); 109 } 110 111 in_pcbconnect(inp, sin) 112 struct inpcb *inp; 113 struct sockaddr_in *sin; 114 { 115 struct inpcb *xp; 116 117 COUNT(IN_PCBCONNECT); 118 if (sin->sin_family != AF_INET) 119 return (EAFNOSUPPORT); 120 if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0) 121 return (EADDRNOTAVAIL); 122 xp = in_pcblookup(inp->inp_head, sin->sin_addr, sin->sin_port, inp->inp_laddr, inp->inp_lport); 123 if (xp->inp_faddr) 124 return (EADDRINUSE); 125 inp->inp_faddr = sin->sin_addr; 126 inp->inp_fport = sin->sin_port; 127 return (0); 128 } 129 130 in_pcbdisconnect(inp) 131 struct inpcb *inp; 132 { 133 134 COUNT(IN_PCBDISCONNECT); 135 inp->inp_faddr.s_addr = 0; 136 if (inp->inp_socket->so_state & SS_USERGONE) 137 in_pcbdetach(inp); 138 } 139 140 in_pcbdetach(inp) 141 struct inpcb *inp; 142 { 143 struct socket *so = inp->inp_socket; 144 145 so->so_pcb = 0; 146 sofree(so); 147 remque(inp); 148 (void) m_free(dtom(inp)); 149 } 150 151 /* 152 * Look for a control block to accept a segment. 153 * First choice is an exact address match. 154 * Second choice is a match of local address, with 155 * unspecified foreign address. 156 */ 157 struct inpcb * 158 in_pcblookup(head, faddr, fport, laddr, lport) 159 struct inpcb *head; 160 struct in_addr faddr, laddr; 161 u_short fport, lport; 162 { 163 register struct inpcb *inp; 164 struct inpcb *match = 0; 165 166 for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 167 if (inp->inp_laddr.s_addr != laddr.s_addr || 168 inp->inp_lport != lport) 169 continue; 170 if (inp->inp_faddr.s_addr == 0) { 171 match = inp; 172 continue; 173 } 174 if (inp->inp_faddr.s_addr == faddr.s_addr && 175 inp->inp_fport == fport) 176 return (inp); 177 } 178 return (match); 179 } 180