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.11 (Berkeley) 02/01/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 = (struct in_ifaddr *)ifa_ifwithnet((struct sockaddr *)sin); 139 if (ia == (struct in_ifaddr *)0) { 140 register struct route *ro; 141 struct ifnet *ifp; 142 143 /* 144 * If route is known or can be allocated now, 145 * our src addr is taken from the i/f, else punt. 146 */ 147 ro = &inp->inp_route; 148 if (ro->ro_rt && 149 satosin(&ro->ro_dst)->sin_addr.s_addr != 150 sin->sin_addr.s_addr) { 151 RTFREE(ro->ro_rt); 152 ro->ro_rt = (struct rtentry *)0; 153 } 154 if ((ro->ro_rt == (struct rtentry *)0) || 155 (ifp = ro->ro_rt->rt_ifp) == (struct ifnet *)0) { 156 /* No route yet, so try to acquire one */ 157 ro->ro_dst.sa_family = AF_INET; 158 ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = 159 sin->sin_addr; 160 rtalloc(ro); 161 if (ro->ro_rt == 0) 162 ifp = (struct ifnet *)0; 163 else 164 ifp = ro->ro_rt->rt_ifp; 165 } 166 if (ifp) { 167 for (ia = in_ifaddr; ia; ia = ia->ia_next) 168 if (ia->ia_ifp == ifp) 169 break; 170 } else 171 ia = (struct in_ifaddr *)0; 172 if (ia == 0) 173 ia = in_ifaddr; 174 if (ia == 0) 175 return (EADDRNOTAVAIL); 176 } 177 ifaddr = (struct sockaddr_in *)&ia->ia_addr; 178 } 179 if (in_pcblookup(inp->inp_head, 180 sin->sin_addr, 181 sin->sin_port, 182 inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 183 inp->inp_lport, 184 0)) 185 return (EADDRINUSE); 186 if (inp->inp_laddr.s_addr == INADDR_ANY) { 187 if (inp->inp_lport == 0) 188 in_pcbbind(inp, (struct mbuf *)0); 189 inp->inp_laddr = ifaddr->sin_addr; 190 } 191 inp->inp_faddr = sin->sin_addr; 192 inp->inp_fport = sin->sin_port; 193 return (0); 194 } 195 196 in_pcbdisconnect(inp) 197 struct inpcb *inp; 198 { 199 200 inp->inp_faddr.s_addr = INADDR_ANY; 201 inp->inp_fport = 0; 202 if (inp->inp_socket->so_state & SS_NOFDREF) 203 in_pcbdetach(inp); 204 } 205 206 in_pcbdetach(inp) 207 struct inpcb *inp; 208 { 209 struct socket *so = inp->inp_socket; 210 211 so->so_pcb = 0; 212 sofree(so); 213 if (inp->inp_options) 214 m_free(inp->inp_options); 215 if (inp->inp_route.ro_rt) 216 rtfree(inp->inp_route.ro_rt); 217 remque(inp); 218 (void) m_free(dtom(inp)); 219 } 220 221 in_setsockaddr(inp, nam) 222 register struct inpcb *inp; 223 struct mbuf *nam; 224 { 225 register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 226 227 nam->m_len = sizeof (*sin); 228 sin = mtod(nam, struct sockaddr_in *); 229 bzero((caddr_t)sin, sizeof (*sin)); 230 sin->sin_family = AF_INET; 231 sin->sin_port = inp->inp_lport; 232 sin->sin_addr = inp->inp_laddr; 233 } 234 235 in_setpeeraddr(inp, nam) 236 register struct inpcb *inp; 237 struct mbuf *nam; 238 { 239 register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 240 241 nam->m_len = sizeof (*sin); 242 sin = mtod(nam, struct sockaddr_in *); 243 bzero((caddr_t)sin, sizeof (*sin)); 244 sin->sin_family = AF_INET; 245 sin->sin_port = inp->inp_fport; 246 sin->sin_addr = inp->inp_faddr; 247 } 248 249 /* 250 * Pass some notification to all connections of a protocol 251 * associated with address dst. Call the protocol specific 252 * routine (if any) to handle each connection. 253 */ 254 in_pcbnotify(head, dst, errno, notify) 255 struct inpcb *head; 256 register struct in_addr *dst; 257 int errno, (*notify)(); 258 { 259 register struct inpcb *inp, *oinp; 260 int s = splimp(); 261 262 for (inp = head->inp_next; inp != head;) { 263 if (inp->inp_faddr.s_addr != dst->s_addr || 264 inp->inp_socket == 0) { 265 inp = inp->inp_next; 266 continue; 267 } 268 if (errno) 269 inp->inp_socket->so_error = errno; 270 oinp = inp; 271 inp = inp->inp_next; 272 if (notify) 273 (*notify)(oinp); 274 } 275 splx(s); 276 } 277 278 /* 279 * Check for alternatives when higher level complains 280 * about service problems. For now, invalidate cached 281 * routing information. If the route was created dynamically 282 * (by a redirect), time to try a default gateway again. 283 */ 284 in_losing(inp) 285 struct inpcb *inp; 286 { 287 register struct rtentry *rt; 288 289 if ((rt = inp->inp_route.ro_rt)) { 290 if (rt->rt_flags & RTF_DYNAMIC) 291 rtrequest(SIOCDELRT, rt); 292 rtfree(rt); 293 inp->inp_route.ro_rt = 0; 294 /* 295 * A new route can be allocated 296 * the next time output is attempted. 297 */ 298 } 299 } 300 301 /* 302 * After a routing change, flush old routing 303 * and allocate a (hopefully) better one. 304 */ 305 in_rtchange(inp) 306 register struct inpcb *inp; 307 { 308 if (inp->inp_route.ro_rt) { 309 rtfree(inp->inp_route.ro_rt); 310 inp->inp_route.ro_rt = 0; 311 /* 312 * A new route can be allocated the next time 313 * output is attempted. 314 */ 315 } 316 } 317 318 struct inpcb * 319 in_pcblookup(head, faddr, fport, laddr, lport, flags) 320 struct inpcb *head; 321 struct in_addr faddr, laddr; 322 u_short fport, lport; 323 int flags; 324 { 325 register struct inpcb *inp, *match = 0; 326 int matchwild = 3, wildcard; 327 328 for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 329 if (inp->inp_lport != lport) 330 continue; 331 wildcard = 0; 332 if (inp->inp_laddr.s_addr != INADDR_ANY) { 333 if (laddr.s_addr == INADDR_ANY) 334 wildcard++; 335 else if (inp->inp_laddr.s_addr != laddr.s_addr) 336 continue; 337 } else { 338 if (laddr.s_addr != INADDR_ANY) 339 wildcard++; 340 } 341 if (inp->inp_faddr.s_addr != INADDR_ANY) { 342 if (faddr.s_addr == INADDR_ANY) 343 wildcard++; 344 else if (inp->inp_faddr.s_addr != faddr.s_addr || 345 inp->inp_fport != fport) 346 continue; 347 } else { 348 if (faddr.s_addr != INADDR_ANY) 349 wildcard++; 350 } 351 if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) 352 continue; 353 if (wildcard < matchwild) { 354 match = inp; 355 matchwild = wildcard; 356 if (matchwild == 0) 357 break; 358 } 359 } 360 return (match); 361 } 362