1 /* in_pcb.c 4.25 82/04/10 */ 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 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 ifp = if_ifwithaf(AF_INET); 150 if (ifp == 0) 151 return (EADDRNOTAVAIL); /* XXX */ 152 } 153 ifaddr = (struct sockaddr_in *)&ifp->if_addr; 154 } 155 if (in_pcblookup(inp->inp_head, 156 sin->sin_addr, 157 sin->sin_port, 158 inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 159 inp->inp_lport, 160 0)) 161 return (EADDRINUSE); 162 if (inp->inp_laddr.s_addr == 0) 163 inp->inp_laddr = ifaddr->sin_addr; 164 inp->inp_faddr = sin->sin_addr; 165 inp->inp_fport = sin->sin_port; 166 return (0); 167 } 168 169 in_pcbdisconnect(inp) 170 struct inpcb *inp; 171 { 172 173 COUNT(IN_PCBDISCONNECT); 174 inp->inp_faddr.s_addr = 0; 175 inp->inp_fport = 0; 176 if (inp->inp_socket->so_state & SS_USERGONE) 177 in_pcbdetach(inp); 178 } 179 180 in_pcbdetach(inp) 181 struct inpcb *inp; 182 { 183 struct socket *so = inp->inp_socket; 184 185 so->so_pcb = 0; 186 sofree(so); 187 if (inp->inp_route.ro_rt) 188 rtfree(inp->inp_route.ro_rt); 189 remque(inp); 190 (void) m_free(dtom(inp)); 191 } 192 193 in_setsockaddr(sin, inp) 194 register struct sockaddr_in *sin; 195 register struct inpcb *inp; 196 { 197 if (sin == 0 || inp == 0) 198 panic("setsockaddr_in"); 199 bzero((caddr_t)sin, sizeof (*sin)); 200 sin->sin_family = AF_INET; 201 sin->sin_port = inp->inp_lport; 202 sin->sin_addr = inp->inp_laddr; 203 } 204 205 /* 206 * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY 207 */ 208 struct inpcb * 209 in_pcblookup(head, faddr, fport, laddr, lport, flags) 210 struct inpcb *head; 211 struct in_addr faddr, laddr; 212 u_short fport, lport; 213 int flags; 214 { 215 register struct inpcb *inp, *match = 0; 216 int matchwild = 3, wildcard; 217 218 for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 219 if (inp->inp_lport != lport) 220 continue; 221 wildcard = 0; 222 if (inp->inp_laddr.s_addr != 0) { 223 if (laddr.s_addr == 0) 224 wildcard++; 225 else if (inp->inp_laddr.s_addr != laddr.s_addr) 226 continue; 227 } else { 228 if (laddr.s_addr != 0) 229 wildcard++; 230 } 231 if (inp->inp_faddr.s_addr != 0) { 232 if (faddr.s_addr == 0) 233 wildcard++; 234 else if (inp->inp_faddr.s_addr != faddr.s_addr || 235 inp->inp_fport != fport) 236 continue; 237 } else { 238 if (faddr.s_addr != 0) 239 wildcard++; 240 } 241 if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) 242 continue; 243 if (wildcard < matchwild) { 244 match = inp; 245 matchwild = wildcard; 246 if (matchwild == 0) 247 break; 248 } 249 } 250 return (match); 251 } 252