xref: /csrg-svn/sys/netinet/in_pcb.c (revision 6583)
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