1*7506Sroot /* in_pcb.c 4.29 82/07/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 41*7506Sroot * disassociate themselves from the socket, marking it SS_NOFDREF 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 51*7506Sroot in_pcbreserve(so, sndcc, rcvcc) 52*7506Sroot struct socket *so; 53*7506Sroot int sndcc, rcvcc; 54*7506Sroot { 55*7506Sroot 56*7506Sroot if (sbreserve(&so->so_snd, sndcc) == 0) 57*7506Sroot goto bad; 58*7506Sroot if (sbreserve(&so->so_rcv, rcvcc) == 0) 59*7506Sroot goto bad2; 60*7506Sroot return (0); 61*7506Sroot bad2: 62*7506Sroot sbrelease(&so->so_snd); 63*7506Sroot bad: 64*7506Sroot return (ENOBUFS); 65*7506Sroot } 66*7506Sroot 67*7506Sroot in_pcballoc(so, head) 68*7506Sroot struct socket *so; 69*7506Sroot struct inpcb *head; 70*7506Sroot { 71*7506Sroot struct mbuf *m; 72*7506Sroot register struct inpcb *inp; 73*7506Sroot 74*7506Sroot m = m_getclr(M_DONTWAIT); 75*7506Sroot if (m == 0) 76*7506Sroot return (ENOBUFS); 77*7506Sroot inp = mtod(m, struct inpcb *); 78*7506Sroot inp->inp_head = head; 79*7506Sroot inp->inp_socket = so; 80*7506Sroot insque(inp, head); 81*7506Sroot so->so_pcb = (caddr_t)inp; 82*7506Sroot return (0); 83*7506Sroot } 84*7506Sroot 85*7506Sroot in_pcbbind(inp, sin) 86*7506Sroot register struct inpcb *inp; 87*7506Sroot struct sockaddr_in *sin; 88*7506Sroot { 89*7506Sroot register struct socket *so = inp->inp_socket; 90*7506Sroot register struct inpcb *head = inp->inp_head; 91*7506Sroot u_short lport = 0; 92*7506Sroot 93*7506Sroot if (ifnet == 0) 94*7506Sroot return (EADDRNOTAVAIL); 95*7506Sroot if (sin) { 96*7506Sroot if (sin->sin_family != AF_INET) 97*7506Sroot return (EAFNOSUPPORT); 98*7506Sroot if (sin->sin_addr.s_addr) { 99*7506Sroot int tport = sin->sin_port; 100*7506Sroot 101*7506Sroot sin->sin_port = 0; /* yech... */ 102*7506Sroot if (if_ifwithaddr((struct sockaddr *)sin) == 0) 103*7506Sroot return (EADDRNOTAVAIL); 104*7506Sroot sin->sin_port = tport; 105*7506Sroot } 106*7506Sroot lport = sin->sin_port; 107*7506Sroot if (lport) { 108*7506Sroot u_short aport = lport; 109*7506Sroot int wild = 0; 110*7506Sroot #if vax 111*7506Sroot aport = htons(aport); 112*7506Sroot #endif 113*7506Sroot /* GROSS */ 114*7506Sroot if (aport < IPPORT_RESERVED && u.u_uid != 0) 115*7506Sroot return (EACCES); 116*7506Sroot if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || 117*7506Sroot (so->so_options & SO_ACCEPTCONN) == 0) 118*7506Sroot wild = INPLOOKUP_WILDCARD; 119*7506Sroot if (in_pcblookup(head, 120*7506Sroot zeroin_addr, 0, sin->sin_addr, lport, wild)) 121*7506Sroot return (EADDRINUSE); 122*7506Sroot } 123*7506Sroot } 124*7506Sroot if (sin) 125*7506Sroot inp->inp_laddr = sin->sin_addr; 126*7506Sroot if (lport == 0) 127*7506Sroot do { 128*7506Sroot if (head->inp_lport++ < IPPORT_RESERVED) 129*7506Sroot head->inp_lport = IPPORT_RESERVED; 130*7506Sroot lport = htons(head->inp_lport); 131*7506Sroot } while (in_pcblookup(head, 132*7506Sroot zeroin_addr, 0, inp->inp_laddr, lport, 0)); 133*7506Sroot inp->inp_lport = lport; 134*7506Sroot return (0); 135*7506Sroot } 136*7506Sroot 137*7506Sroot /* BEGIN DEPRECATED */ 1385161Swnj /* 1394951Swnj * Allocate a protocol control block, space 1404951Swnj * for send and receive data, and local host information. 1414951Swnj * Return error. If no error make socket point at pcb. 1424951Swnj */ 1435161Swnj in_pcbattach(so, head, sndcc, rcvcc, sin) 1444951Swnj struct socket *so; 1454951Swnj struct inpcb *head; 1464951Swnj int sndcc, rcvcc; 1474951Swnj struct sockaddr_in *sin; 1484905Swnj { 1494905Swnj struct mbuf *m; 1505240Sroot register struct inpcb *inp; 1515994Swnj u_short lport = 0; 1524905Swnj 1535994Swnj if (ifnet == 0) 1545994Swnj return (EADDRNOTAVAIL); 1554951Swnj if (sin) { 1564951Swnj if (sin->sin_family != AF_INET) 1574951Swnj return (EAFNOSUPPORT); 1586350Ssam if (sin->sin_addr.s_addr) { 1596350Ssam int tport = sin->sin_port; 1606350Ssam 1616350Ssam sin->sin_port = 0; /* yech... */ 1626350Ssam if (if_ifwithaddr((struct sockaddr *)sin) == 0) 1636350Ssam return (EADDRNOTAVAIL); 1646350Ssam sin->sin_port = tport; 1656350Ssam } 1664951Swnj lport = sin->sin_port; 1675994Swnj if (lport) { 1685994Swnj u_short aport = lport; 1696116Swnj int wild = 0; 1705994Swnj #if vax 1715994Swnj aport = htons(aport); 1725994Swnj #endif 1735994Swnj /* GROSS */ 1745994Swnj if (aport < IPPORT_RESERVED && u.u_uid != 0) 1756583Ssam return (EACCES); 1766116Swnj if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || 1776116Swnj (so->so_options & SO_ACCEPTCONN) == 0) 1786116Swnj wild = INPLOOKUP_WILDCARD; 1795994Swnj if (in_pcblookup(head, 1806116Swnj zeroin_addr, 0, sin->sin_addr, lport, wild)) 1815994Swnj return (EADDRINUSE); 1825994Swnj } 1834951Swnj } 1845852Sroot m = m_getclr(M_DONTWAIT); 1854951Swnj if (m == 0) 1864983Swnj return (ENOBUFS); 1874951Swnj if (sbreserve(&so->so_snd, sndcc) == 0) 1884951Swnj goto bad; 1894951Swnj if (sbreserve(&so->so_rcv, rcvcc) == 0) 1904951Swnj goto bad2; 1914951Swnj inp = mtod(m, struct inpcb *); 1925172Swnj inp->inp_head = head; 1935994Swnj if (sin) 1945994Swnj inp->inp_laddr = sin->sin_addr; 1955172Swnj if (lport == 0) 1965172Swnj do { 1975994Swnj if (head->inp_lport++ < IPPORT_RESERVED) 1985994Swnj head->inp_lport = IPPORT_RESERVED; 1995172Swnj lport = htons(head->inp_lport); 2005994Swnj } while (in_pcblookup(head, 2015994Swnj zeroin_addr, 0, inp->inp_laddr, lport, 0)); 2025172Swnj inp->inp_lport = lport; 2034983Swnj inp->inp_socket = so; 2044983Swnj insque(inp, head); 2054951Swnj so->so_pcb = (caddr_t)inp; 2064951Swnj return (0); 2074951Swnj bad2: 2084951Swnj sbrelease(&so->so_snd); 2094951Swnj bad: 2104967Swnj (void) m_free(m); 2114951Swnj return (ENOBUFS); 2124905Swnj } 213*7506Sroot /* END DEPRECATED */ 2144905Swnj 2156116Swnj /* 2166116Swnj * Connect from a socket to a specified address. 2176116Swnj * Both address and port must be specified in argument sin. 2186116Swnj * If don't have a local address for this socket yet, 2196116Swnj * then pick one. 2206116Swnj */ 2215161Swnj in_pcbconnect(inp, sin) 2224951Swnj struct inpcb *inp; 2234951Swnj struct sockaddr_in *sin; 2244923Swnj { 2255994Swnj struct ifnet *ifp; 2266338Ssam struct sockaddr_in *ifaddr; 2274923Swnj 2284951Swnj if (sin->sin_family != AF_INET) 2294951Swnj return (EAFNOSUPPORT); 2304951Swnj if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0) 2314951Swnj return (EADDRNOTAVAIL); 2325994Swnj if (inp->inp_laddr.s_addr == 0) { 2337166Ssam ifp = if_ifonnetof(in_netof(sin->sin_addr)); 2346338Ssam if (ifp == 0) { 2356583Ssam /* 2366583Ssam * We should select the interface based on 2376583Ssam * the route to be used, but for udp this would 2386583Ssam * result in two calls to rtalloc for each packet 2396583Ssam * sent; hardly worthwhile... 2406583Ssam */ 2416338Ssam ifp = if_ifwithaf(AF_INET); 2426338Ssam if (ifp == 0) 2436583Ssam return (EADDRNOTAVAIL); 2446338Ssam } 2456338Ssam ifaddr = (struct sockaddr_in *)&ifp->if_addr; 2465994Swnj } 2475994Swnj if (in_pcblookup(inp->inp_head, 2486116Swnj sin->sin_addr, 2496116Swnj sin->sin_port, 2506338Ssam inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 2516116Swnj inp->inp_lport, 2526116Swnj 0)) 2535172Swnj return (EADDRINUSE); 2546509Ssam if (inp->inp_laddr.s_addr == 0) 2556338Ssam inp->inp_laddr = ifaddr->sin_addr; 2564951Swnj inp->inp_faddr = sin->sin_addr; 2574951Swnj inp->inp_fport = sin->sin_port; 2584923Swnj return (0); 2594923Swnj } 2604923Swnj 2615161Swnj in_pcbdisconnect(inp) 2624905Swnj struct inpcb *inp; 2634905Swnj { 2645161Swnj 2655161Swnj inp->inp_faddr.s_addr = 0; 2666028Sroot inp->inp_fport = 0; 267*7506Sroot if (inp->inp_socket->so_state & SS_NOFDREF) 2685161Swnj in_pcbdetach(inp); 2695161Swnj } 2705161Swnj 2715161Swnj in_pcbdetach(inp) 2725161Swnj struct inpcb *inp; 2735161Swnj { 2744905Swnj struct socket *so = inp->inp_socket; 2754905Swnj 2765009Swnj so->so_pcb = 0; 2775009Swnj sofree(so); 2786350Ssam if (inp->inp_route.ro_rt) 2796367Ssam rtfree(inp->inp_route.ro_rt); 2804983Swnj remque(inp); 2814907Swnj (void) m_free(dtom(inp)); 2824905Swnj } 2834905Swnj 2846509Ssam in_setsockaddr(sin, inp) 2856509Ssam register struct sockaddr_in *sin; 2866509Ssam register struct inpcb *inp; 2876509Ssam { 2886509Ssam if (sin == 0 || inp == 0) 2896509Ssam panic("setsockaddr_in"); 2906509Ssam bzero((caddr_t)sin, sizeof (*sin)); 2916509Ssam sin->sin_family = AF_INET; 2926509Ssam sin->sin_port = inp->inp_lport; 2936509Ssam sin->sin_addr = inp->inp_laddr; 2946509Ssam } 2956509Ssam 2965161Swnj /* 2976583Ssam * Pass an error to all internet connections 2986583Ssam * associated with address sin. Call the 2996583Ssam * protocol specific routine to clean up the 3006583Ssam * mess afterwards. 3016583Ssam */ 3026583Ssam in_pcbnotify(head, dst, errno, abort) 3036583Ssam struct inpcb *head; 3046583Ssam register struct in_addr *dst; 3056583Ssam int errno, (*abort)(); 3066583Ssam { 3076583Ssam register struct inpcb *inp, *oinp; 3086583Ssam int s = splimp(); 3096583Ssam 3106583Ssam for (inp = head->inp_next; inp != head;) { 3116583Ssam if (inp->inp_faddr.s_addr != dst->s_addr) { 3126583Ssam next: 3136583Ssam inp = inp->inp_next; 3146583Ssam continue; 3156583Ssam } 3166583Ssam if (inp->inp_socket == 0) 3176583Ssam goto next; 3186583Ssam inp->inp_socket->so_error = errno; 3196583Ssam oinp = inp; 3206583Ssam inp = inp->inp_next; 3216583Ssam (*abort)(oinp); 3226583Ssam } 3236583Ssam splx(s); 3246583Ssam } 3256583Ssam 3266583Ssam /* 3275994Swnj * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY 3285161Swnj */ 3294907Swnj struct inpcb * 3306028Sroot in_pcblookup(head, faddr, fport, laddr, lport, flags) 3314905Swnj struct inpcb *head; 3324951Swnj struct in_addr faddr, laddr; 3334905Swnj u_short fport, lport; 3346028Sroot int flags; 3354905Swnj { 3365994Swnj register struct inpcb *inp, *match = 0; 3375994Swnj int matchwild = 3, wildcard; 3384905Swnj 3395161Swnj for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 3405994Swnj if (inp->inp_lport != lport) 3415161Swnj continue; 3425994Swnj wildcard = 0; 3435994Swnj if (inp->inp_laddr.s_addr != 0) { 3446116Swnj if (laddr.s_addr == 0) 3456116Swnj wildcard++; 3466116Swnj else if (inp->inp_laddr.s_addr != laddr.s_addr) 3475994Swnj continue; 3485994Swnj } else { 3495994Swnj if (laddr.s_addr != 0) 3505994Swnj wildcard++; 3515994Swnj } 3525994Swnj if (inp->inp_faddr.s_addr != 0) { 3536116Swnj if (faddr.s_addr == 0) 3546116Swnj wildcard++; 3556116Swnj else if (inp->inp_faddr.s_addr != faddr.s_addr || 3566028Sroot inp->inp_fport != fport) 3575994Swnj continue; 3585994Swnj } else { 3595994Swnj if (faddr.s_addr != 0) 3605994Swnj wildcard++; 3615994Swnj } 3626028Sroot if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) 3635994Swnj continue; 3645994Swnj if (wildcard < matchwild) { 3655161Swnj match = inp; 3665994Swnj matchwild = wildcard; 3675994Swnj if (matchwild == 0) 3685994Swnj break; 3695161Swnj } 3705161Swnj } 3715161Swnj return (match); 3724905Swnj } 373