xref: /csrg-svn/sys/netinet/in_pcb.c (revision 8937)
1*8937Sroot /*	in_pcb.c	4.35	82/10/30	*/
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"
108397Swnj #include "../netinet/in.h"
118397Swnj #include "../netinet/in_systm.h"
124951Swnj #include "../net/if.h"
136350Ssam #include "../net/route.h"
148397Swnj #include "../netinet/in_pcb.h"
156116Swnj #include "../h/protosw.h"
164905Swnj 
175240Sroot struct	in_addr zeroin_addr;
185161Swnj 
197506Sroot in_pcbreserve(so, sndcc, rcvcc)
207506Sroot 	struct socket *so;
217506Sroot 	int sndcc, rcvcc;
227506Sroot {
237506Sroot 
247506Sroot 	if (sbreserve(&so->so_snd, sndcc) == 0)
257506Sroot 		goto bad;
267506Sroot 	if (sbreserve(&so->so_rcv, rcvcc) == 0)
277506Sroot 		goto bad2;
287506Sroot 	return (0);
297506Sroot bad2:
307506Sroot 	sbrelease(&so->so_snd);
317506Sroot bad:
327506Sroot 	return (ENOBUFS);
337506Sroot }
347506Sroot 
357506Sroot in_pcballoc(so, head)
367506Sroot 	struct socket *so;
377506Sroot 	struct inpcb *head;
387506Sroot {
397506Sroot 	struct mbuf *m;
407506Sroot 	register struct inpcb *inp;
417506Sroot 
427506Sroot 	m = m_getclr(M_DONTWAIT);
437506Sroot 	if (m == 0)
447506Sroot 		return (ENOBUFS);
457506Sroot 	inp = mtod(m, struct inpcb *);
467506Sroot 	inp->inp_head = head;
477506Sroot 	inp->inp_socket = so;
487506Sroot 	insque(inp, head);
497506Sroot 	so->so_pcb = (caddr_t)inp;
507506Sroot 	return (0);
517506Sroot }
527506Sroot 
538270Sroot in_pcbbind(inp, nam)
547506Sroot 	register struct inpcb *inp;
558270Sroot 	struct mbuf *nam;
567506Sroot {
577506Sroot 	register struct socket *so = inp->inp_socket;
587506Sroot 	register struct inpcb *head = inp->inp_head;
598270Sroot 	register struct sockaddr_in *sin;
607506Sroot 	u_short lport = 0;
617506Sroot 
627506Sroot 	if (ifnet == 0)
637506Sroot 		return (EADDRNOTAVAIL);
648270Sroot 	if (inp->inp_lport || inp->inp_laddr.s_addr)
658270Sroot 		return (EINVAL);
668270Sroot 	if (nam == 0)
678270Sroot 		goto noname;
688270Sroot 	sin = mtod(nam, struct sockaddr_in *);
698270Sroot 	if (nam->m_len != sizeof (*sin))
708270Sroot 		return (EINVAL);
718270Sroot 	if (sin->sin_addr.s_addr) {
728270Sroot 		int tport = sin->sin_port;
737506Sroot 
748270Sroot 		sin->sin_port = 0;		/* yech... */
758270Sroot 		if (if_ifwithaddr((struct sockaddr *)sin) == 0)
768270Sroot 			return (EADDRNOTAVAIL);
778270Sroot 		sin->sin_port = tport;
787506Sroot 	}
798270Sroot 	lport = sin->sin_port;
808270Sroot 	if (lport) {
81*8937Sroot 		u_short aport = htons(lport);
828270Sroot 		int wild = 0;
837506Sroot 
848270Sroot 		/* GROSS */
858270Sroot 		if (aport < IPPORT_RESERVED && u.u_uid != 0)
868270Sroot 			return (EACCES);
878270Sroot 		if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0)
888270Sroot 			wild = INPLOOKUP_WILDCARD;
898270Sroot 		if (in_pcblookup(head,
908270Sroot 		    zeroin_addr, 0, sin->sin_addr, lport, wild))
918270Sroot 			return (EADDRINUSE);
924951Swnj 	}
938270Sroot 	inp->inp_laddr = sin->sin_addr;
948270Sroot noname:
955172Swnj 	if (lport == 0)
965172Swnj 		do {
975994Swnj 			if (head->inp_lport++ < IPPORT_RESERVED)
985994Swnj 				head->inp_lport = IPPORT_RESERVED;
995172Swnj 			lport = htons(head->inp_lport);
1005994Swnj 		} while (in_pcblookup(head,
1015994Swnj 			    zeroin_addr, 0, inp->inp_laddr, lport, 0));
1025172Swnj 	inp->inp_lport = lport;
1034951Swnj 	return (0);
1044905Swnj }
1054905Swnj 
1066116Swnj /*
1076116Swnj  * Connect from a socket to a specified address.
1086116Swnj  * Both address and port must be specified in argument sin.
1096116Swnj  * If don't have a local address for this socket yet,
1106116Swnj  * then pick one.
1116116Swnj  */
1128270Sroot in_pcbconnect(inp, nam)
1134951Swnj 	struct inpcb *inp;
1148270Sroot 	struct mbuf *nam;
1154923Swnj {
1165994Swnj 	struct ifnet *ifp;
1176338Ssam 	struct sockaddr_in *ifaddr;
1188270Sroot 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
1194923Swnj 
1208270Sroot 	if (nam->m_len != sizeof (*sin))
1218270Sroot 		return (EINVAL);
1224951Swnj 	if (sin->sin_family != AF_INET)
1234951Swnj 		return (EAFNOSUPPORT);
1244951Swnj 	if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
1254951Swnj 		return (EADDRNOTAVAIL);
1265994Swnj 	if (inp->inp_laddr.s_addr == 0) {
1277166Ssam 		ifp = if_ifonnetof(in_netof(sin->sin_addr));
1286338Ssam 		if (ifp == 0) {
1296583Ssam 			/*
1306583Ssam 			 * We should select the interface based on
1316583Ssam 			 * the route to be used, but for udp this would
1326583Ssam 			 * result in two calls to rtalloc for each packet
1336583Ssam 			 * sent; hardly worthwhile...
1346583Ssam 			 */
1356338Ssam 			ifp = if_ifwithaf(AF_INET);
1366338Ssam 			if (ifp == 0)
1376583Ssam 				return (EADDRNOTAVAIL);
1386338Ssam 		}
1396338Ssam 		ifaddr = (struct sockaddr_in *)&ifp->if_addr;
1405994Swnj 	}
1415994Swnj 	if (in_pcblookup(inp->inp_head,
1426116Swnj 	    sin->sin_addr,
1436116Swnj 	    sin->sin_port,
1446338Ssam 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
1456116Swnj 	    inp->inp_lport,
1466116Swnj 	    0))
1475172Swnj 		return (EADDRINUSE);
1486509Ssam 	if (inp->inp_laddr.s_addr == 0)
1496338Ssam 		inp->inp_laddr = ifaddr->sin_addr;
1504951Swnj 	inp->inp_faddr = sin->sin_addr;
1514951Swnj 	inp->inp_fport = sin->sin_port;
1524923Swnj 	return (0);
1534923Swnj }
1544923Swnj 
1555161Swnj in_pcbdisconnect(inp)
1564905Swnj 	struct inpcb *inp;
1574905Swnj {
1585161Swnj 
1595161Swnj 	inp->inp_faddr.s_addr = 0;
1606028Sroot 	inp->inp_fport = 0;
1617506Sroot 	if (inp->inp_socket->so_state & SS_NOFDREF)
1625161Swnj 		in_pcbdetach(inp);
1635161Swnj }
1645161Swnj 
1655161Swnj in_pcbdetach(inp)
1665161Swnj 	struct inpcb *inp;
1675161Swnj {
1684905Swnj 	struct socket *so = inp->inp_socket;
1694905Swnj 
1705009Swnj 	so->so_pcb = 0;
1715009Swnj 	sofree(so);
1726350Ssam 	if (inp->inp_route.ro_rt)
1736367Ssam 		rtfree(inp->inp_route.ro_rt);
1744983Swnj 	remque(inp);
1754907Swnj 	(void) m_free(dtom(inp));
1764905Swnj }
1774905Swnj 
1788270Sroot in_setsockaddr(inp, nam)
1796509Ssam 	register struct inpcb *inp;
1808270Sroot 	struct mbuf *nam;
1816509Ssam {
1828270Sroot 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
1838270Sroot 
1848270Sroot 	nam->m_len = sizeof (*sin);
1858270Sroot 	sin = mtod(nam, struct sockaddr_in *);
1866509Ssam 	bzero((caddr_t)sin, sizeof (*sin));
1876509Ssam 	sin->sin_family = AF_INET;
1886509Ssam 	sin->sin_port = inp->inp_lport;
1896509Ssam 	sin->sin_addr = inp->inp_laddr;
1906509Ssam }
1916509Ssam 
1925161Swnj /*
1936583Ssam  * Pass an error to all internet connections
1946583Ssam  * associated with address sin.  Call the
1956583Ssam  * protocol specific routine to clean up the
1966583Ssam  * mess afterwards.
1976583Ssam  */
1986583Ssam in_pcbnotify(head, dst, errno, abort)
1996583Ssam 	struct inpcb *head;
2006583Ssam 	register struct in_addr *dst;
2016583Ssam 	int errno, (*abort)();
2026583Ssam {
2036583Ssam 	register struct inpcb *inp, *oinp;
2046583Ssam 	int s = splimp();
2056583Ssam 
2066583Ssam 	for (inp = head->inp_next; inp != head;) {
2076583Ssam 		if (inp->inp_faddr.s_addr != dst->s_addr) {
2086583Ssam 	next:
2096583Ssam 			inp = inp->inp_next;
2106583Ssam 			continue;
2116583Ssam 		}
2126583Ssam 		if (inp->inp_socket == 0)
2136583Ssam 			goto next;
2146583Ssam 		inp->inp_socket->so_error = errno;
2156583Ssam 		oinp = inp;
2166583Ssam 		inp = inp->inp_next;
2176583Ssam 		(*abort)(oinp);
2186583Ssam 	}
2196583Ssam 	splx(s);
2206583Ssam }
2216583Ssam 
2224907Swnj struct inpcb *
2236028Sroot in_pcblookup(head, faddr, fport, laddr, lport, flags)
2244905Swnj 	struct inpcb *head;
2254951Swnj 	struct in_addr faddr, laddr;
2264905Swnj 	u_short fport, lport;
2276028Sroot 	int flags;
2284905Swnj {
2295994Swnj 	register struct inpcb *inp, *match = 0;
2305994Swnj 	int matchwild = 3, wildcard;
2314905Swnj 
2325161Swnj 	for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
2335994Swnj 		if (inp->inp_lport != lport)
2345161Swnj 			continue;
2355994Swnj 		wildcard = 0;
2365994Swnj 		if (inp->inp_laddr.s_addr != 0) {
2376116Swnj 			if (laddr.s_addr == 0)
2386116Swnj 				wildcard++;
2396116Swnj 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
2405994Swnj 				continue;
2415994Swnj 		} else {
2425994Swnj 			if (laddr.s_addr != 0)
2435994Swnj 				wildcard++;
2445994Swnj 		}
2455994Swnj 		if (inp->inp_faddr.s_addr != 0) {
2466116Swnj 			if (faddr.s_addr == 0)
2476116Swnj 				wildcard++;
2486116Swnj 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
2496028Sroot 			    inp->inp_fport != fport)
2505994Swnj 				continue;
2515994Swnj 		} else {
2525994Swnj 			if (faddr.s_addr != 0)
2535994Swnj 				wildcard++;
2545994Swnj 		}
2556028Sroot 		if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
2565994Swnj 			continue;
2575994Swnj 		if (wildcard < matchwild) {
2585161Swnj 			match = inp;
2595994Swnj 			matchwild = wildcard;
2605994Swnj 			if (matchwild == 0)
2615994Swnj 				break;
2625161Swnj 		}
2635161Swnj 	}
2645161Swnj 	return (match);
2654905Swnj }
266