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