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