1*6509Ssam /* in_pcb.c 4.25 82/04/10 */ 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" 136350Ssam #include "../net/route.h" 145084Swnj #include "../net/in_pcb.h" 156116Swnj #include "../h/protosw.h" 164905Swnj 174951Swnj /* 185161Swnj * Routines to manage internet protocol control blocks. 195161Swnj * 205161Swnj * At PRU_ATTACH time a protocol control block is allocated in 215161Swnj * in_pcballoc() and inserted on a doubly-linked list of such blocks 225161Swnj * for the protocol. A port address is either requested (and verified 235161Swnj * to not be in use) or assigned at this time. We also allocate 245161Swnj * space in the socket sockbuf structures here, although this is 255161Swnj * not a clearly correct place to put this function. 265161Swnj * 275161Swnj * A connectionless protocol will have its protocol control block 285161Swnj * removed at PRU_DETACH time, when the socket will be freed (freeing 295161Swnj * the space reserved) and the block will be removed from the list of 305161Swnj * blocks for its protocol. 315161Swnj * 325161Swnj * A connection-based protocol may be connected to a remote peer at 335161Swnj * PRU_CONNECT time through the routine in_pcbconnect(). In the normal 345161Swnj * case a PRU_DISCONNECT occurs causing a in_pcbdisconnect(). 355161Swnj * It is also possible that higher-level routines will opt out of the 365161Swnj * relationship with the connection before the connection shut down 375161Swnj * is complete. This often occurs in protocols like TCP where we must 385161Swnj * hold on to the protocol control block for a unreasonably long time 395161Swnj * after the connection is used up to avoid races in later connection 405161Swnj * establishment. To handle this we allow higher-level routines to 415161Swnj * disassociate themselves from the socket, marking it SS_USERGONE while 425161Swnj * the disconnect is in progress. We notice that this has happened 435161Swnj * when the disconnect is complete, and perform the PRU_DETACH operation, 445161Swnj * freeing the socket. 455172Swnj * 465172Swnj * TODO: 475172Swnj * use hashing 485161Swnj */ 495240Sroot struct in_addr zeroin_addr; 505161Swnj 515161Swnj /* 524951Swnj * Allocate a protocol control block, space 534951Swnj * for send and receive data, and local host information. 544951Swnj * Return error. If no error make socket point at pcb. 554951Swnj */ 565161Swnj in_pcbattach(so, head, sndcc, rcvcc, sin) 574951Swnj struct socket *so; 584951Swnj struct inpcb *head; 594951Swnj int sndcc, rcvcc; 604951Swnj struct sockaddr_in *sin; 614905Swnj { 624905Swnj struct mbuf *m; 635240Sroot register struct inpcb *inp; 645994Swnj u_short lport = 0; 654905Swnj 665161Swnj COUNT(IN_PCBATTACH); 675994Swnj if (ifnet == 0) 685994Swnj return (EADDRNOTAVAIL); 694951Swnj if (sin) { 704951Swnj if (sin->sin_family != AF_INET) 714951Swnj return (EAFNOSUPPORT); 726350Ssam if (sin->sin_addr.s_addr) { 736350Ssam int tport = sin->sin_port; 746350Ssam 756350Ssam sin->sin_port = 0; /* yech... */ 766350Ssam if (if_ifwithaddr((struct sockaddr *)sin) == 0) 776350Ssam return (EADDRNOTAVAIL); 786350Ssam sin->sin_port = tport; 796350Ssam } 804951Swnj lport = sin->sin_port; 815994Swnj if (lport) { 825994Swnj u_short aport = lport; 836116Swnj int wild = 0; 845994Swnj #if vax 855994Swnj aport = htons(aport); 865994Swnj #endif 875994Swnj /* GROSS */ 885994Swnj if (aport < IPPORT_RESERVED && u.u_uid != 0) 895994Swnj return (EPERM); 906116Swnj if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || 916116Swnj (so->so_options & SO_ACCEPTCONN) == 0) 926116Swnj wild = INPLOOKUP_WILDCARD; 935994Swnj if (in_pcblookup(head, 946116Swnj zeroin_addr, 0, sin->sin_addr, lport, wild)) 955994Swnj return (EADDRINUSE); 965994Swnj } 974951Swnj } 985852Sroot m = m_getclr(M_DONTWAIT); 994951Swnj if (m == 0) 1004983Swnj return (ENOBUFS); 1014951Swnj if (sbreserve(&so->so_snd, sndcc) == 0) 1024951Swnj goto bad; 1034951Swnj if (sbreserve(&so->so_rcv, rcvcc) == 0) 1044951Swnj goto bad2; 1054951Swnj inp = mtod(m, struct inpcb *); 1065172Swnj inp->inp_head = head; 1075994Swnj if (sin) 1085994Swnj inp->inp_laddr = sin->sin_addr; 1095172Swnj if (lport == 0) 1105172Swnj do { 1115994Swnj if (head->inp_lport++ < IPPORT_RESERVED) 1125994Swnj head->inp_lport = IPPORT_RESERVED; 1135172Swnj lport = htons(head->inp_lport); 1145994Swnj } while (in_pcblookup(head, 1155994Swnj zeroin_addr, 0, inp->inp_laddr, lport, 0)); 1165172Swnj inp->inp_lport = lport; 1174983Swnj inp->inp_socket = so; 1184983Swnj insque(inp, head); 1194951Swnj so->so_pcb = (caddr_t)inp; 1204951Swnj return (0); 1214951Swnj bad2: 1224951Swnj sbrelease(&so->so_snd); 1234951Swnj bad: 1244967Swnj (void) m_free(m); 1254951Swnj return (ENOBUFS); 1264905Swnj } 1274905Swnj 1286116Swnj /* 1296116Swnj * Connect from a socket to a specified address. 1306116Swnj * Both address and port must be specified in argument sin. 1316116Swnj * If don't have a local address for this socket yet, 1326116Swnj * then pick one. 1336116Swnj */ 1345161Swnj in_pcbconnect(inp, sin) 1354951Swnj struct inpcb *inp; 1364951Swnj struct sockaddr_in *sin; 1374923Swnj { 1385994Swnj struct ifnet *ifp; 1396338Ssam struct sockaddr_in *ifaddr; 1404923Swnj 1415161Swnj COUNT(IN_PCBCONNECT); 1424951Swnj if (sin->sin_family != AF_INET) 1434951Swnj return (EAFNOSUPPORT); 1444951Swnj if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0) 1454951Swnj return (EADDRNOTAVAIL); 1465994Swnj if (inp->inp_laddr.s_addr == 0) { 1476338Ssam ifp = if_ifonnetof(sin->sin_addr.s_net); 1486338Ssam if (ifp == 0) { 1496338Ssam ifp = if_ifwithaf(AF_INET); 1506338Ssam if (ifp == 0) 1516338Ssam return (EADDRNOTAVAIL); /* XXX */ 1526338Ssam } 1536338Ssam ifaddr = (struct sockaddr_in *)&ifp->if_addr; 1545994Swnj } 1555994Swnj if (in_pcblookup(inp->inp_head, 1566116Swnj sin->sin_addr, 1576116Swnj sin->sin_port, 1586338Ssam inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 1596116Swnj inp->inp_lport, 1606116Swnj 0)) 1615172Swnj return (EADDRINUSE); 162*6509Ssam if (inp->inp_laddr.s_addr == 0) 1636338Ssam inp->inp_laddr = ifaddr->sin_addr; 1644951Swnj inp->inp_faddr = sin->sin_addr; 1654951Swnj inp->inp_fport = sin->sin_port; 1664923Swnj return (0); 1674923Swnj } 1684923Swnj 1695161Swnj in_pcbdisconnect(inp) 1704905Swnj struct inpcb *inp; 1714905Swnj { 1725161Swnj 1735161Swnj COUNT(IN_PCBDISCONNECT); 1745161Swnj inp->inp_faddr.s_addr = 0; 1756028Sroot inp->inp_fport = 0; 1765161Swnj if (inp->inp_socket->so_state & SS_USERGONE) 1775161Swnj in_pcbdetach(inp); 1785161Swnj } 1795161Swnj 1805161Swnj in_pcbdetach(inp) 1815161Swnj struct inpcb *inp; 1825161Swnj { 1834905Swnj struct socket *so = inp->inp_socket; 1844905Swnj 1855009Swnj so->so_pcb = 0; 1865009Swnj sofree(so); 1876350Ssam if (inp->inp_route.ro_rt) 1886367Ssam rtfree(inp->inp_route.ro_rt); 1894983Swnj remque(inp); 1904907Swnj (void) m_free(dtom(inp)); 1914905Swnj } 1924905Swnj 193*6509Ssam in_setsockaddr(sin, inp) 194*6509Ssam register struct sockaddr_in *sin; 195*6509Ssam register struct inpcb *inp; 196*6509Ssam { 197*6509Ssam if (sin == 0 || inp == 0) 198*6509Ssam panic("setsockaddr_in"); 199*6509Ssam bzero((caddr_t)sin, sizeof (*sin)); 200*6509Ssam sin->sin_family = AF_INET; 201*6509Ssam sin->sin_port = inp->inp_lport; 202*6509Ssam sin->sin_addr = inp->inp_laddr; 203*6509Ssam } 204*6509Ssam 2055161Swnj /* 2065994Swnj * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY 2075161Swnj */ 2084907Swnj struct inpcb * 2096028Sroot in_pcblookup(head, faddr, fport, laddr, lport, flags) 2104905Swnj struct inpcb *head; 2114951Swnj struct in_addr faddr, laddr; 2124905Swnj u_short fport, lport; 2136028Sroot int flags; 2144905Swnj { 2155994Swnj register struct inpcb *inp, *match = 0; 2165994Swnj int matchwild = 3, wildcard; 2174905Swnj 2185161Swnj for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 2195994Swnj if (inp->inp_lport != lport) 2205161Swnj continue; 2215994Swnj wildcard = 0; 2225994Swnj if (inp->inp_laddr.s_addr != 0) { 2236116Swnj if (laddr.s_addr == 0) 2246116Swnj wildcard++; 2256116Swnj else if (inp->inp_laddr.s_addr != laddr.s_addr) 2265994Swnj continue; 2275994Swnj } else { 2285994Swnj if (laddr.s_addr != 0) 2295994Swnj wildcard++; 2305994Swnj } 2315994Swnj if (inp->inp_faddr.s_addr != 0) { 2326116Swnj if (faddr.s_addr == 0) 2336116Swnj wildcard++; 2346116Swnj else if (inp->inp_faddr.s_addr != faddr.s_addr || 2356028Sroot inp->inp_fport != fport) 2365994Swnj continue; 2375994Swnj } else { 2385994Swnj if (faddr.s_addr != 0) 2395994Swnj wildcard++; 2405994Swnj } 2416028Sroot if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) 2425994Swnj continue; 2435994Swnj if (wildcard < matchwild) { 2445161Swnj match = inp; 2455994Swnj matchwild = wildcard; 2465994Swnj if (matchwild == 0) 2475994Swnj break; 2485161Swnj } 2495161Swnj } 2505161Swnj return (match); 2514905Swnj } 252