1*6583Ssam /* in_pcb.c 4.26 82/04/24 */ 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) 89*6583Ssam return (EACCES); 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) { 149*6583Ssam /* 150*6583Ssam * We should select the interface based on 151*6583Ssam * the route to be used, but for udp this would 152*6583Ssam * result in two calls to rtalloc for each packet 153*6583Ssam * sent; hardly worthwhile... 154*6583Ssam */ 1556338Ssam ifp = if_ifwithaf(AF_INET); 1566338Ssam if (ifp == 0) 157*6583Ssam return (EADDRNOTAVAIL); 1586338Ssam } 1596338Ssam ifaddr = (struct sockaddr_in *)&ifp->if_addr; 1605994Swnj } 1615994Swnj if (in_pcblookup(inp->inp_head, 1626116Swnj sin->sin_addr, 1636116Swnj sin->sin_port, 1646338Ssam inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 1656116Swnj inp->inp_lport, 1666116Swnj 0)) 1675172Swnj return (EADDRINUSE); 1686509Ssam if (inp->inp_laddr.s_addr == 0) 1696338Ssam inp->inp_laddr = ifaddr->sin_addr; 1704951Swnj inp->inp_faddr = sin->sin_addr; 1714951Swnj inp->inp_fport = sin->sin_port; 1724923Swnj return (0); 1734923Swnj } 1744923Swnj 1755161Swnj in_pcbdisconnect(inp) 1764905Swnj struct inpcb *inp; 1774905Swnj { 1785161Swnj 1795161Swnj COUNT(IN_PCBDISCONNECT); 1805161Swnj inp->inp_faddr.s_addr = 0; 1816028Sroot inp->inp_fport = 0; 1825161Swnj if (inp->inp_socket->so_state & SS_USERGONE) 1835161Swnj in_pcbdetach(inp); 1845161Swnj } 1855161Swnj 1865161Swnj in_pcbdetach(inp) 1875161Swnj struct inpcb *inp; 1885161Swnj { 1894905Swnj struct socket *so = inp->inp_socket; 1904905Swnj 191*6583Ssam COUNT(IN_PCBDETACH); 1925009Swnj so->so_pcb = 0; 1935009Swnj sofree(so); 1946350Ssam if (inp->inp_route.ro_rt) 1956367Ssam rtfree(inp->inp_route.ro_rt); 1964983Swnj remque(inp); 1974907Swnj (void) m_free(dtom(inp)); 1984905Swnj } 1994905Swnj 2006509Ssam in_setsockaddr(sin, inp) 2016509Ssam register struct sockaddr_in *sin; 2026509Ssam register struct inpcb *inp; 2036509Ssam { 204*6583Ssam COUNT(IN_SETSOCKADDR); 2056509Ssam if (sin == 0 || inp == 0) 2066509Ssam panic("setsockaddr_in"); 2076509Ssam bzero((caddr_t)sin, sizeof (*sin)); 2086509Ssam sin->sin_family = AF_INET; 2096509Ssam sin->sin_port = inp->inp_lport; 2106509Ssam sin->sin_addr = inp->inp_laddr; 2116509Ssam } 2126509Ssam 2135161Swnj /* 214*6583Ssam * Pass an error to all internet connections 215*6583Ssam * associated with address sin. Call the 216*6583Ssam * protocol specific routine to clean up the 217*6583Ssam * mess afterwards. 218*6583Ssam */ 219*6583Ssam in_pcbnotify(head, dst, errno, abort) 220*6583Ssam struct inpcb *head; 221*6583Ssam register struct in_addr *dst; 222*6583Ssam int errno, (*abort)(); 223*6583Ssam { 224*6583Ssam register struct inpcb *inp, *oinp; 225*6583Ssam int s = splimp(); 226*6583Ssam 227*6583Ssam COUNT(INP_PCBNOTIFY); 228*6583Ssam for (inp = head->inp_next; inp != head;) { 229*6583Ssam if (inp->inp_faddr.s_addr != dst->s_addr) { 230*6583Ssam next: 231*6583Ssam inp = inp->inp_next; 232*6583Ssam continue; 233*6583Ssam } 234*6583Ssam if (inp->inp_socket == 0) 235*6583Ssam goto next; 236*6583Ssam inp->inp_socket->so_error = errno; 237*6583Ssam oinp = inp; 238*6583Ssam inp = inp->inp_next; 239*6583Ssam (*abort)(oinp); 240*6583Ssam } 241*6583Ssam splx(s); 242*6583Ssam } 243*6583Ssam 244*6583Ssam /* 2455994Swnj * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY 2465161Swnj */ 2474907Swnj struct inpcb * 2486028Sroot in_pcblookup(head, faddr, fport, laddr, lport, flags) 2494905Swnj struct inpcb *head; 2504951Swnj struct in_addr faddr, laddr; 2514905Swnj u_short fport, lport; 2526028Sroot int flags; 2534905Swnj { 2545994Swnj register struct inpcb *inp, *match = 0; 2555994Swnj int matchwild = 3, wildcard; 2564905Swnj 2575161Swnj for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 2585994Swnj if (inp->inp_lport != lport) 2595161Swnj continue; 2605994Swnj wildcard = 0; 2615994Swnj if (inp->inp_laddr.s_addr != 0) { 2626116Swnj if (laddr.s_addr == 0) 2636116Swnj wildcard++; 2646116Swnj else if (inp->inp_laddr.s_addr != laddr.s_addr) 2655994Swnj continue; 2665994Swnj } else { 2675994Swnj if (laddr.s_addr != 0) 2685994Swnj wildcard++; 2695994Swnj } 2705994Swnj if (inp->inp_faddr.s_addr != 0) { 2716116Swnj if (faddr.s_addr == 0) 2726116Swnj wildcard++; 2736116Swnj else if (inp->inp_faddr.s_addr != faddr.s_addr || 2746028Sroot inp->inp_fport != fport) 2755994Swnj continue; 2765994Swnj } else { 2775994Swnj if (faddr.s_addr != 0) 2785994Swnj wildcard++; 2795994Swnj } 2806028Sroot if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) 2815994Swnj continue; 2825994Swnj if (wildcard < matchwild) { 2835161Swnj match = inp; 2845994Swnj matchwild = wildcard; 2855994Swnj if (matchwild == 0) 2865994Swnj break; 2875161Swnj } 2885161Swnj } 2895161Swnj return (match); 2904905Swnj } 291