1 /* in_pcb.c 4.18 82/03/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 struct in_addr zeroin_addr; 48 49 /* 50 * Allocate a protocol control block, space 51 * for send and receive data, and local host information. 52 * Return error. If no error make socket point at pcb. 53 */ 54 in_pcbattach(so, head, sndcc, rcvcc, sin) 55 struct socket *so; 56 struct inpcb *head; 57 int sndcc, rcvcc; 58 struct sockaddr_in *sin; 59 { 60 struct mbuf *m; 61 register struct inpcb *inp; 62 u_short lport = 0; 63 64 COUNT(IN_PCBATTACH); 65 if (ifnet == 0) 66 return (EADDRNOTAVAIL); 67 if (sin) { 68 if (sin->sin_family != AF_INET) 69 return (EAFNOSUPPORT); 70 if (sin->sin_addr.s_addr && 71 if_ifwithaddr(sin->sin_addr.s_addr) == 0) 72 return (EADDRNOTAVAIL); 73 lport = sin->sin_port; 74 if (lport) { 75 u_short aport = lport; 76 #if vax 77 aport = htons(aport); 78 #endif 79 /* GROSS */ 80 if (aport < IPPORT_RESERVED && u.u_uid != 0) 81 return (EPERM); 82 if (in_pcblookup(head, 83 zeroin_addr, 0, sin->sin_addr, lport, 0)) 84 return (EADDRINUSE); 85 } 86 } 87 m = m_getclr(M_DONTWAIT); 88 if (m == 0) 89 return (ENOBUFS); 90 if (sbreserve(&so->so_snd, sndcc) == 0) 91 goto bad; 92 if (sbreserve(&so->so_rcv, rcvcc) == 0) 93 goto bad2; 94 inp = mtod(m, struct inpcb *); 95 inp->inp_head = head; 96 if (sin) 97 inp->inp_laddr = sin->sin_addr; 98 if (lport == 0) 99 do { 100 if (head->inp_lport++ < IPPORT_RESERVED) 101 head->inp_lport = IPPORT_RESERVED; 102 lport = htons(head->inp_lport); 103 } while (in_pcblookup(head, 104 zeroin_addr, 0, inp->inp_laddr, lport, 0)); 105 inp->inp_lport = lport; 106 inp->inp_socket = so; 107 insque(inp, head); 108 so->so_pcb = (caddr_t)inp; 109 in_setsockaddr(inp); 110 return (0); 111 bad2: 112 sbrelease(&so->so_snd); 113 bad: 114 (void) m_free(m); 115 return (ENOBUFS); 116 } 117 118 in_pcbconnect(inp, sin) 119 struct inpcb *inp; 120 struct sockaddr_in *sin; 121 { 122 struct ifnet *ifp; 123 124 COUNT(IN_PCBCONNECT); 125 if (sin->sin_family != AF_INET) 126 return (EAFNOSUPPORT); 127 if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0) 128 return (EADDRNOTAVAIL); 129 if (inp->inp_laddr.s_addr == 0) { 130 ifp = if_ifonnetof(sin->sin_addr.s_addr); 131 if (ifp == 0) 132 ifp = ifnet; 133 } 134 if (in_pcblookup(inp->inp_head, 135 sin->sin_addr, sin->sin_port, inp->inp_laddr, inp->inp_lport, 0)) 136 return (EADDRINUSE); 137 if (inp->inp_laddr.s_addr == 0) 138 inp->inp_laddr = ifp->if_addr; 139 inp->inp_faddr = sin->sin_addr; 140 inp->inp_fport = sin->sin_port; 141 return (0); 142 } 143 144 in_setsockaddr(inp) 145 struct inpcb *inp; 146 { 147 register struct sockaddr_in *sin = 148 (struct sockaddr_in *)&inp->inp_socket->so_addr; 149 150 sin->sin_family = AF_INET; 151 sin->sin_addr = inp->inp_laddr; 152 sin->sin_port = inp->inp_lport; 153 } 154 155 in_pcbdisconnect(inp) 156 struct inpcb *inp; 157 { 158 159 COUNT(IN_PCBDISCONNECT); 160 inp->inp_faddr.s_addr = 0; 161 inp->inp_fport = 0; 162 if (inp->inp_socket->so_state & SS_USERGONE) 163 in_pcbdetach(inp); 164 } 165 166 in_pcbdetach(inp) 167 struct inpcb *inp; 168 { 169 struct socket *so = inp->inp_socket; 170 171 so->so_pcb = 0; 172 sofree(so); 173 remque(inp); 174 (void) m_free(dtom(inp)); 175 } 176 177 /* 178 * Look for a control block to accept a segment. 179 * First choice is an exact address match. 180 * Second choice is a match with either the foreign or the local 181 * address specified. 182 * 183 * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY 184 */ 185 struct inpcb * 186 in_pcblookup(head, faddr, fport, laddr, lport, flags) 187 struct inpcb *head; 188 struct in_addr faddr, laddr; 189 u_short fport, lport; 190 int flags; 191 { 192 register struct inpcb *inp, *match = 0; 193 int matchwild = 3, wildcard; 194 195 for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 196 if (inp->inp_lport != lport) 197 continue; 198 wildcard = 0; 199 if (inp->inp_laddr.s_addr != 0) { 200 if (inp->inp_laddr.s_addr != laddr.s_addr) 201 continue; 202 } else { 203 if (laddr.s_addr != 0) 204 wildcard++; 205 } 206 if (inp->inp_faddr.s_addr != 0) { 207 if (inp->inp_faddr.s_addr != faddr.s_addr || 208 inp->inp_fport != fport) 209 continue; 210 } else { 211 if (faddr.s_addr != 0) 212 wildcard++; 213 } 214 if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) 215 continue; 216 if (wildcard < matchwild) { 217 match = inp; 218 matchwild = wildcard; 219 if (matchwild == 0) 220 break; 221 } 222 } 223 return (match); 224 } 225