1 /* in_pcb.c 4.20 82/03/13 */ 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/in_pcb.h" 14 #include "../h/protosw.h" 15 16 /* 17 * Routines to manage internet protocol control blocks. 18 * 19 * At PRU_ATTACH time a protocol control block is allocated in 20 * in_pcballoc() and inserted on a doubly-linked list of such blocks 21 * for the protocol. A port address is either requested (and verified 22 * to not be in use) or assigned at this time. We also allocate 23 * space in the socket sockbuf structures here, although this is 24 * not a clearly correct place to put this function. 25 * 26 * A connectionless protocol will have its protocol control block 27 * removed at PRU_DETACH time, when the socket will be freed (freeing 28 * the space reserved) and the block will be removed from the list of 29 * blocks for its protocol. 30 * 31 * A connection-based protocol may be connected to a remote peer at 32 * PRU_CONNECT time through the routine in_pcbconnect(). In the normal 33 * case a PRU_DISCONNECT occurs causing a in_pcbdisconnect(). 34 * It is also possible that higher-level routines will opt out of the 35 * relationship with the connection before the connection shut down 36 * is complete. This often occurs in protocols like TCP where we must 37 * hold on to the protocol control block for a unreasonably long time 38 * after the connection is used up to avoid races in later connection 39 * establishment. To handle this we allow higher-level routines to 40 * disassociate themselves from the socket, marking it SS_USERGONE while 41 * the disconnect is in progress. We notice that this has happened 42 * when the disconnect is complete, and perform the PRU_DETACH operation, 43 * freeing the socket. 44 * 45 * TODO: 46 * use hashing 47 */ 48 struct in_addr zeroin_addr; 49 50 /* 51 * Allocate a protocol control block, space 52 * for send and receive data, and local host information. 53 * Return error. If no error make socket point at pcb. 54 */ 55 in_pcbattach(so, head, sndcc, rcvcc, sin) 56 struct socket *so; 57 struct inpcb *head; 58 int sndcc, rcvcc; 59 struct sockaddr_in *sin; 60 { 61 struct mbuf *m; 62 register struct inpcb *inp; 63 u_short lport = 0; 64 65 COUNT(IN_PCBATTACH); 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 if_ifwithaddr(sin->sin_addr) == 0) 73 return (EADDRNOTAVAIL); 74 lport = sin->sin_port; 75 if (lport) { 76 u_short aport = lport; 77 int wild = 0; 78 #if vax 79 aport = htons(aport); 80 #endif 81 /* GROSS */ 82 if (aport < IPPORT_RESERVED && u.u_uid != 0) 83 return (EPERM); 84 if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || 85 (so->so_options & SO_ACCEPTCONN) == 0) 86 wild = INPLOOKUP_WILDCARD; 87 if (in_pcblookup(head, 88 zeroin_addr, 0, sin->sin_addr, lport, wild)) 89 return (EADDRINUSE); 90 } 91 } 92 m = m_getclr(M_DONTWAIT); 93 if (m == 0) 94 return (ENOBUFS); 95 if (sbreserve(&so->so_snd, sndcc) == 0) 96 goto bad; 97 if (sbreserve(&so->so_rcv, rcvcc) == 0) 98 goto bad2; 99 inp = mtod(m, struct inpcb *); 100 inp->inp_head = head; 101 if (sin) 102 inp->inp_laddr = sin->sin_addr; 103 if (lport == 0) 104 do { 105 if (head->inp_lport++ < IPPORT_RESERVED) 106 head->inp_lport = IPPORT_RESERVED; 107 lport = htons(head->inp_lport); 108 } while (in_pcblookup(head, 109 zeroin_addr, 0, inp->inp_laddr, lport, 0)); 110 inp->inp_lport = lport; 111 inp->inp_socket = so; 112 insque(inp, head); 113 so->so_pcb = (caddr_t)inp; 114 in_setsockaddr(inp); 115 return (0); 116 bad2: 117 sbrelease(&so->so_snd); 118 bad: 119 (void) m_free(m); 120 return (ENOBUFS); 121 } 122 123 /* 124 * Connect from a socket to a specified address. 125 * Both address and port must be specified in argument sin. 126 * If don't have a local address for this socket yet, 127 * then pick one. 128 */ 129 in_pcbconnect(inp, sin) 130 struct inpcb *inp; 131 struct sockaddr_in *sin; 132 { 133 struct ifnet *ifp; 134 135 COUNT(IN_PCBCONNECT); 136 if (sin->sin_family != AF_INET) 137 return (EAFNOSUPPORT); 138 if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0) 139 return (EADDRNOTAVAIL); 140 if (inp->inp_laddr.s_addr == 0) { 141 ifp = if_ifonnetof(sin->sin_addr); 142 if (ifp == 0) 143 ifp = ifnet; 144 } 145 if (in_pcblookup(inp->inp_head, 146 sin->sin_addr, 147 sin->sin_port, 148 inp->inp_laddr.s_addr ? inp->inp_laddr : ifp->if_addr, 149 inp->inp_lport, 150 0)) 151 return (EADDRINUSE); 152 if (inp->inp_laddr.s_addr == 0) 153 inp->inp_laddr = ifp->if_addr; 154 inp->inp_faddr = sin->sin_addr; 155 inp->inp_fport = sin->sin_port; 156 return (0); 157 } 158 159 in_setsockaddr(inp) 160 struct inpcb *inp; 161 { 162 register struct sockaddr_in *sin = 163 (struct sockaddr_in *)&inp->inp_socket->so_addr; 164 165 sin->sin_family = AF_INET; 166 sin->sin_addr = inp->inp_laddr; 167 sin->sin_port = inp->inp_lport; 168 } 169 170 in_pcbdisconnect(inp) 171 struct inpcb *inp; 172 { 173 174 COUNT(IN_PCBDISCONNECT); 175 inp->inp_faddr.s_addr = 0; 176 inp->inp_fport = 0; 177 if (inp->inp_socket->so_state & SS_USERGONE) 178 in_pcbdetach(inp); 179 } 180 181 in_pcbdetach(inp) 182 struct inpcb *inp; 183 { 184 struct socket *so = inp->inp_socket; 185 186 so->so_pcb = 0; 187 sofree(so); 188 remque(inp); 189 (void) m_free(dtom(inp)); 190 } 191 192 /* 193 * Look for a control block to accept a segment, or to make 194 * sure 195 * First choice is an exact address match. 196 * Second choice is a match with either the foreign or the local 197 * address specified. 198 * 199 * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY 200 */ 201 struct inpcb * 202 in_pcblookup(head, faddr, fport, laddr, lport, flags) 203 struct inpcb *head; 204 struct in_addr faddr, laddr; 205 u_short fport, lport; 206 int flags; 207 { 208 register struct inpcb *inp, *match = 0; 209 int matchwild = 3, wildcard; 210 211 for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 212 if (inp->inp_lport != lport) 213 continue; 214 wildcard = 0; 215 if (inp->inp_laddr.s_addr != 0) { 216 if (laddr.s_addr == 0) 217 wildcard++; 218 else if (inp->inp_laddr.s_addr != laddr.s_addr) 219 continue; 220 } else { 221 if (laddr.s_addr != 0) 222 wildcard++; 223 } 224 if (inp->inp_faddr.s_addr != 0) { 225 if (faddr.s_addr == 0) 226 wildcard++; 227 else if (inp->inp_faddr.s_addr != faddr.s_addr || 228 inp->inp_fport != fport) 229 continue; 230 } else { 231 if (faddr.s_addr != 0) 232 wildcard++; 233 } 234 if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) 235 continue; 236 if (wildcard < matchwild) { 237 match = inp; 238 matchwild = wildcard; 239 if (matchwild == 0) 240 break; 241 } 242 } 243 return (match); 244 } 245