1 /* 2 * Copyright (c) 1982 Regents of the University of California. 3 * All rights reserved. The Berkeley software License Agreement 4 * specifies the terms and conditions for redistribution. 5 * 6 * @(#)in_pcb.c 6.14 (Berkeley) 02/23/86 7 */ 8 9 #include "param.h" 10 #include "systm.h" 11 #include "dir.h" 12 #include "user.h" 13 #include "mbuf.h" 14 #include "socket.h" 15 #include "socketvar.h" 16 #include "ioctl.h" 17 #include "in.h" 18 #include "in_systm.h" 19 #include "../net/if.h" 20 #include "../net/route.h" 21 #include "in_pcb.h" 22 #include "in_var.h" 23 #include "protosw.h" 24 25 struct in_addr zeroin_addr; 26 27 in_pcballoc(so, head) 28 struct socket *so; 29 struct inpcb *head; 30 { 31 struct mbuf *m; 32 register struct inpcb *inp; 33 34 m = m_getclr(M_DONTWAIT, MT_PCB); 35 if (m == NULL) 36 return (ENOBUFS); 37 inp = mtod(m, struct inpcb *); 38 inp->inp_head = head; 39 inp->inp_socket = so; 40 insque(inp, head); 41 so->so_pcb = (caddr_t)inp; 42 return (0); 43 } 44 45 in_pcbbind(inp, nam) 46 register struct inpcb *inp; 47 struct mbuf *nam; 48 { 49 register struct socket *so = inp->inp_socket; 50 register struct inpcb *head = inp->inp_head; 51 register struct sockaddr_in *sin; 52 u_short lport = 0; 53 54 if (in_ifaddr == 0) 55 return (EADDRNOTAVAIL); 56 if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) 57 return (EINVAL); 58 if (nam == 0) 59 goto noname; 60 sin = mtod(nam, struct sockaddr_in *); 61 if (nam->m_len != sizeof (*sin)) 62 return (EINVAL); 63 if (sin->sin_addr.s_addr != INADDR_ANY) { 64 int tport = sin->sin_port; 65 66 sin->sin_port = 0; /* yech... */ 67 if (ifa_ifwithaddr((struct sockaddr *)sin) == 0) 68 return (EADDRNOTAVAIL); 69 sin->sin_port = tport; 70 } 71 lport = sin->sin_port; 72 if (lport) { 73 u_short aport = ntohs(lport); 74 int wild = 0; 75 76 /* GROSS */ 77 if (aport < IPPORT_RESERVED && u.u_uid != 0) 78 return (EACCES); 79 /* even GROSSER, but this is the Internet */ 80 if ((so->so_options & SO_REUSEADDR) == 0 && 81 ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || 82 (so->so_options & SO_ACCEPTCONN) == 0)) 83 wild = INPLOOKUP_WILDCARD; 84 if (in_pcblookup(head, 85 zeroin_addr, 0, sin->sin_addr, lport, wild)) 86 return (EADDRINUSE); 87 } 88 inp->inp_laddr = sin->sin_addr; 89 noname: 90 if (lport == 0) 91 do { 92 if (head->inp_lport++ < IPPORT_RESERVED || 93 head->inp_lport > IPPORT_USERRESERVED) 94 head->inp_lport = IPPORT_RESERVED; 95 lport = htons(head->inp_lport); 96 } while (in_pcblookup(head, 97 zeroin_addr, 0, inp->inp_laddr, lport, 0)); 98 inp->inp_lport = lport; 99 return (0); 100 } 101 102 /* 103 * Connect from a socket to a specified address. 104 * Both address and port must be specified in argument sin. 105 * If don't have a local address for this socket yet, 106 * then pick one. 107 */ 108 in_pcbconnect(inp, nam) 109 struct inpcb *inp; 110 struct mbuf *nam; 111 { 112 struct in_ifaddr *ia; 113 struct sockaddr_in *ifaddr; 114 register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 115 116 if (nam->m_len != sizeof (*sin)) 117 return (EINVAL); 118 if (sin->sin_family != AF_INET) 119 return (EAFNOSUPPORT); 120 if (sin->sin_port == 0) 121 return (EADDRNOTAVAIL); 122 if (in_ifaddr) { 123 /* 124 * If the destination address is INADDR_ANY, 125 * use the primary local address. 126 * If the supplied address is INADDR_BROADCAST, 127 * and the primary interface supports broadcast, 128 * choose the broadcast address for that interface. 129 */ 130 #define satosin(sa) ((struct sockaddr_in *)(sa)) 131 if (sin->sin_addr.s_addr == INADDR_ANY) 132 sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr; 133 else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST && 134 (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST)) 135 sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr; 136 } 137 if (inp->inp_laddr.s_addr == INADDR_ANY) { 138 ia = in_iaonnetof(in_netof(sin->sin_addr)); 139 if (ia == (struct in_ifaddr *)0 || 140 (ia->ia_ifp->if_flags & IFF_UP) == 0) { 141 register struct route *ro; 142 struct ifnet *ifp; 143 144 ia = (struct in_ifaddr *)0; 145 /* 146 * If route is known or can be allocated now, 147 * our src addr is taken from the i/f, else punt. 148 */ 149 ro = &inp->inp_route; 150 if (ro->ro_rt && 151 satosin(&ro->ro_dst)->sin_addr.s_addr != 152 sin->sin_addr.s_addr) { 153 RTFREE(ro->ro_rt); 154 ro->ro_rt = (struct rtentry *)0; 155 } 156 if ((ro->ro_rt == (struct rtentry *)0) || 157 (ifp = ro->ro_rt->rt_ifp) == (struct ifnet *)0) { 158 /* No route yet, so try to acquire one */ 159 ro->ro_dst.sa_family = AF_INET; 160 ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = 161 sin->sin_addr; 162 rtalloc(ro); 163 if (ro->ro_rt == (struct rtentry *)0) 164 ifp = (struct ifnet *)0; 165 else 166 ifp = ro->ro_rt->rt_ifp; 167 } 168 if (ifp) { 169 for (ia = in_ifaddr; ia; ia = ia->ia_next) 170 if (ia->ia_ifp == ifp) 171 break; 172 } 173 if (ia == 0) 174 ia = in_ifaddr; 175 if (ia == 0) 176 return (EADDRNOTAVAIL); 177 } 178 ifaddr = (struct sockaddr_in *)&ia->ia_addr; 179 } 180 if (in_pcblookup(inp->inp_head, 181 sin->sin_addr, 182 sin->sin_port, 183 inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 184 inp->inp_lport, 185 0)) 186 return (EADDRINUSE); 187 if (inp->inp_laddr.s_addr == INADDR_ANY) { 188 if (inp->inp_lport == 0) 189 (void)in_pcbbind(inp, (struct mbuf *)0); 190 inp->inp_laddr = ifaddr->sin_addr; 191 } 192 inp->inp_faddr = sin->sin_addr; 193 inp->inp_fport = sin->sin_port; 194 return (0); 195 } 196 197 in_pcbdisconnect(inp) 198 struct inpcb *inp; 199 { 200 201 inp->inp_faddr.s_addr = INADDR_ANY; 202 inp->inp_fport = 0; 203 if (inp->inp_socket->so_state & SS_NOFDREF) 204 in_pcbdetach(inp); 205 } 206 207 in_pcbdetach(inp) 208 struct inpcb *inp; 209 { 210 struct socket *so = inp->inp_socket; 211 212 so->so_pcb = 0; 213 sofree(so); 214 if (inp->inp_options) 215 (void)m_free(inp->inp_options); 216 if (inp->inp_route.ro_rt) 217 rtfree(inp->inp_route.ro_rt); 218 remque(inp); 219 (void) m_free(dtom(inp)); 220 } 221 222 in_setsockaddr(inp, nam) 223 register struct inpcb *inp; 224 struct mbuf *nam; 225 { 226 register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 227 228 nam->m_len = sizeof (*sin); 229 sin = mtod(nam, struct sockaddr_in *); 230 bzero((caddr_t)sin, sizeof (*sin)); 231 sin->sin_family = AF_INET; 232 sin->sin_port = inp->inp_lport; 233 sin->sin_addr = inp->inp_laddr; 234 } 235 236 in_setpeeraddr(inp, nam) 237 register struct inpcb *inp; 238 struct mbuf *nam; 239 { 240 register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 241 242 nam->m_len = sizeof (*sin); 243 sin = mtod(nam, struct sockaddr_in *); 244 bzero((caddr_t)sin, sizeof (*sin)); 245 sin->sin_family = AF_INET; 246 sin->sin_port = inp->inp_fport; 247 sin->sin_addr = inp->inp_faddr; 248 } 249 250 /* 251 * Pass some notification to all connections of a protocol 252 * associated with address dst. Call the protocol specific 253 * routine (if any) to handle each connection. 254 */ 255 in_pcbnotify(head, dst, errno, notify) 256 struct inpcb *head; 257 register struct in_addr *dst; 258 int errno, (*notify)(); 259 { 260 register struct inpcb *inp, *oinp; 261 int s = splimp(); 262 263 for (inp = head->inp_next; inp != head;) { 264 if (inp->inp_faddr.s_addr != dst->s_addr || 265 inp->inp_socket == 0) { 266 inp = inp->inp_next; 267 continue; 268 } 269 if (errno) 270 inp->inp_socket->so_error = errno; 271 oinp = inp; 272 inp = inp->inp_next; 273 if (notify) 274 (*notify)(oinp); 275 } 276 splx(s); 277 } 278 279 /* 280 * Check for alternatives when higher level complains 281 * about service problems. For now, invalidate cached 282 * routing information. If the route was created dynamically 283 * (by a redirect), time to try a default gateway again. 284 */ 285 in_losing(inp) 286 struct inpcb *inp; 287 { 288 register struct rtentry *rt; 289 290 if ((rt = inp->inp_route.ro_rt)) { 291 if (rt->rt_flags & RTF_DYNAMIC) 292 (void) rtrequest(SIOCDELRT, rt); 293 rtfree(rt); 294 inp->inp_route.ro_rt = 0; 295 /* 296 * A new route can be allocated 297 * the next time output is attempted. 298 */ 299 } 300 } 301 302 /* 303 * After a routing change, flush old routing 304 * and allocate a (hopefully) better one. 305 */ 306 in_rtchange(inp) 307 register struct inpcb *inp; 308 { 309 if (inp->inp_route.ro_rt) { 310 rtfree(inp->inp_route.ro_rt); 311 inp->inp_route.ro_rt = 0; 312 /* 313 * A new route can be allocated the next time 314 * output is attempted. 315 */ 316 } 317 } 318 319 struct inpcb * 320 in_pcblookup(head, faddr, fport, laddr, lport, flags) 321 struct inpcb *head; 322 struct in_addr faddr, laddr; 323 u_short fport, lport; 324 int flags; 325 { 326 register struct inpcb *inp, *match = 0; 327 int matchwild = 3, wildcard; 328 329 for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 330 if (inp->inp_lport != lport) 331 continue; 332 wildcard = 0; 333 if (inp->inp_laddr.s_addr != INADDR_ANY) { 334 if (laddr.s_addr == INADDR_ANY) 335 wildcard++; 336 else if (inp->inp_laddr.s_addr != laddr.s_addr) 337 continue; 338 } else { 339 if (laddr.s_addr != INADDR_ANY) 340 wildcard++; 341 } 342 if (inp->inp_faddr.s_addr != INADDR_ANY) { 343 if (faddr.s_addr == INADDR_ANY) 344 wildcard++; 345 else if (inp->inp_faddr.s_addr != faddr.s_addr || 346 inp->inp_fport != fport) 347 continue; 348 } else { 349 if (faddr.s_addr != INADDR_ANY) 350 wildcard++; 351 } 352 if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) 353 continue; 354 if (wildcard < matchwild) { 355 match = inp; 356 matchwild = wildcard; 357 if (matchwild == 0) 358 break; 359 } 360 } 361 return (match); 362 } 363