1 /* in_pcb.c 4.29 82/07/24 */ 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/route.h" 14 #include "../net/in_pcb.h" 15 #include "../h/protosw.h" 16 17 /* 18 * Routines to manage internet protocol control blocks. 19 * 20 * At PRU_ATTACH time a protocol control block is allocated in 21 * in_pcballoc() and inserted on a doubly-linked list of such blocks 22 * for the protocol. A port address is either requested (and verified 23 * to not be in use) or assigned at this time. We also allocate 24 * space in the socket sockbuf structures here, although this is 25 * not a clearly correct place to put this function. 26 * 27 * A connectionless protocol will have its protocol control block 28 * removed at PRU_DETACH time, when the socket will be freed (freeing 29 * the space reserved) and the block will be removed from the list of 30 * blocks for its protocol. 31 * 32 * A connection-based protocol may be connected to a remote peer at 33 * PRU_CONNECT time through the routine in_pcbconnect(). In the normal 34 * case a PRU_DISCONNECT occurs causing a in_pcbdisconnect(). 35 * It is also possible that higher-level routines will opt out of the 36 * relationship with the connection before the connection shut down 37 * is complete. This often occurs in protocols like TCP where we must 38 * hold on to the protocol control block for a unreasonably long time 39 * after the connection is used up to avoid races in later connection 40 * establishment. To handle this we allow higher-level routines to 41 * disassociate themselves from the socket, marking it SS_NOFDREF while 42 * the disconnect is in progress. We notice that this has happened 43 * when the disconnect is complete, and perform the PRU_DETACH operation, 44 * freeing the socket. 45 * 46 * TODO: 47 * use hashing 48 */ 49 struct in_addr zeroin_addr; 50 51 in_pcbreserve(so, sndcc, rcvcc) 52 struct socket *so; 53 int sndcc, rcvcc; 54 { 55 56 if (sbreserve(&so->so_snd, sndcc) == 0) 57 goto bad; 58 if (sbreserve(&so->so_rcv, rcvcc) == 0) 59 goto bad2; 60 return (0); 61 bad2: 62 sbrelease(&so->so_snd); 63 bad: 64 return (ENOBUFS); 65 } 66 67 in_pcballoc(so, head) 68 struct socket *so; 69 struct inpcb *head; 70 { 71 struct mbuf *m; 72 register struct inpcb *inp; 73 74 m = m_getclr(M_DONTWAIT); 75 if (m == 0) 76 return (ENOBUFS); 77 inp = mtod(m, struct inpcb *); 78 inp->inp_head = head; 79 inp->inp_socket = so; 80 insque(inp, head); 81 so->so_pcb = (caddr_t)inp; 82 return (0); 83 } 84 85 in_pcbbind(inp, sin) 86 register struct inpcb *inp; 87 struct sockaddr_in *sin; 88 { 89 register struct socket *so = inp->inp_socket; 90 register struct inpcb *head = inp->inp_head; 91 u_short lport = 0; 92 93 if (ifnet == 0) 94 return (EADDRNOTAVAIL); 95 if (sin) { 96 if (sin->sin_family != AF_INET) 97 return (EAFNOSUPPORT); 98 if (sin->sin_addr.s_addr) { 99 int tport = sin->sin_port; 100 101 sin->sin_port = 0; /* yech... */ 102 if (if_ifwithaddr((struct sockaddr *)sin) == 0) 103 return (EADDRNOTAVAIL); 104 sin->sin_port = tport; 105 } 106 lport = sin->sin_port; 107 if (lport) { 108 u_short aport = lport; 109 int wild = 0; 110 #if vax 111 aport = htons(aport); 112 #endif 113 /* GROSS */ 114 if (aport < IPPORT_RESERVED && u.u_uid != 0) 115 return (EACCES); 116 if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || 117 (so->so_options & SO_ACCEPTCONN) == 0) 118 wild = INPLOOKUP_WILDCARD; 119 if (in_pcblookup(head, 120 zeroin_addr, 0, sin->sin_addr, lport, wild)) 121 return (EADDRINUSE); 122 } 123 } 124 if (sin) 125 inp->inp_laddr = sin->sin_addr; 126 if (lport == 0) 127 do { 128 if (head->inp_lport++ < IPPORT_RESERVED) 129 head->inp_lport = IPPORT_RESERVED; 130 lport = htons(head->inp_lport); 131 } while (in_pcblookup(head, 132 zeroin_addr, 0, inp->inp_laddr, lport, 0)); 133 inp->inp_lport = lport; 134 return (0); 135 } 136 137 /* BEGIN DEPRECATED */ 138 /* 139 * Allocate a protocol control block, space 140 * for send and receive data, and local host information. 141 * Return error. If no error make socket point at pcb. 142 */ 143 in_pcbattach(so, head, sndcc, rcvcc, sin) 144 struct socket *so; 145 struct inpcb *head; 146 int sndcc, rcvcc; 147 struct sockaddr_in *sin; 148 { 149 struct mbuf *m; 150 register struct inpcb *inp; 151 u_short lport = 0; 152 153 if (ifnet == 0) 154 return (EADDRNOTAVAIL); 155 if (sin) { 156 if (sin->sin_family != AF_INET) 157 return (EAFNOSUPPORT); 158 if (sin->sin_addr.s_addr) { 159 int tport = sin->sin_port; 160 161 sin->sin_port = 0; /* yech... */ 162 if (if_ifwithaddr((struct sockaddr *)sin) == 0) 163 return (EADDRNOTAVAIL); 164 sin->sin_port = tport; 165 } 166 lport = sin->sin_port; 167 if (lport) { 168 u_short aport = lport; 169 int wild = 0; 170 #if vax 171 aport = htons(aport); 172 #endif 173 /* GROSS */ 174 if (aport < IPPORT_RESERVED && u.u_uid != 0) 175 return (EACCES); 176 if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || 177 (so->so_options & SO_ACCEPTCONN) == 0) 178 wild = INPLOOKUP_WILDCARD; 179 if (in_pcblookup(head, 180 zeroin_addr, 0, sin->sin_addr, lport, wild)) 181 return (EADDRINUSE); 182 } 183 } 184 m = m_getclr(M_DONTWAIT); 185 if (m == 0) 186 return (ENOBUFS); 187 if (sbreserve(&so->so_snd, sndcc) == 0) 188 goto bad; 189 if (sbreserve(&so->so_rcv, rcvcc) == 0) 190 goto bad2; 191 inp = mtod(m, struct inpcb *); 192 inp->inp_head = head; 193 if (sin) 194 inp->inp_laddr = sin->sin_addr; 195 if (lport == 0) 196 do { 197 if (head->inp_lport++ < IPPORT_RESERVED) 198 head->inp_lport = IPPORT_RESERVED; 199 lport = htons(head->inp_lport); 200 } while (in_pcblookup(head, 201 zeroin_addr, 0, inp->inp_laddr, lport, 0)); 202 inp->inp_lport = lport; 203 inp->inp_socket = so; 204 insque(inp, head); 205 so->so_pcb = (caddr_t)inp; 206 return (0); 207 bad2: 208 sbrelease(&so->so_snd); 209 bad: 210 (void) m_free(m); 211 return (ENOBUFS); 212 } 213 /* END DEPRECATED */ 214 215 /* 216 * Connect from a socket to a specified address. 217 * Both address and port must be specified in argument sin. 218 * If don't have a local address for this socket yet, 219 * then pick one. 220 */ 221 in_pcbconnect(inp, sin) 222 struct inpcb *inp; 223 struct sockaddr_in *sin; 224 { 225 struct ifnet *ifp; 226 struct sockaddr_in *ifaddr; 227 228 if (sin->sin_family != AF_INET) 229 return (EAFNOSUPPORT); 230 if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0) 231 return (EADDRNOTAVAIL); 232 if (inp->inp_laddr.s_addr == 0) { 233 ifp = if_ifonnetof(in_netof(sin->sin_addr)); 234 if (ifp == 0) { 235 /* 236 * We should select the interface based on 237 * the route to be used, but for udp this would 238 * result in two calls to rtalloc for each packet 239 * sent; hardly worthwhile... 240 */ 241 ifp = if_ifwithaf(AF_INET); 242 if (ifp == 0) 243 return (EADDRNOTAVAIL); 244 } 245 ifaddr = (struct sockaddr_in *)&ifp->if_addr; 246 } 247 if (in_pcblookup(inp->inp_head, 248 sin->sin_addr, 249 sin->sin_port, 250 inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 251 inp->inp_lport, 252 0)) 253 return (EADDRINUSE); 254 if (inp->inp_laddr.s_addr == 0) 255 inp->inp_laddr = ifaddr->sin_addr; 256 inp->inp_faddr = sin->sin_addr; 257 inp->inp_fport = sin->sin_port; 258 return (0); 259 } 260 261 in_pcbdisconnect(inp) 262 struct inpcb *inp; 263 { 264 265 inp->inp_faddr.s_addr = 0; 266 inp->inp_fport = 0; 267 if (inp->inp_socket->so_state & SS_NOFDREF) 268 in_pcbdetach(inp); 269 } 270 271 in_pcbdetach(inp) 272 struct inpcb *inp; 273 { 274 struct socket *so = inp->inp_socket; 275 276 so->so_pcb = 0; 277 sofree(so); 278 if (inp->inp_route.ro_rt) 279 rtfree(inp->inp_route.ro_rt); 280 remque(inp); 281 (void) m_free(dtom(inp)); 282 } 283 284 in_setsockaddr(sin, inp) 285 register struct sockaddr_in *sin; 286 register struct inpcb *inp; 287 { 288 if (sin == 0 || inp == 0) 289 panic("setsockaddr_in"); 290 bzero((caddr_t)sin, sizeof (*sin)); 291 sin->sin_family = AF_INET; 292 sin->sin_port = inp->inp_lport; 293 sin->sin_addr = inp->inp_laddr; 294 } 295 296 /* 297 * Pass an error to all internet connections 298 * associated with address sin. Call the 299 * protocol specific routine to clean up the 300 * mess afterwards. 301 */ 302 in_pcbnotify(head, dst, errno, abort) 303 struct inpcb *head; 304 register struct in_addr *dst; 305 int errno, (*abort)(); 306 { 307 register struct inpcb *inp, *oinp; 308 int s = splimp(); 309 310 for (inp = head->inp_next; inp != head;) { 311 if (inp->inp_faddr.s_addr != dst->s_addr) { 312 next: 313 inp = inp->inp_next; 314 continue; 315 } 316 if (inp->inp_socket == 0) 317 goto next; 318 inp->inp_socket->so_error = errno; 319 oinp = inp; 320 inp = inp->inp_next; 321 (*abort)(oinp); 322 } 323 splx(s); 324 } 325 326 /* 327 * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY 328 */ 329 struct inpcb * 330 in_pcblookup(head, faddr, fport, laddr, lport, flags) 331 struct inpcb *head; 332 struct in_addr faddr, laddr; 333 u_short fport, lport; 334 int flags; 335 { 336 register struct inpcb *inp, *match = 0; 337 int matchwild = 3, wildcard; 338 339 for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 340 if (inp->inp_lport != lport) 341 continue; 342 wildcard = 0; 343 if (inp->inp_laddr.s_addr != 0) { 344 if (laddr.s_addr == 0) 345 wildcard++; 346 else if (inp->inp_laddr.s_addr != laddr.s_addr) 347 continue; 348 } else { 349 if (laddr.s_addr != 0) 350 wildcard++; 351 } 352 if (inp->inp_faddr.s_addr != 0) { 353 if (faddr.s_addr == 0) 354 wildcard++; 355 else if (inp->inp_faddr.s_addr != faddr.s_addr || 356 inp->inp_fport != fport) 357 continue; 358 } else { 359 if (faddr.s_addr != 0) 360 wildcard++; 361 } 362 if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) 363 continue; 364 if (wildcard < matchwild) { 365 match = inp; 366 matchwild = wildcard; 367 if (matchwild == 0) 368 break; 369 } 370 } 371 return (match); 372 } 373