1*6338Ssam /* in_pcb.c 4.22 82/03/28 */ 24905Swnj 34905Swnj #include "../h/param.h" 44951Swnj #include "../h/systm.h" 54951Swnj #include "../h/dir.h" 64951Swnj #include "../h/user.h" 74905Swnj #include "../h/mbuf.h" 84905Swnj #include "../h/socket.h" 94905Swnj #include "../h/socketvar.h" 105084Swnj #include "../net/in.h" 115084Swnj #include "../net/in_systm.h" 124951Swnj #include "../net/if.h" 135084Swnj #include "../net/in_pcb.h" 146116Swnj #include "../h/protosw.h" 154905Swnj 164951Swnj /* 175161Swnj * Routines to manage internet protocol control blocks. 185161Swnj * 195161Swnj * At PRU_ATTACH time a protocol control block is allocated in 205161Swnj * in_pcballoc() and inserted on a doubly-linked list of such blocks 215161Swnj * for the protocol. A port address is either requested (and verified 225161Swnj * to not be in use) or assigned at this time. We also allocate 235161Swnj * space in the socket sockbuf structures here, although this is 245161Swnj * not a clearly correct place to put this function. 255161Swnj * 265161Swnj * A connectionless protocol will have its protocol control block 275161Swnj * removed at PRU_DETACH time, when the socket will be freed (freeing 285161Swnj * the space reserved) and the block will be removed from the list of 295161Swnj * blocks for its protocol. 305161Swnj * 315161Swnj * A connection-based protocol may be connected to a remote peer at 325161Swnj * PRU_CONNECT time through the routine in_pcbconnect(). In the normal 335161Swnj * case a PRU_DISCONNECT occurs causing a in_pcbdisconnect(). 345161Swnj * It is also possible that higher-level routines will opt out of the 355161Swnj * relationship with the connection before the connection shut down 365161Swnj * is complete. This often occurs in protocols like TCP where we must 375161Swnj * hold on to the protocol control block for a unreasonably long time 385161Swnj * after the connection is used up to avoid races in later connection 395161Swnj * establishment. To handle this we allow higher-level routines to 405161Swnj * disassociate themselves from the socket, marking it SS_USERGONE while 415161Swnj * the disconnect is in progress. We notice that this has happened 425161Swnj * when the disconnect is complete, and perform the PRU_DETACH operation, 435161Swnj * freeing the socket. 445172Swnj * 455172Swnj * TODO: 465172Swnj * use hashing 475161Swnj */ 485240Sroot struct in_addr zeroin_addr; 495161Swnj 505161Swnj /* 514951Swnj * Allocate a protocol control block, space 524951Swnj * for send and receive data, and local host information. 534951Swnj * Return error. If no error make socket point at pcb. 544951Swnj */ 555161Swnj in_pcbattach(so, head, sndcc, rcvcc, sin) 564951Swnj struct socket *so; 574951Swnj struct inpcb *head; 584951Swnj int sndcc, rcvcc; 594951Swnj struct sockaddr_in *sin; 604905Swnj { 614905Swnj struct mbuf *m; 625240Sroot register struct inpcb *inp; 635994Swnj u_short lport = 0; 644905Swnj 655161Swnj COUNT(IN_PCBATTACH); 665994Swnj if (ifnet == 0) 675994Swnj return (EADDRNOTAVAIL); 684951Swnj if (sin) { 694951Swnj if (sin->sin_family != AF_INET) 704951Swnj return (EAFNOSUPPORT); 715994Swnj if (sin->sin_addr.s_addr && 72*6338Ssam if_ifwithaddr((struct sockaddr *)sin) == 0) 735994Swnj return (EADDRNOTAVAIL); 744951Swnj lport = sin->sin_port; 755994Swnj if (lport) { 765994Swnj u_short aport = lport; 776116Swnj int wild = 0; 785994Swnj #if vax 795994Swnj aport = htons(aport); 805994Swnj #endif 815994Swnj /* GROSS */ 825994Swnj if (aport < IPPORT_RESERVED && u.u_uid != 0) 835994Swnj return (EPERM); 846116Swnj if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || 856116Swnj (so->so_options & SO_ACCEPTCONN) == 0) 866116Swnj wild = INPLOOKUP_WILDCARD; 875994Swnj if (in_pcblookup(head, 886116Swnj zeroin_addr, 0, sin->sin_addr, lport, wild)) 895994Swnj return (EADDRINUSE); 905994Swnj } 914951Swnj } 925852Sroot m = m_getclr(M_DONTWAIT); 934951Swnj if (m == 0) 944983Swnj return (ENOBUFS); 954951Swnj if (sbreserve(&so->so_snd, sndcc) == 0) 964951Swnj goto bad; 974951Swnj if (sbreserve(&so->so_rcv, rcvcc) == 0) 984951Swnj goto bad2; 994951Swnj inp = mtod(m, struct inpcb *); 1005172Swnj inp->inp_head = head; 1015994Swnj if (sin) 1025994Swnj inp->inp_laddr = sin->sin_addr; 1035172Swnj if (lport == 0) 1045172Swnj do { 1055994Swnj if (head->inp_lport++ < IPPORT_RESERVED) 1065994Swnj head->inp_lport = IPPORT_RESERVED; 1075172Swnj lport = htons(head->inp_lport); 1085994Swnj } while (in_pcblookup(head, 1095994Swnj zeroin_addr, 0, inp->inp_laddr, lport, 0)); 1105172Swnj inp->inp_lport = lport; 1114983Swnj inp->inp_socket = so; 1124983Swnj insque(inp, head); 1134951Swnj so->so_pcb = (caddr_t)inp; 1145994Swnj in_setsockaddr(inp); 1154951Swnj return (0); 1164951Swnj bad2: 1174951Swnj sbrelease(&so->so_snd); 1184951Swnj bad: 1194967Swnj (void) m_free(m); 1204951Swnj return (ENOBUFS); 1214905Swnj } 1224905Swnj 1236116Swnj /* 1246116Swnj * Connect from a socket to a specified address. 1256116Swnj * Both address and port must be specified in argument sin. 1266116Swnj * If don't have a local address for this socket yet, 1276116Swnj * then pick one. 1286116Swnj */ 1295161Swnj in_pcbconnect(inp, sin) 1304951Swnj struct inpcb *inp; 1314951Swnj struct sockaddr_in *sin; 1324923Swnj { 1335994Swnj struct ifnet *ifp; 134*6338Ssam struct sockaddr_in *ifaddr; 1354923Swnj 1365161Swnj COUNT(IN_PCBCONNECT); 1374951Swnj if (sin->sin_family != AF_INET) 1384951Swnj return (EAFNOSUPPORT); 1394951Swnj if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0) 1404951Swnj return (EADDRNOTAVAIL); 1415994Swnj if (inp->inp_laddr.s_addr == 0) { 142*6338Ssam ifp = if_ifonnetof(sin->sin_addr.s_net); 143*6338Ssam if (ifp == 0) { 144*6338Ssam ifp = if_ifwithaf(AF_INET); 145*6338Ssam if (ifp == 0) 146*6338Ssam return (EADDRNOTAVAIL); /* XXX */ 147*6338Ssam } 148*6338Ssam ifaddr = (struct sockaddr_in *)&ifp->if_addr; 1495994Swnj } 1505994Swnj if (in_pcblookup(inp->inp_head, 1516116Swnj sin->sin_addr, 1526116Swnj sin->sin_port, 153*6338Ssam inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 1546116Swnj inp->inp_lport, 1556116Swnj 0)) 1565172Swnj return (EADDRINUSE); 1576297Swnj if (inp->inp_laddr.s_addr == 0) { 1586297Swnj struct sockaddr_in *sin2 = 1596297Swnj (struct sockaddr_in *)&inp->inp_socket->so_addr; 1606297Swnj 161*6338Ssam inp->inp_laddr = ifaddr->sin_addr; 1626297Swnj sin2->sin_addr = inp->inp_laddr; 1636297Swnj } 1644951Swnj inp->inp_faddr = sin->sin_addr; 1654951Swnj inp->inp_fport = sin->sin_port; 1664923Swnj return (0); 1674923Swnj } 1684923Swnj 1695994Swnj in_setsockaddr(inp) 1705277Sroot struct inpcb *inp; 1715277Sroot { 1725994Swnj register struct sockaddr_in *sin = 1735994Swnj (struct sockaddr_in *)&inp->inp_socket->so_addr; 1745277Sroot 1755277Sroot sin->sin_family = AF_INET; 1765994Swnj sin->sin_addr = inp->inp_laddr; 1775994Swnj sin->sin_port = inp->inp_lport; 1785277Sroot } 1795277Sroot 1805161Swnj in_pcbdisconnect(inp) 1814905Swnj struct inpcb *inp; 1824905Swnj { 1835161Swnj 1845161Swnj COUNT(IN_PCBDISCONNECT); 1855161Swnj inp->inp_faddr.s_addr = 0; 1866028Sroot inp->inp_fport = 0; 1875161Swnj if (inp->inp_socket->so_state & SS_USERGONE) 1885161Swnj in_pcbdetach(inp); 1895161Swnj } 1905161Swnj 1915161Swnj in_pcbdetach(inp) 1925161Swnj struct inpcb *inp; 1935161Swnj { 1944905Swnj struct socket *so = inp->inp_socket; 1954905Swnj 1965009Swnj so->so_pcb = 0; 1975009Swnj sofree(so); 1984983Swnj remque(inp); 1994907Swnj (void) m_free(dtom(inp)); 2004905Swnj } 2014905Swnj 2025161Swnj /* 2035994Swnj * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY 2045161Swnj */ 2054907Swnj struct inpcb * 2066028Sroot in_pcblookup(head, faddr, fport, laddr, lport, flags) 2074905Swnj struct inpcb *head; 2084951Swnj struct in_addr faddr, laddr; 2094905Swnj u_short fport, lport; 2106028Sroot int flags; 2114905Swnj { 2125994Swnj register struct inpcb *inp, *match = 0; 2135994Swnj int matchwild = 3, wildcard; 2144905Swnj 2155161Swnj for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 2165994Swnj if (inp->inp_lport != lport) 2175161Swnj continue; 2185994Swnj wildcard = 0; 2195994Swnj if (inp->inp_laddr.s_addr != 0) { 2206116Swnj if (laddr.s_addr == 0) 2216116Swnj wildcard++; 2226116Swnj else if (inp->inp_laddr.s_addr != laddr.s_addr) 2235994Swnj continue; 2245994Swnj } else { 2255994Swnj if (laddr.s_addr != 0) 2265994Swnj wildcard++; 2275994Swnj } 2285994Swnj if (inp->inp_faddr.s_addr != 0) { 2296116Swnj if (faddr.s_addr == 0) 2306116Swnj wildcard++; 2316116Swnj else if (inp->inp_faddr.s_addr != faddr.s_addr || 2326028Sroot inp->inp_fport != fport) 2335994Swnj continue; 2345994Swnj } else { 2355994Swnj if (faddr.s_addr != 0) 2365994Swnj wildcard++; 2375994Swnj } 2386028Sroot if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) 2395994Swnj continue; 2405994Swnj if (wildcard < matchwild) { 2415161Swnj match = inp; 2425994Swnj matchwild = wildcard; 2435994Swnj if (matchwild == 0) 2445994Swnj break; 2455161Swnj } 2465161Swnj } 2475161Swnj return (match); 2484905Swnj } 249