xref: /csrg-svn/sys/netinet/in_pcb.c (revision 6116)
1*6116Swnj /*	in_pcb.c	4.19	82/03/11	*/
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"
14*6116Swnj #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 &&
725994Swnj 		    if_ifwithaddr(sin->sin_addr.s_addr) == 0)
735994Swnj 			return (EADDRNOTAVAIL);
744951Swnj 		lport = sin->sin_port;
755994Swnj 		if (lport) {
765994Swnj 			u_short aport = lport;
77*6116Swnj 			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);
84*6116Swnj 			if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
85*6116Swnj 			    (so->so_options & SO_ACCEPTCONN) == 0)
86*6116Swnj 				wild = INPLOOKUP_WILDCARD;
875994Swnj 			if (in_pcblookup(head,
88*6116Swnj 			    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 
123*6116Swnj /*
124*6116Swnj  * Connect from a socket to a specified address.
125*6116Swnj  * Both address and port must be specified in argument sin.
126*6116Swnj  * If don't have a local address for this socket yet,
127*6116Swnj  * then pick one.
128*6116Swnj  */
1295161Swnj in_pcbconnect(inp, sin)
1304951Swnj 	struct inpcb *inp;
1314951Swnj 	struct sockaddr_in *sin;
1324923Swnj {
1335994Swnj 	struct ifnet *ifp;
1344923Swnj 
1355161Swnj COUNT(IN_PCBCONNECT);
1364951Swnj 	if (sin->sin_family != AF_INET)
1374951Swnj 		return (EAFNOSUPPORT);
1384951Swnj 	if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
1394951Swnj 		return (EADDRNOTAVAIL);
1405994Swnj 	if (inp->inp_laddr.s_addr == 0) {
1415994Swnj 		ifp = if_ifonnetof(sin->sin_addr.s_addr);
1425994Swnj 		if (ifp == 0)
1435994Swnj 			ifp = ifnet;
1445994Swnj 	}
1455994Swnj 	if (in_pcblookup(inp->inp_head,
146*6116Swnj 	    sin->sin_addr,
147*6116Swnj 	    sin->sin_port,
148*6116Swnj 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifp->if_addr,
149*6116Swnj 	    inp->inp_lport,
150*6116Swnj 	    0))
1515172Swnj 		return (EADDRINUSE);
1526028Sroot 	if (inp->inp_laddr.s_addr == 0)
1536028Sroot 		inp->inp_laddr = ifp->if_addr;
1544951Swnj 	inp->inp_faddr = sin->sin_addr;
1554951Swnj 	inp->inp_fport = sin->sin_port;
1564923Swnj 	return (0);
1574923Swnj }
1584923Swnj 
1595994Swnj in_setsockaddr(inp)
1605277Sroot 	struct inpcb *inp;
1615277Sroot {
1625994Swnj 	register struct sockaddr_in *sin =
1635994Swnj 	    (struct sockaddr_in *)&inp->inp_socket->so_addr;
1645277Sroot 
1655277Sroot 	sin->sin_family = AF_INET;
1665994Swnj 	sin->sin_addr = inp->inp_laddr;
1675994Swnj 	sin->sin_port = inp->inp_lport;
1685277Sroot }
1695277Sroot 
1705161Swnj in_pcbdisconnect(inp)
1714905Swnj 	struct inpcb *inp;
1724905Swnj {
1735161Swnj 
1745161Swnj COUNT(IN_PCBDISCONNECT);
1755161Swnj 	inp->inp_faddr.s_addr = 0;
1766028Sroot 	inp->inp_fport = 0;
1775161Swnj 	if (inp->inp_socket->so_state & SS_USERGONE)
1785161Swnj 		in_pcbdetach(inp);
1795161Swnj }
1805161Swnj 
1815161Swnj in_pcbdetach(inp)
1825161Swnj 	struct inpcb *inp;
1835161Swnj {
1844905Swnj 	struct socket *so = inp->inp_socket;
1854905Swnj 
1865009Swnj 	so->so_pcb = 0;
1875009Swnj 	sofree(so);
1884983Swnj 	remque(inp);
1894907Swnj 	(void) m_free(dtom(inp));
1904905Swnj }
1914905Swnj 
1925161Swnj /*
193*6116Swnj  * Look for a control block to accept a segment, or to make
194*6116Swnj  * sure
1955161Swnj  * First choice is an exact address match.
1965994Swnj  * Second choice is a match with either the foreign or the local
1975994Swnj  * address specified.
1985994Swnj  *
1995994Swnj  * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY
2005161Swnj  */
2014907Swnj struct inpcb *
2026028Sroot in_pcblookup(head, faddr, fport, laddr, lport, flags)
2034905Swnj 	struct inpcb *head;
2044951Swnj 	struct in_addr faddr, laddr;
2054905Swnj 	u_short fport, lport;
2066028Sroot 	int flags;
2074905Swnj {
2085994Swnj 	register struct inpcb *inp, *match = 0;
2095994Swnj 	int matchwild = 3, wildcard;
2104905Swnj 
2115161Swnj 	for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
2125994Swnj 		if (inp->inp_lport != lport)
2135161Swnj 			continue;
2145994Swnj 		wildcard = 0;
2155994Swnj 		if (inp->inp_laddr.s_addr != 0) {
216*6116Swnj 			if (laddr.s_addr == 0)
217*6116Swnj 				wildcard++;
218*6116Swnj 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
2195994Swnj 				continue;
2205994Swnj 		} else {
2215994Swnj 			if (laddr.s_addr != 0)
2225994Swnj 				wildcard++;
2235994Swnj 		}
2245994Swnj 		if (inp->inp_faddr.s_addr != 0) {
225*6116Swnj 			if (faddr.s_addr == 0)
226*6116Swnj 				wildcard++;
227*6116Swnj 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
2286028Sroot 			    inp->inp_fport != fport)
2295994Swnj 				continue;
2305994Swnj 		} else {
2315994Swnj 			if (faddr.s_addr != 0)
2325994Swnj 				wildcard++;
2335994Swnj 		}
2346028Sroot 		if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
2355994Swnj 			continue;
2365994Swnj 		if (wildcard < matchwild) {
2375161Swnj 			match = inp;
2385994Swnj 			matchwild = wildcard;
2395994Swnj 			if (matchwild == 0)
2405994Swnj 				break;
2415161Swnj 		}
2425161Swnj 	}
2435161Swnj 	return (match);
2444905Swnj }
245