1 /* in_pcb.c 4.26 82/04/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_USERGONE 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 /* 52 * Allocate a protocol control block, space 53 * for send and receive data, and local host information. 54 * Return error. If no error make socket point at pcb. 55 */ 56 in_pcbattach(so, head, sndcc, rcvcc, sin) 57 struct socket *so; 58 struct inpcb *head; 59 int sndcc, rcvcc; 60 struct sockaddr_in *sin; 61 { 62 struct mbuf *m; 63 register struct inpcb *inp; 64 u_short lport = 0; 65 66 COUNT(IN_PCBATTACH); 67 if (ifnet == 0) 68 return (EADDRNOTAVAIL); 69 if (sin) { 70 if (sin->sin_family != AF_INET) 71 return (EAFNOSUPPORT); 72 if (sin->sin_addr.s_addr) { 73 int tport = sin->sin_port; 74 75 sin->sin_port = 0; /* yech... */ 76 if (if_ifwithaddr((struct sockaddr *)sin) == 0) 77 return (EADDRNOTAVAIL); 78 sin->sin_port = tport; 79 } 80 lport = sin->sin_port; 81 if (lport) { 82 u_short aport = lport; 83 int wild = 0; 84 #if vax 85 aport = htons(aport); 86 #endif 87 /* GROSS */ 88 if (aport < IPPORT_RESERVED && u.u_uid != 0) 89 return (EACCES); 90 if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || 91 (so->so_options & SO_ACCEPTCONN) == 0) 92 wild = INPLOOKUP_WILDCARD; 93 if (in_pcblookup(head, 94 zeroin_addr, 0, sin->sin_addr, lport, wild)) 95 return (EADDRINUSE); 96 } 97 } 98 m = m_getclr(M_DONTWAIT); 99 if (m == 0) 100 return (ENOBUFS); 101 if (sbreserve(&so->so_snd, sndcc) == 0) 102 goto bad; 103 if (sbreserve(&so->so_rcv, rcvcc) == 0) 104 goto bad2; 105 inp = mtod(m, struct inpcb *); 106 inp->inp_head = head; 107 if (sin) 108 inp->inp_laddr = sin->sin_addr; 109 if (lport == 0) 110 do { 111 if (head->inp_lport++ < IPPORT_RESERVED) 112 head->inp_lport = IPPORT_RESERVED; 113 lport = htons(head->inp_lport); 114 } while (in_pcblookup(head, 115 zeroin_addr, 0, inp->inp_laddr, lport, 0)); 116 inp->inp_lport = lport; 117 inp->inp_socket = so; 118 insque(inp, head); 119 so->so_pcb = (caddr_t)inp; 120 return (0); 121 bad2: 122 sbrelease(&so->so_snd); 123 bad: 124 (void) m_free(m); 125 return (ENOBUFS); 126 } 127 128 /* 129 * Connect from a socket to a specified address. 130 * Both address and port must be specified in argument sin. 131 * If don't have a local address for this socket yet, 132 * then pick one. 133 */ 134 in_pcbconnect(inp, sin) 135 struct inpcb *inp; 136 struct sockaddr_in *sin; 137 { 138 struct ifnet *ifp; 139 struct sockaddr_in *ifaddr; 140 141 COUNT(IN_PCBCONNECT); 142 if (sin->sin_family != AF_INET) 143 return (EAFNOSUPPORT); 144 if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0) 145 return (EADDRNOTAVAIL); 146 if (inp->inp_laddr.s_addr == 0) { 147 ifp = if_ifonnetof(sin->sin_addr.s_net); 148 if (ifp == 0) { 149 /* 150 * We should select the interface based on 151 * the route to be used, but for udp this would 152 * result in two calls to rtalloc for each packet 153 * sent; hardly worthwhile... 154 */ 155 ifp = if_ifwithaf(AF_INET); 156 if (ifp == 0) 157 return (EADDRNOTAVAIL); 158 } 159 ifaddr = (struct sockaddr_in *)&ifp->if_addr; 160 } 161 if (in_pcblookup(inp->inp_head, 162 sin->sin_addr, 163 sin->sin_port, 164 inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 165 inp->inp_lport, 166 0)) 167 return (EADDRINUSE); 168 if (inp->inp_laddr.s_addr == 0) 169 inp->inp_laddr = ifaddr->sin_addr; 170 inp->inp_faddr = sin->sin_addr; 171 inp->inp_fport = sin->sin_port; 172 return (0); 173 } 174 175 in_pcbdisconnect(inp) 176 struct inpcb *inp; 177 { 178 179 COUNT(IN_PCBDISCONNECT); 180 inp->inp_faddr.s_addr = 0; 181 inp->inp_fport = 0; 182 if (inp->inp_socket->so_state & SS_USERGONE) 183 in_pcbdetach(inp); 184 } 185 186 in_pcbdetach(inp) 187 struct inpcb *inp; 188 { 189 struct socket *so = inp->inp_socket; 190 191 COUNT(IN_PCBDETACH); 192 so->so_pcb = 0; 193 sofree(so); 194 if (inp->inp_route.ro_rt) 195 rtfree(inp->inp_route.ro_rt); 196 remque(inp); 197 (void) m_free(dtom(inp)); 198 } 199 200 in_setsockaddr(sin, inp) 201 register struct sockaddr_in *sin; 202 register struct inpcb *inp; 203 { 204 COUNT(IN_SETSOCKADDR); 205 if (sin == 0 || inp == 0) 206 panic("setsockaddr_in"); 207 bzero((caddr_t)sin, sizeof (*sin)); 208 sin->sin_family = AF_INET; 209 sin->sin_port = inp->inp_lport; 210 sin->sin_addr = inp->inp_laddr; 211 } 212 213 /* 214 * Pass an error to all internet connections 215 * associated with address sin. Call the 216 * protocol specific routine to clean up the 217 * mess afterwards. 218 */ 219 in_pcbnotify(head, dst, errno, abort) 220 struct inpcb *head; 221 register struct in_addr *dst; 222 int errno, (*abort)(); 223 { 224 register struct inpcb *inp, *oinp; 225 int s = splimp(); 226 227 COUNT(INP_PCBNOTIFY); 228 for (inp = head->inp_next; inp != head;) { 229 if (inp->inp_faddr.s_addr != dst->s_addr) { 230 next: 231 inp = inp->inp_next; 232 continue; 233 } 234 if (inp->inp_socket == 0) 235 goto next; 236 inp->inp_socket->so_error = errno; 237 oinp = inp; 238 inp = inp->inp_next; 239 (*abort)(oinp); 240 } 241 splx(s); 242 } 243 244 /* 245 * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY 246 */ 247 struct inpcb * 248 in_pcblookup(head, faddr, fport, laddr, lport, flags) 249 struct inpcb *head; 250 struct in_addr faddr, laddr; 251 u_short fport, lport; 252 int flags; 253 { 254 register struct inpcb *inp, *match = 0; 255 int matchwild = 3, wildcard; 256 257 for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 258 if (inp->inp_lport != lport) 259 continue; 260 wildcard = 0; 261 if (inp->inp_laddr.s_addr != 0) { 262 if (laddr.s_addr == 0) 263 wildcard++; 264 else if (inp->inp_laddr.s_addr != laddr.s_addr) 265 continue; 266 } else { 267 if (laddr.s_addr != 0) 268 wildcard++; 269 } 270 if (inp->inp_faddr.s_addr != 0) { 271 if (faddr.s_addr == 0) 272 wildcard++; 273 else if (inp->inp_faddr.s_addr != faddr.s_addr || 274 inp->inp_fport != fport) 275 continue; 276 } else { 277 if (faddr.s_addr != 0) 278 wildcard++; 279 } 280 if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) 281 continue; 282 if (wildcard < matchwild) { 283 match = inp; 284 matchwild = wildcard; 285 if (matchwild == 0) 286 break; 287 } 288 } 289 return (match); 290 } 291