1*23177Smckusick /* 2*23177Smckusick * Copyright (c) 1982 Regents of the University of California. 3*23177Smckusick * All rights reserved. The Berkeley software License Agreement 4*23177Smckusick * specifies the terms and conditions for redistribution. 5*23177Smckusick * 6*23177Smckusick * @(#)in_pcb.c 6.8 (Berkeley) 06/08/85 7*23177Smckusick */ 84905Swnj 917058Sbloom #include "param.h" 1017058Sbloom #include "systm.h" 1117058Sbloom #include "dir.h" 1217058Sbloom #include "user.h" 1317058Sbloom #include "mbuf.h" 1417058Sbloom #include "socket.h" 1517058Sbloom #include "socketvar.h" 1617058Sbloom #include "in.h" 1717058Sbloom #include "in_systm.h" 184951Swnj #include "../net/if.h" 196350Ssam #include "../net/route.h" 2017058Sbloom #include "in_pcb.h" 2118374Skarels #include "in_var.h" 2217058Sbloom #include "protosw.h" 234905Swnj 245240Sroot struct in_addr zeroin_addr; 255161Swnj 267506Sroot in_pcballoc(so, head) 277506Sroot struct socket *so; 287506Sroot struct inpcb *head; 297506Sroot { 307506Sroot struct mbuf *m; 317506Sroot register struct inpcb *inp; 327506Sroot 339639Ssam m = m_getclr(M_DONTWAIT, MT_PCB); 3410141Ssam if (m == NULL) 357506Sroot return (ENOBUFS); 367506Sroot inp = mtod(m, struct inpcb *); 377506Sroot inp->inp_head = head; 387506Sroot inp->inp_socket = so; 397506Sroot insque(inp, head); 407506Sroot so->so_pcb = (caddr_t)inp; 417506Sroot return (0); 427506Sroot } 437506Sroot 448270Sroot in_pcbbind(inp, nam) 457506Sroot register struct inpcb *inp; 468270Sroot struct mbuf *nam; 477506Sroot { 487506Sroot register struct socket *so = inp->inp_socket; 497506Sroot register struct inpcb *head = inp->inp_head; 508270Sroot register struct sockaddr_in *sin; 517506Sroot u_short lport = 0; 527506Sroot 5318374Skarels if (in_ifaddr == 0) 547506Sroot return (EADDRNOTAVAIL); 5510141Ssam if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) 568270Sroot return (EINVAL); 578270Sroot if (nam == 0) 588270Sroot goto noname; 598270Sroot sin = mtod(nam, struct sockaddr_in *); 608270Sroot if (nam->m_len != sizeof (*sin)) 618270Sroot return (EINVAL); 6210141Ssam if (sin->sin_addr.s_addr != INADDR_ANY) { 638270Sroot int tport = sin->sin_port; 647506Sroot 658270Sroot sin->sin_port = 0; /* yech... */ 6618374Skarels if (ifa_ifwithaddr((struct sockaddr *)sin) == 0) 678270Sroot return (EADDRNOTAVAIL); 688270Sroot sin->sin_port = tport; 697506Sroot } 708270Sroot lport = sin->sin_port; 718270Sroot if (lport) { 7217429Skarels u_short aport = ntohs(lport); 738270Sroot int wild = 0; 747506Sroot 758270Sroot /* GROSS */ 768270Sroot if (aport < IPPORT_RESERVED && u.u_uid != 0) 778270Sroot return (EACCES); 7810598Ssam /* even GROSSER, but this is the Internet */ 7910598Ssam if ((so->so_options & SO_REUSEADDR) == 0 && 8010598Ssam ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || 8110598Ssam (so->so_options & SO_ACCEPTCONN) == 0)) 828270Sroot wild = INPLOOKUP_WILDCARD; 838270Sroot if (in_pcblookup(head, 848270Sroot zeroin_addr, 0, sin->sin_addr, lport, wild)) 858270Sroot return (EADDRINUSE); 864951Swnj } 878270Sroot inp->inp_laddr = sin->sin_addr; 888270Sroot noname: 895172Swnj if (lport == 0) 905172Swnj do { 915994Swnj if (head->inp_lport++ < IPPORT_RESERVED) 925994Swnj head->inp_lport = IPPORT_RESERVED; 935172Swnj lport = htons(head->inp_lport); 945994Swnj } while (in_pcblookup(head, 955994Swnj zeroin_addr, 0, inp->inp_laddr, lport, 0)); 965172Swnj inp->inp_lport = lport; 974951Swnj return (0); 984905Swnj } 994905Swnj 1006116Swnj /* 1016116Swnj * Connect from a socket to a specified address. 1026116Swnj * Both address and port must be specified in argument sin. 1036116Swnj * If don't have a local address for this socket yet, 1046116Swnj * then pick one. 1056116Swnj */ 1068270Sroot in_pcbconnect(inp, nam) 1074951Swnj struct inpcb *inp; 1088270Sroot struct mbuf *nam; 1094923Swnj { 11018374Skarels struct in_ifaddr *ia; 1116338Ssam struct sockaddr_in *ifaddr; 1128270Sroot register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 1134923Swnj 1148270Sroot if (nam->m_len != sizeof (*sin)) 1158270Sroot return (EINVAL); 1164951Swnj if (sin->sin_family != AF_INET) 1174951Swnj return (EAFNOSUPPORT); 11818374Skarels if (sin->sin_port == 0) 1194951Swnj return (EADDRNOTAVAIL); 12018374Skarels if (in_ifaddr) { 12118655Skarels /* 12218655Skarels * If the destination address is INADDR_ANY, 12318655Skarels * use the primary local address. 12418655Skarels * If the supplied address is INADDR_BROADCAST, 12518655Skarels * and the primary interface supports broadcast, 12618655Skarels * choose the broadcast address for that interface. 12718655Skarels */ 12818374Skarels #define satosin(sa) ((struct sockaddr_in *)(sa)) 12918374Skarels if (sin->sin_addr.s_addr == INADDR_ANY) 13018655Skarels sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr; 13118655Skarels else if (sin->sin_addr.s_addr == INADDR_BROADCAST && 13218655Skarels (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST)) 13318655Skarels sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr; 13418374Skarels } 13510141Ssam if (inp->inp_laddr.s_addr == INADDR_ANY) { 13618655Skarels ia = (struct in_ifaddr *)ifa_ifwithnet((struct sockaddr *)sin); 13718374Skarels if (ia == (struct in_ifaddr *)0) { 13817271Skarels register struct route *ro; 13918374Skarels struct ifnet *ifp; 14017271Skarels 14117271Skarels /* 14217271Skarels * If route is known or can be allocated now, 14317271Skarels * our src addr is taken from the i/f, else punt. 1446583Ssam */ 14517271Skarels ro = &inp->inp_route; 14617271Skarels if (ro->ro_rt && 14717271Skarels satosin(&ro->ro_dst)->sin_addr.s_addr != 14817271Skarels sin->sin_addr.s_addr) { 14917271Skarels RTFREE(ro->ro_rt); 15017271Skarels ro->ro_rt = (struct rtentry *)0; 15117271Skarels } 15217271Skarels if ((ro->ro_rt == (struct rtentry *)0) || 15317271Skarels (ifp = ro->ro_rt->rt_ifp) == (struct ifnet *)0) { 15417271Skarels /* No route yet, so try to acquire one */ 15517271Skarels ro->ro_dst.sa_family = AF_INET; 15617271Skarels ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = 15717271Skarels sin->sin_addr; 15817271Skarels rtalloc(ro); 15917271Skarels if (ro->ro_rt == 0) 16018655Skarels ifp = (struct ifnet *)0; 16117271Skarels else 16218655Skarels ifp = ro->ro_rt->rt_ifp; 16318655Skarels } 16418655Skarels if (ifp) { 16518655Skarels for (ia = in_ifaddr; ia; ia = ia->ia_next) 16618374Skarels if (ia->ia_ifp == ifp) 16718655Skarels break; 16818655Skarels } else 16918655Skarels ia = (struct in_ifaddr *)0; 17018374Skarels if (ia == 0) 17118374Skarels ia = in_ifaddr; 17218374Skarels if (ia == 0) 1736583Ssam return (EADDRNOTAVAIL); 1746338Ssam } 17518374Skarels ifaddr = (struct sockaddr_in *)&ia->ia_addr; 1765994Swnj } 1775994Swnj if (in_pcblookup(inp->inp_head, 1786116Swnj sin->sin_addr, 1796116Swnj sin->sin_port, 1806338Ssam inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 1816116Swnj inp->inp_lport, 1826116Swnj 0)) 1835172Swnj return (EADDRINUSE); 18414146Ssam if (inp->inp_laddr.s_addr == INADDR_ANY) { 18514146Ssam if (inp->inp_lport == 0) 18614146Ssam in_pcbbind(inp, (struct mbuf *)0); 1876338Ssam inp->inp_laddr = ifaddr->sin_addr; 18814146Ssam } 1894951Swnj inp->inp_faddr = sin->sin_addr; 1904951Swnj inp->inp_fport = sin->sin_port; 1914923Swnj return (0); 1924923Swnj } 1934923Swnj 1945161Swnj in_pcbdisconnect(inp) 1954905Swnj struct inpcb *inp; 1964905Swnj { 1975161Swnj 19810141Ssam inp->inp_faddr.s_addr = INADDR_ANY; 1996028Sroot inp->inp_fport = 0; 2007506Sroot if (inp->inp_socket->so_state & SS_NOFDREF) 2015161Swnj in_pcbdetach(inp); 2025161Swnj } 2035161Swnj 2045161Swnj in_pcbdetach(inp) 2055161Swnj struct inpcb *inp; 2065161Swnj { 2074905Swnj struct socket *so = inp->inp_socket; 2084905Swnj 2095009Swnj so->so_pcb = 0; 2105009Swnj sofree(so); 2116350Ssam if (inp->inp_route.ro_rt) 2126367Ssam rtfree(inp->inp_route.ro_rt); 2134983Swnj remque(inp); 2144907Swnj (void) m_free(dtom(inp)); 2154905Swnj } 2164905Swnj 2178270Sroot in_setsockaddr(inp, nam) 2186509Ssam register struct inpcb *inp; 2198270Sroot struct mbuf *nam; 2206509Ssam { 2218270Sroot register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 2228270Sroot 2238270Sroot nam->m_len = sizeof (*sin); 2248270Sroot sin = mtod(nam, struct sockaddr_in *); 2256509Ssam bzero((caddr_t)sin, sizeof (*sin)); 2266509Ssam sin->sin_family = AF_INET; 2276509Ssam sin->sin_port = inp->inp_lport; 2286509Ssam sin->sin_addr = inp->inp_laddr; 2296509Ssam } 2306509Ssam 23114123Ssam in_setpeeraddr(inp, nam) 23214123Ssam register struct inpcb *inp; 23314123Ssam struct mbuf *nam; 23414123Ssam { 23514123Ssam register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 23614123Ssam 23714123Ssam nam->m_len = sizeof (*sin); 23814123Ssam sin = mtod(nam, struct sockaddr_in *); 23914123Ssam bzero((caddr_t)sin, sizeof (*sin)); 24014123Ssam sin->sin_family = AF_INET; 24114123Ssam sin->sin_port = inp->inp_fport; 24214123Ssam sin->sin_addr = inp->inp_faddr; 24314123Ssam } 24414123Ssam 2455161Swnj /* 24617357Skarels * Pass some notification to all connections of a protocol 24717357Skarels * associated with address dst. Call the 24817357Skarels * protocol specific routine to handle each connection. 2496583Ssam */ 25017357Skarels in_pcbnotify(head, dst, errno, notify) 2516583Ssam struct inpcb *head; 2526583Ssam register struct in_addr *dst; 25317357Skarels int errno, (*notify)(); 2546583Ssam { 2556583Ssam register struct inpcb *inp, *oinp; 2566583Ssam int s = splimp(); 2576583Ssam 2586583Ssam for (inp = head->inp_next; inp != head;) { 2596583Ssam if (inp->inp_faddr.s_addr != dst->s_addr) { 2606583Ssam next: 2616583Ssam inp = inp->inp_next; 2626583Ssam continue; 2636583Ssam } 2646583Ssam if (inp->inp_socket == 0) 2656583Ssam goto next; 26617357Skarels if (errno) 26717357Skarels inp->inp_socket->so_error = errno; 2686583Ssam oinp = inp; 2696583Ssam inp = inp->inp_next; 27017357Skarels (*notify)(oinp); 2716583Ssam } 2726583Ssam splx(s); 2736583Ssam } 2746583Ssam 27517357Skarels /* 27617357Skarels * After a routing change, flush old routing 27717357Skarels * and allocate a (hopefully) better one. 27817357Skarels */ 27917357Skarels in_rtchange(inp) 28017357Skarels struct inpcb *inp; 28117357Skarels { 28217357Skarels if (inp->inp_route.ro_rt) { 28317357Skarels rtfree(inp->inp_route.ro_rt); 28417357Skarels inp->inp_route.ro_rt = 0; 28517357Skarels /* 28617357Skarels * A new route can be allocated the next time 28717357Skarels * output is attempted. 28817357Skarels */ 28917357Skarels } 29017357Skarels /* SHOULD NOTIFY HIGHER-LEVEL PROTOCOLS */ 29117357Skarels } 29217357Skarels 2934907Swnj struct inpcb * 2946028Sroot in_pcblookup(head, faddr, fport, laddr, lport, flags) 2954905Swnj struct inpcb *head; 2964951Swnj struct in_addr faddr, laddr; 2974905Swnj u_short fport, lport; 2986028Sroot int flags; 2994905Swnj { 3005994Swnj register struct inpcb *inp, *match = 0; 3015994Swnj int matchwild = 3, wildcard; 3024905Swnj 3035161Swnj for (inp = head->inp_next; inp != head; inp = inp->inp_next) { 3045994Swnj if (inp->inp_lport != lport) 3055161Swnj continue; 3065994Swnj wildcard = 0; 30710141Ssam if (inp->inp_laddr.s_addr != INADDR_ANY) { 30810141Ssam if (laddr.s_addr == INADDR_ANY) 3096116Swnj wildcard++; 3106116Swnj else if (inp->inp_laddr.s_addr != laddr.s_addr) 3115994Swnj continue; 3125994Swnj } else { 31310141Ssam if (laddr.s_addr != INADDR_ANY) 3145994Swnj wildcard++; 3155994Swnj } 31610141Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 31710141Ssam if (faddr.s_addr == INADDR_ANY) 3186116Swnj wildcard++; 3196116Swnj else if (inp->inp_faddr.s_addr != faddr.s_addr || 3206028Sroot inp->inp_fport != fport) 3215994Swnj continue; 3225994Swnj } else { 32310141Ssam if (faddr.s_addr != INADDR_ANY) 3245994Swnj wildcard++; 3255994Swnj } 3266028Sroot if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) 3275994Swnj continue; 3285994Swnj if (wildcard < matchwild) { 3295161Swnj match = inp; 3305994Swnj matchwild = wildcard; 3315994Swnj if (matchwild == 0) 3325994Swnj break; 3335161Swnj } 3345161Swnj } 3355161Swnj return (match); 3364905Swnj } 337