1 #ifdef RCSIDENT 2 static char rcsident[] = "$Header: in_pcb.c,v 1.12 84/11/29 17:02:13 walsh Exp $"; 3 #endif RCSIDENT 4 5 #include "../h/param.h" 6 #include "../h/systm.h" 7 #include "../h/dir.h" 8 #include "../h/user.h" 9 #include "../h/mbuf.h" 10 #include "../h/socket.h" 11 #include "../h/socketvar.h" 12 #include "../h/protosw.h" 13 #include "../h/domain.h" 14 15 #include "../net/if.h" 16 #include "../net/route.h" 17 18 #include "../bbnnet/in.h" 19 #include "../bbnnet/net.h" 20 #include "../bbnnet/in_pcb.h" 21 #include "../bbnnet/in_var.h" 22 23 extern struct rtentry *ip_route(); 24 extern struct domain inetdomain; 25 26 in_pcballoc(so, head) 27 struct socket *so; 28 struct inpcb *head; 29 { 30 register struct mbuf *m; 31 register struct inpcb *inp; 32 33 m = m_getclr(M_DONTWAIT, MT_PCB); 34 if (m == NULL) 35 return (ENOBUFS); 36 37 inp = mtod(m, struct inpcb *); 38 inp->inp_socket = so; 39 40 insque(inp,head); 41 42 so->so_pcb = (caddr_t)inp; 43 44 return (0); 45 } 46 47 /* 48 * changed from 4.2 to accept a structure which has protocol 49 * specific data like how to break down port allocation. 50 */ 51 52 in_pcbbind(inp, nam, advice) 53 register struct inpcb *inp; 54 struct mbuf *nam; 55 struct pr_advice *advice; 56 { 57 register struct socket *so = inp->inp_socket; 58 register struct sockaddr_in *sin; 59 register u_short lport = 0; 60 61 if (in_ifaddr == NULL) 62 return (EADDRNOTAVAIL); 63 /* 64 * socket must not already be bound 65 */ 66 67 if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) 68 return (EINVAL); 69 70 if (nam == 0) 71 goto noname; 72 sin = mtod(nam, struct sockaddr_in *); 73 if (nam->m_len != sizeof (*sin)) 74 return (EINVAL); 75 /* 76 * Since Berkeley left this out, some of their programs (ftpd) 77 * aren't ready for it 78 * 79 if (sin->sin_family != AF_INET) 80 return (EAFNOSUPPORT); 81 */ 82 83 if (sin->sin_addr.s_addr != INADDR_ANY) 84 { 85 /* some code says ..withnet() */ 86 if (in_iawithaddr(sin->sin_addr, FALSE) == NULL) 87 return (EADDRNOTAVAIL); 88 89 } 90 91 /* user gives port to us in net order */ 92 if (lport = sin->sin_port) 93 { 94 u_short aport; 95 96 /* if portsize > 2 a major rewrite needed to 97 * accomodate longs..... 98 */ 99 100 if (advice->portsize > 1) 101 aport = ntohs(lport); 102 else 103 { 104 if (lport != (lport & 0xff)) 105 return(EINVAL); /* must be 8 bits */ 106 107 aport = lport; /* a char is a char */ 108 } 109 110 /* 111 * really only a worry for byte size ports 112 */ 113 114 if (aport > advice->maxport) 115 return(EADDRNOTAVAIL); 116 117 if (aport <= advice->rootport && u.u_uid != 0) 118 return (EACCES); 119 120 /* 121 * Check to see if the local address/port is in use. 122 * but, process may use this pair to communicate with 123 * several destinations (each with its own tcp) if he 124 * sets SO_REUSEADDR 125 */ 126 if (advice->bind_used && 127 (*(advice->bind_used))(inp, /* current binding */ 128 lport, /* desired port */ 129 sin->sin_addr.s_addr, /* desired address */ 130 so->so_options & SO_REUSEADDR)) 131 { 132 return (EADDRINUSE); 133 } 134 } 135 inp->inp_laddr = sin->sin_addr; 136 137 noname : 138 /* any ports for random allocation by non-root users? */ 139 if ((advice->maxport <= advice->resvport) && (u.u_uid)) 140 return(EADDRNOTAVAIL); 141 142 if (lport == 0) 143 { 144 /* 145 * Allow for reserved ports for non-super users 146 * so that don't interfere with some project's software. 147 */ 148 u_short possible = advice->nowport; 149 150 do 151 { 152 if (advice->portsize > 1) 153 lport = htons(possible); 154 else 155 lport = possible; 156 157 /* 158 * catch roll over..... 159 */ 160 161 if (possible >= advice->maxport) 162 possible = advice->resvport + 1; 163 else 164 possible++; 165 166 /* 167 * no free ports??? RDP/HMP problem 168 */ 169 170 if (possible == advice->nowport) 171 return(EADDRNOTAVAIL); 172 173 } 174 while (advice->bind_used && 175 (*(advice->bind_used))(inp, lport, inp->inp_laddr.s_addr, 0)); 176 177 advice->nowport = possible; 178 } 179 inp->inp_lport = lport; 180 return (0); 181 } 182 183 /* 184 * Connect from a socket to a specified address. 185 * Both address and port must be specified in argument sin. 186 * If don't have a local address for this socket yet, 187 * then pick one. 188 */ 189 in_pcbconnect(inp, nam, conn_used) 190 struct inpcb *inp; 191 struct mbuf *nam; 192 char *(*conn_used)(); 193 { 194 register struct ifnet *ifp = NULL; 195 register struct in_ifaddr *ia = NULL; 196 register struct sockaddr_in *ifaddr; 197 register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 198 register struct rtentry *rt; 199 struct sockaddr_in *inpsin; 200 201 if (nam->m_len != sizeof (*sin)) 202 return (EINVAL); 203 if (sin->sin_family != AF_INET) 204 return (EAFNOSUPPORT); 205 if (sin->sin_addr.s_addr == INADDR_ANY || sin->sin_port == 0) 206 return (EADDRNOTAVAIL); 207 208 /* 209 * Find route for connection. For a tcp connecting to a server, 210 * this route will be used for the duration of the connection 211 * (unless redirected...). For a UDP doing a connect, this route 212 * will also be used for the duration. For a UDP unconnected send, 213 * this route will be used for the current packet. 214 * 215 * rtalloc cannot handle routing with both sides already bound 216 */ 217 rt = (struct rtentry *) NULL; 218 219 /* 220 * NOTE: programmers often forget to zero sin_zero[0-1]. 221 * rtalloc does not want to know the port number for routes to hosts. 222 */ 223 inpsin = (struct sockaddr_in *) &inp->inp_route.ro_dst; 224 bcopy((caddr_t)sin, (caddr_t)inpsin, sizeof (*sin)); 225 inpsin->sin_port = 0; 226 227 if (inp->inp_laddr.s_addr == INADDR_ANY) 228 { 229 rtalloc(&inp->inp_route); 230 if (rt = inp->inp_route.ro_rt) 231 ifp = rt->rt_ifp; 232 } 233 else 234 { 235 if (rt = ip_route(&inp->inp_laddr, &sin->sin_addr)) 236 { 237 inp->inp_route.ro_rt = rt; 238 ifp = rt->rt_ifp; 239 } 240 } 241 242 if (ifp == NULL) 243 return (ENETUNREACH); 244 245 /* 246 * find Internet address structure for this interface. 247 */ 248 ia = in_iafromif(ifp); 249 250 if (ia == NULL) 251 /* ??? */ 252 return (ENETUNREACH); 253 254 ifaddr = (struct sockaddr_in *) &ia->ia_addr; 255 256 #ifdef bsd42 257 /* 258 * 8.7.0.2 (on IMP net) can send to 128.11.0.0 (on Ethernet), but 259 * not to 8.0.0.0 260 */ 261 if (in_broadcast(sin->sin_addr) && 262 iptonet(sin->sin_addr) == iptonet(ifaddr->sin_addr) && 263 !(ifp->if_flags & IFF_BROADCAST) ) 264 { 265 if (rt) 266 { 267 rtfree(rt); 268 inp->inp_route.ro_rt = NULL; 269 } 270 return (EADDRNOTAVAIL); 271 } 272 #endif 273 274 if ((*conn_used)(inp, 275 inp->inp_lport, 276 (inp->inp_laddr.s_addr ? inp->inp_laddr.s_addr : ifaddr->sin_addr.s_addr), 277 sin->sin_port, 278 sin->sin_addr.s_addr) != (char *)NULL) 279 { 280 281 if (rt) 282 { 283 rtfree(rt); 284 inp->inp_route.ro_rt = NULL; 285 } 286 return (EADDRINUSE); 287 } 288 289 if (inp->inp_laddr.s_addr == INADDR_ANY) 290 inp->inp_laddr = ifaddr->sin_addr; 291 inp->inp_faddr = sin->sin_addr; 292 inp->inp_fport = sin->sin_port; 293 return (0); 294 } 295 296 in_pcbdisconnect(inp, pcb_free_func) 297 struct inpcb *inp; 298 int (*pcb_free_func)(); 299 { 300 inp->inp_faddr.s_addr = INADDR_ANY; 301 inp->inp_fport = 0; 302 /* 303 * may attach a route to an inpcb several times. For example, 304 * when UDP does unconnected, but bound, sends. 305 */ 306 if (inp->inp_route.ro_rt) 307 { 308 rtfree(inp->inp_route.ro_rt); 309 inp->inp_route.ro_rt = NULL; 310 } 311 312 if (inp->inp_socket->so_state & SS_NOFDREF) 313 in_pcbdetach(inp, pcb_free_func); 314 } 315 316 /* 317 * Don't need to splnet while altering lists, since called from places 318 * where that has already been done. 319 */ 320 in_pcbdetach(inp, pcb_free_func) 321 register struct inpcb *inp; 322 int (*pcb_free_func)(); 323 { 324 register struct socket *so; 325 326 if (so = inp->inp_socket) 327 { 328 so->so_pcb = (caddr_t) NULL; 329 /* inp->inp_socket = (struct socket *) NULL; */ 330 soisdisconnected(so); 331 sofree(so); 332 } 333 else 334 panic("in_pcbdetach"); 335 336 if (inp->inp_route.ro_rt) 337 rtfree(inp->inp_route.ro_rt); 338 339 if (inp->inp_ppcb) 340 (*pcb_free_func)(inp); /* free per-protocol block */ 341 342 remque(inp); 343 344 (void) m_free(dtom(inp)); 345 } 346 347 in_setsockaddr(inp, nam) 348 register struct inpcb *inp; 349 struct mbuf *nam; 350 { 351 register struct sockaddr_in *sin; 352 353 nam->m_len = sizeof (*sin); 354 sin = mtod(nam, struct sockaddr_in *); 355 bzero((caddr_t)sin, sizeof (*sin)); 356 sin->sin_family = AF_INET; 357 sin->sin_port = inp->inp_lport; 358 sin->sin_addr = inp->inp_laddr; 359 } 360 361 in_setpeeraddr(inp, nam) 362 register struct inpcb *inp; 363 struct mbuf *nam; 364 { 365 register struct sockaddr_in *sin; 366 367 nam->m_len = sizeof (*sin); 368 sin = mtod(nam, struct sockaddr_in *); 369 bzero((caddr_t)sin, sizeof (*sin)); 370 sin->sin_family = AF_INET; 371 sin->sin_port = inp->inp_fport; 372 sin->sin_addr = inp->inp_faddr; 373 } 374 375 /* 376 * somewhat different from the one in 4.2 and (I think) substantially 377 * easier to read, though a bit slower. 378 * 379 * fport == 0 if don't want/need match on remote port # (HMP and UDP) 380 */ 381 struct inpcb * 382 in_pcblookup(head,faddr,fport,laddr,lport,wild) 383 struct inpcb *head; 384 u_long faddr, laddr; 385 u_short fport, lport; 386 int wild; 387 { 388 register struct inpcb *inp; 389 390 /* try exact match */ 391 for(inp = head->inp_next; inp != head; inp = inp->inp_next) 392 { 393 /* ports check */ 394 if (inp->inp_lport != lport) 395 continue; 396 397 if (fport && (inp->inp_fport != fport)) 398 continue; 399 400 if ((inp->inp_faddr.s_addr != faddr) || (inp->inp_laddr.s_addr != laddr)) 401 continue; 402 403 /* keep it! */ 404 return(inp); 405 } 406 407 /* try wildcard ? */ 408 if (wild) 409 { 410 for(inp = head->inp_next; inp != head; inp = inp->inp_next) 411 { 412 /* ports again */ 413 if (inp->inp_lport != lport) 414 continue; 415 416 if (fport && (inp->inp_fport != fport) && inp->inp_fport) 417 continue; 418 419 if ((inp->inp_faddr.s_addr) && (inp->inp_faddr.s_addr != faddr)) 420 continue; 421 422 if ((inp->inp_laddr.s_addr) && (inp->inp_laddr.s_addr != laddr)) 423 continue; 424 425 return(inp); 426 } 427 } 428 429 return((struct inpcb *) NULL); 430 } 431 432 433 /* 434 * This only advises process and does not internally close socket, 435 * not so much because the user can do much but close when he gets a 436 * HOSTDEAD/HOSTUNREACH indication, but because it is possible that 437 * the destination host has saved connection state information. (His IMP 438 * interface went down for PM, but the machine stayed up...) 439 * 440 * Also, this makes addition of new protocols easy, since we don't need to 441 * know the name and calling sequence of their close/abort routine. 442 * 443 * We do not close child sockets of listen(2)ers for connection oriented 444 * protocols. We let the protocol do that by timing out connection 445 * establishment. 446 */ 447 inpcb_notify(head, laddr, faddr, error) 448 register struct inpcb *head; 449 register u_long laddr; 450 register u_long faddr; 451 { 452 register struct inpcb *inp; 453 454 for(inp = head->inp_next; inp != head; inp = inp->inp_next) 455 if (((inp->inp_faddr.s_addr == faddr) || (faddr == 0)) && 456 ((inp->inp_laddr.s_addr == laddr) || (laddr == 0))) 457 advise_user(inp->inp_socket, error); 458 } 459 460 advise_user(so, error) 461 struct socket *so; 462 int error; 463 { 464 if (so == 0) 465 return; 466 467 so->so_error = error; 468 469 wakeup((caddr_t) &so->so_timeo); /* in connect(2) */ 470 sowwakeup(so); 471 sorwakeup(so); 472 } 473