1 /* in_pcb.c 4.24 82/03/30 */ 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 (EPERM); 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 in_setsockaddr(inp); 121 return (0); 122 bad2: 123 sbrelease(&so->so_snd); 124 bad: 125 (void) m_free(m); 126 return (ENOBUFS); 127 } 128 129 /* 130 * Connect from a socket to a specified address. 131 * Both address and port must be specified in argument sin. 132 * If don't have a local address for this socket yet, 133 * then pick one. 134 */ 135 in_pcbconnect(inp, sin) 136 struct inpcb *inp; 137 struct sockaddr_in *sin; 138 { 139 struct ifnet *ifp; 140 struct sockaddr_in *ifaddr; 141 142 COUNT(IN_PCBCONNECT); 143 if (sin->sin_family != AF_INET) 144 return (EAFNOSUPPORT); 145 if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0) 146 return (EADDRNOTAVAIL); 147 if (inp->inp_laddr.s_addr == 0) { 148 ifp = if_ifonnetof(sin->sin_addr.s_net); 149 if (ifp == 0) { 150 ifp = if_ifwithaf(AF_INET); 151 if (ifp == 0) 152 return (EADDRNOTAVAIL); /* XXX */ 153 } 154 ifaddr = (struct sockaddr_in *)&ifp->if_addr; 155 } 156 if (in_pcblookup(inp->inp_head, 157 sin->sin_addr, 158 sin->sin_port, 159 inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 160 inp->inp_lport, 161 0)) 162 return (EADDRINUSE); 163 if (inp->inp_laddr.s_addr == 0) { 164 struct sockaddr_in *sin2 = 165 (struct sockaddr_in *)&inp->inp_socket->so_addr; 166 167 inp->inp_laddr = ifaddr->sin_addr; 168 sin2->sin_addr = inp->inp_laddr; 169 } 170 inp->inp_faddr = sin->sin_addr; 171 inp->inp_fport = sin->sin_port; 172 return (0); 173 } 174 175 in_setsockaddr(inp) 176 struct inpcb *inp; 177 { 178 register struct sockaddr_in *sin = 179 (struct sockaddr_in *)&inp->inp_socket->so_addr; 180 181 sin->sin_family = AF_INET; 182 sin->sin_addr = inp->inp_laddr; 183 sin->sin_port = inp->inp_lport; 184 } 185 186 in_pcbdisconnect(inp) 187 struct inpcb *inp; 188 { 189 190 COUNT(IN_PCBDISCONNECT); 191 inp->inp_faddr.s_addr = 0; 192 inp->inp_fport = 0; 193 if (inp->inp_socket->so_state & SS_USERGONE) 194 in_pcbdetach(inp); 195 } 196 197 in_pcbdetach(inp) 198 struct inpcb *inp; 199 { 200 struct socket *so = inp->inp_socket; 201 202 so->so_pcb = 0; 203 sofree(so); 204 if (inp->inp_route.ro_rt) 205 rtfree(inp->inp_route.ro_rt); 206 remque(inp); 207 (void) m_free(dtom(inp)); 208 } 209 210 /* 211 * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY 212 */ 213 struct inpcb * 214 in_pcblookup(head, faddr, fport, laddr, lport, flags) 215 struct inpcb *head; 216 struct in_addr faddr, laddr; 217 u_short fport, lport; 218 int flags; 219 { 220 register struct inpcb *inp, *match = 0; 221 int matchwild = 3, wildcard; 222 223 for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 224 if (inp->inp_lport != lport) 225 continue; 226 wildcard = 0; 227 if (inp->inp_laddr.s_addr != 0) { 228 if (laddr.s_addr == 0) 229 wildcard++; 230 else if (inp->inp_laddr.s_addr != laddr.s_addr) 231 continue; 232 } else { 233 if (laddr.s_addr != 0) 234 wildcard++; 235 } 236 if (inp->inp_faddr.s_addr != 0) { 237 if (faddr.s_addr == 0) 238 wildcard++; 239 else if (inp->inp_faddr.s_addr != faddr.s_addr || 240 inp->inp_fport != fport) 241 continue; 242 } else { 243 if (faddr.s_addr != 0) 244 wildcard++; 245 } 246 if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) 247 continue; 248 if (wildcard < matchwild) { 249 match = inp; 250 matchwild = wildcard; 251 if (matchwild == 0) 252 break; 253 } 254 } 255 return (match); 256 } 257