xref: /csrg-svn/sys/netinet/in_pcb.c (revision 5994)
1*5994Swnj /*	in_pcb.c	4.17	82/02/27	*/
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"
144905Swnj 
154951Swnj /*
165161Swnj  * Routines to manage internet protocol control blocks.
175161Swnj  *
185161Swnj  * At PRU_ATTACH time a protocol control block is allocated in
195161Swnj  * in_pcballoc() and inserted on a doubly-linked list of such blocks
205161Swnj  * for the protocol.  A port address is either requested (and verified
215161Swnj  * to not be in use) or assigned at this time.  We also allocate
225161Swnj  * space in the socket sockbuf structures here, although this is
235161Swnj  * not a clearly correct place to put this function.
245161Swnj  *
255161Swnj  * A connectionless protocol will have its protocol control block
265161Swnj  * removed at PRU_DETACH time, when the socket will be freed (freeing
275161Swnj  * the space reserved) and the block will be removed from the list of
285161Swnj  * blocks for its protocol.
295161Swnj  *
305161Swnj  * A connection-based protocol may be connected to a remote peer at
315161Swnj  * PRU_CONNECT time through the routine in_pcbconnect().  In the normal
325161Swnj  * case a PRU_DISCONNECT occurs causing a in_pcbdisconnect().
335161Swnj  * It is also possible that higher-level routines will opt out of the
345161Swnj  * relationship with the connection before the connection shut down
355161Swnj  * is complete.  This often occurs in protocols like TCP where we must
365161Swnj  * hold on to the protocol control block for a unreasonably long time
375161Swnj  * after the connection is used up to avoid races in later connection
385161Swnj  * establishment.  To handle this we allow higher-level routines to
395161Swnj  * disassociate themselves from the socket, marking it SS_USERGONE while
405161Swnj  * the disconnect is in progress.  We notice that this has happened
415161Swnj  * when the disconnect is complete, and perform the PRU_DETACH operation,
425161Swnj  * freeing the socket.
435172Swnj  *
445172Swnj  * TODO:
455172Swnj  *	use hashing
465161Swnj  */
475240Sroot struct	in_addr zeroin_addr;
485161Swnj 
495161Swnj /*
504951Swnj  * Allocate a protocol control block, space
514951Swnj  * for send and receive data, and local host information.
524951Swnj  * Return error.  If no error make socket point at pcb.
534951Swnj  */
545161Swnj in_pcbattach(so, head, sndcc, rcvcc, sin)
554951Swnj 	struct socket *so;
564951Swnj 	struct inpcb *head;
574951Swnj 	int sndcc, rcvcc;
584951Swnj 	struct sockaddr_in *sin;
594905Swnj {
604905Swnj 	struct mbuf *m;
615240Sroot 	register struct inpcb *inp;
62*5994Swnj 	u_short lport = 0;
634905Swnj 
645161Swnj COUNT(IN_PCBATTACH);
65*5994Swnj 	if (ifnet == 0)
66*5994Swnj 		return (EADDRNOTAVAIL);
674951Swnj 	if (sin) {
684951Swnj 		if (sin->sin_family != AF_INET)
694951Swnj 			return (EAFNOSUPPORT);
70*5994Swnj 		if (sin->sin_addr.s_addr &&
71*5994Swnj 		    if_ifwithaddr(sin->sin_addr.s_addr) == 0)
72*5994Swnj 			return (EADDRNOTAVAIL);
734951Swnj 		lport = sin->sin_port;
74*5994Swnj 		if (lport) {
75*5994Swnj 			u_short aport = lport;
76*5994Swnj #if vax
77*5994Swnj 			aport = htons(aport);
78*5994Swnj #endif
79*5994Swnj 			/* GROSS */
80*5994Swnj 			if (aport < IPPORT_RESERVED && u.u_uid != 0)
81*5994Swnj 				return (EPERM);
82*5994Swnj 			if (in_pcblookup(head,
83*5994Swnj 			    zeroin_addr, 0, sin->sin_addr, lport, 0))
84*5994Swnj 				return (EADDRINUSE);
85*5994Swnj 		}
864951Swnj 	}
875852Sroot 	m = m_getclr(M_DONTWAIT);
884951Swnj 	if (m == 0)
894983Swnj 		return (ENOBUFS);
904951Swnj 	if (sbreserve(&so->so_snd, sndcc) == 0)
914951Swnj 		goto bad;
924951Swnj 	if (sbreserve(&so->so_rcv, rcvcc) == 0)
934951Swnj 		goto bad2;
944951Swnj 	inp = mtod(m, struct inpcb *);
955172Swnj 	inp->inp_head = head;
96*5994Swnj 	if (sin)
97*5994Swnj 		inp->inp_laddr = sin->sin_addr;
985172Swnj 	if (lport == 0)
995172Swnj 		do {
100*5994Swnj 			if (head->inp_lport++ < IPPORT_RESERVED)
101*5994Swnj 				head->inp_lport = IPPORT_RESERVED;
1025172Swnj 			lport = htons(head->inp_lport);
103*5994Swnj 		} while (in_pcblookup(head,
104*5994Swnj 			    zeroin_addr, 0, inp->inp_laddr, lport, 0));
1055172Swnj 	inp->inp_lport = lport;
1064983Swnj 	inp->inp_socket = so;
1074983Swnj 	insque(inp, head);
1084951Swnj 	so->so_pcb = (caddr_t)inp;
109*5994Swnj 	in_setsockaddr(inp);
1104951Swnj 	return (0);
1114951Swnj bad2:
1124951Swnj 	sbrelease(&so->so_snd);
1134951Swnj bad:
1144967Swnj 	(void) m_free(m);
1154951Swnj 	return (ENOBUFS);
1164905Swnj }
1174905Swnj 
1185161Swnj in_pcbconnect(inp, sin)
1194951Swnj 	struct inpcb *inp;
1204951Swnj 	struct sockaddr_in *sin;
1214923Swnj {
122*5994Swnj 	struct ifnet *ifp;
1234923Swnj 
1245161Swnj COUNT(IN_PCBCONNECT);
1254951Swnj 	if (sin->sin_family != AF_INET)
1264951Swnj 		return (EAFNOSUPPORT);
1274951Swnj 	if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
1284951Swnj 		return (EADDRNOTAVAIL);
129*5994Swnj 	if (inp->inp_laddr.s_addr == 0) {
130*5994Swnj 		ifp = if_ifonnetof(sin->sin_addr.s_addr);
131*5994Swnj 		if (ifp == 0)
132*5994Swnj 			ifp = ifnet;
133*5994Swnj 		inp->inp_laddr = ifp->if_addr;
134*5994Swnj 	}
135*5994Swnj 	if (in_pcblookup(inp->inp_head,
136*5994Swnj 	    sin->sin_addr, sin->sin_port, inp->inp_laddr, inp->inp_lport, 0))
1375172Swnj 		return (EADDRINUSE);
1384951Swnj 	inp->inp_faddr = sin->sin_addr;
1394951Swnj 	inp->inp_fport = sin->sin_port;
1404923Swnj 	return (0);
1414923Swnj }
1424923Swnj 
143*5994Swnj in_setsockaddr(inp)
1445277Sroot 	struct inpcb *inp;
1455277Sroot {
146*5994Swnj 	register struct sockaddr_in *sin =
147*5994Swnj 	    (struct sockaddr_in *)&inp->inp_socket->so_addr;
1485277Sroot 
1495277Sroot 	sin->sin_family = AF_INET;
150*5994Swnj 	sin->sin_addr = inp->inp_laddr;
151*5994Swnj 	sin->sin_port = inp->inp_lport;
1525277Sroot }
1535277Sroot 
1545161Swnj in_pcbdisconnect(inp)
1554905Swnj 	struct inpcb *inp;
1564905Swnj {
1575161Swnj 
1585161Swnj COUNT(IN_PCBDISCONNECT);
1595161Swnj 	inp->inp_faddr.s_addr = 0;
1605161Swnj 	if (inp->inp_socket->so_state & SS_USERGONE)
1615161Swnj 		in_pcbdetach(inp);
1625161Swnj }
1635161Swnj 
1645161Swnj in_pcbdetach(inp)
1655161Swnj 	struct inpcb *inp;
1665161Swnj {
1674905Swnj 	struct socket *so = inp->inp_socket;
1684905Swnj 
1695009Swnj 	so->so_pcb = 0;
1705009Swnj 	sofree(so);
1714983Swnj 	remque(inp);
1724907Swnj 	(void) m_free(dtom(inp));
1734905Swnj }
1744905Swnj 
1755161Swnj /*
1765161Swnj  * Look for a control block to accept a segment.
1775161Swnj  * First choice is an exact address match.
178*5994Swnj  * Second choice is a match with either the foreign or the local
179*5994Swnj  * address specified.
180*5994Swnj  *
181*5994Swnj  * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY
1825161Swnj  */
1834907Swnj struct inpcb *
184*5994Swnj in_pcblookup(head, faddr, fport, laddr, lport, enter)
1854905Swnj 	struct inpcb *head;
1864951Swnj 	struct in_addr faddr, laddr;
1874905Swnj 	u_short fport, lport;
188*5994Swnj 	int enter;
1894905Swnj {
190*5994Swnj 	register struct inpcb *inp, *match = 0;
191*5994Swnj 	int matchwild = 3, wildcard;
1924905Swnj 
1935161Swnj 	for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
194*5994Swnj 		if (inp->inp_lport != lport)
1955161Swnj 			continue;
196*5994Swnj 		wildcard = 0;
197*5994Swnj 		if (inp->inp_laddr.s_addr != 0) {
198*5994Swnj 			if (inp->inp_laddr.s_addr != laddr.s_addr)
199*5994Swnj 				continue;
200*5994Swnj 		} else {
201*5994Swnj 			if (laddr.s_addr != 0)
202*5994Swnj 				wildcard++;
203*5994Swnj 		}
204*5994Swnj 		if (inp->inp_faddr.s_addr != 0) {
205*5994Swnj 			if (inp->inp_faddr.s_addr != faddr.s_addr)
206*5994Swnj 				continue;
207*5994Swnj 		} else {
208*5994Swnj 			if (faddr.s_addr != 0)
209*5994Swnj 				wildcard++;
210*5994Swnj 		}
211*5994Swnj 		if (enter == 0 && wildcard)
212*5994Swnj 			continue;
213*5994Swnj 		if (wildcard < matchwild) {
2145161Swnj 			match = inp;
215*5994Swnj 			matchwild = wildcard;
216*5994Swnj 			if (matchwild == 0)
217*5994Swnj 				break;
2185161Swnj 		}
2195161Swnj 	}
220*5994Swnj 	if (match && enter) {
221*5994Swnj 		match->inp_laddr = laddr;
222*5994Swnj 		in_setsockaddr(match);
223*5994Swnj 	}
2245161Swnj 	return (match);
2254905Swnj }
226