xref: /csrg-svn/sys/netinet/in_pcb.c (revision 9639)
1*9639Ssam /*	in_pcb.c	4.37	82/12/14	*/
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_pcballoc(so, head)
207506Sroot 	struct socket *so;
217506Sroot 	struct inpcb *head;
227506Sroot {
237506Sroot 	struct mbuf *m;
247506Sroot 	register struct inpcb *inp;
257506Sroot 
26*9639Ssam 	m = m_getclr(M_DONTWAIT, MT_PCB);
277506Sroot 	if (m == 0)
287506Sroot 		return (ENOBUFS);
297506Sroot 	inp = mtod(m, struct inpcb *);
307506Sroot 	inp->inp_head = head;
317506Sroot 	inp->inp_socket = so;
327506Sroot 	insque(inp, head);
337506Sroot 	so->so_pcb = (caddr_t)inp;
347506Sroot 	return (0);
357506Sroot }
367506Sroot 
378270Sroot in_pcbbind(inp, nam)
387506Sroot 	register struct inpcb *inp;
398270Sroot 	struct mbuf *nam;
407506Sroot {
417506Sroot 	register struct socket *so = inp->inp_socket;
427506Sroot 	register struct inpcb *head = inp->inp_head;
438270Sroot 	register struct sockaddr_in *sin;
447506Sroot 	u_short lport = 0;
457506Sroot 
467506Sroot 	if (ifnet == 0)
477506Sroot 		return (EADDRNOTAVAIL);
488270Sroot 	if (inp->inp_lport || inp->inp_laddr.s_addr)
498270Sroot 		return (EINVAL);
508270Sroot 	if (nam == 0)
518270Sroot 		goto noname;
528270Sroot 	sin = mtod(nam, struct sockaddr_in *);
538270Sroot 	if (nam->m_len != sizeof (*sin))
548270Sroot 		return (EINVAL);
558270Sroot 	if (sin->sin_addr.s_addr) {
568270Sroot 		int tport = sin->sin_port;
577506Sroot 
588270Sroot 		sin->sin_port = 0;		/* yech... */
598270Sroot 		if (if_ifwithaddr((struct sockaddr *)sin) == 0)
608270Sroot 			return (EADDRNOTAVAIL);
618270Sroot 		sin->sin_port = tport;
627506Sroot 	}
638270Sroot 	lport = sin->sin_port;
648270Sroot 	if (lport) {
658937Sroot 		u_short aport = htons(lport);
668270Sroot 		int wild = 0;
677506Sroot 
688270Sroot 		/* GROSS */
698270Sroot 		if (aport < IPPORT_RESERVED && u.u_uid != 0)
708270Sroot 			return (EACCES);
718270Sroot 		if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0)
728270Sroot 			wild = INPLOOKUP_WILDCARD;
738270Sroot 		if (in_pcblookup(head,
748270Sroot 		    zeroin_addr, 0, sin->sin_addr, lport, wild))
758270Sroot 			return (EADDRINUSE);
764951Swnj 	}
778270Sroot 	inp->inp_laddr = sin->sin_addr;
788270Sroot noname:
795172Swnj 	if (lport == 0)
805172Swnj 		do {
815994Swnj 			if (head->inp_lport++ < IPPORT_RESERVED)
825994Swnj 				head->inp_lport = IPPORT_RESERVED;
835172Swnj 			lport = htons(head->inp_lport);
845994Swnj 		} while (in_pcblookup(head,
855994Swnj 			    zeroin_addr, 0, inp->inp_laddr, lport, 0));
865172Swnj 	inp->inp_lport = lport;
874951Swnj 	return (0);
884905Swnj }
894905Swnj 
906116Swnj /*
916116Swnj  * Connect from a socket to a specified address.
926116Swnj  * Both address and port must be specified in argument sin.
936116Swnj  * If don't have a local address for this socket yet,
946116Swnj  * then pick one.
956116Swnj  */
968270Sroot in_pcbconnect(inp, nam)
974951Swnj 	struct inpcb *inp;
988270Sroot 	struct mbuf *nam;
994923Swnj {
1005994Swnj 	struct ifnet *ifp;
1016338Ssam 	struct sockaddr_in *ifaddr;
1028270Sroot 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
1034923Swnj 
1048270Sroot 	if (nam->m_len != sizeof (*sin))
1058270Sroot 		return (EINVAL);
1064951Swnj 	if (sin->sin_family != AF_INET)
1074951Swnj 		return (EAFNOSUPPORT);
1084951Swnj 	if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
1094951Swnj 		return (EADDRNOTAVAIL);
1105994Swnj 	if (inp->inp_laddr.s_addr == 0) {
1117166Ssam 		ifp = if_ifonnetof(in_netof(sin->sin_addr));
1126338Ssam 		if (ifp == 0) {
1136583Ssam 			/*
1146583Ssam 			 * We should select the interface based on
1156583Ssam 			 * the route to be used, but for udp this would
1166583Ssam 			 * result in two calls to rtalloc for each packet
1176583Ssam 			 * sent; hardly worthwhile...
1186583Ssam 			 */
1196338Ssam 			ifp = if_ifwithaf(AF_INET);
1206338Ssam 			if (ifp == 0)
1216583Ssam 				return (EADDRNOTAVAIL);
1226338Ssam 		}
1236338Ssam 		ifaddr = (struct sockaddr_in *)&ifp->if_addr;
1245994Swnj 	}
1255994Swnj 	if (in_pcblookup(inp->inp_head,
1266116Swnj 	    sin->sin_addr,
1276116Swnj 	    sin->sin_port,
1286338Ssam 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
1296116Swnj 	    inp->inp_lport,
1306116Swnj 	    0))
1315172Swnj 		return (EADDRINUSE);
1326509Ssam 	if (inp->inp_laddr.s_addr == 0)
1336338Ssam 		inp->inp_laddr = ifaddr->sin_addr;
1344951Swnj 	inp->inp_faddr = sin->sin_addr;
1354951Swnj 	inp->inp_fport = sin->sin_port;
1364923Swnj 	return (0);
1374923Swnj }
1384923Swnj 
1395161Swnj in_pcbdisconnect(inp)
1404905Swnj 	struct inpcb *inp;
1414905Swnj {
1425161Swnj 
1435161Swnj 	inp->inp_faddr.s_addr = 0;
1446028Sroot 	inp->inp_fport = 0;
1457506Sroot 	if (inp->inp_socket->so_state & SS_NOFDREF)
1465161Swnj 		in_pcbdetach(inp);
1475161Swnj }
1485161Swnj 
1495161Swnj in_pcbdetach(inp)
1505161Swnj 	struct inpcb *inp;
1515161Swnj {
1524905Swnj 	struct socket *so = inp->inp_socket;
1534905Swnj 
1545009Swnj 	so->so_pcb = 0;
1555009Swnj 	sofree(so);
1566350Ssam 	if (inp->inp_route.ro_rt)
1576367Ssam 		rtfree(inp->inp_route.ro_rt);
1584983Swnj 	remque(inp);
1594907Swnj 	(void) m_free(dtom(inp));
1604905Swnj }
1614905Swnj 
1628270Sroot in_setsockaddr(inp, nam)
1636509Ssam 	register struct inpcb *inp;
1648270Sroot 	struct mbuf *nam;
1656509Ssam {
1668270Sroot 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
1678270Sroot 
1688270Sroot 	nam->m_len = sizeof (*sin);
1698270Sroot 	sin = mtod(nam, struct sockaddr_in *);
1706509Ssam 	bzero((caddr_t)sin, sizeof (*sin));
1716509Ssam 	sin->sin_family = AF_INET;
1726509Ssam 	sin->sin_port = inp->inp_lport;
1736509Ssam 	sin->sin_addr = inp->inp_laddr;
1746509Ssam }
1756509Ssam 
1765161Swnj /*
1776583Ssam  * Pass an error to all internet connections
1786583Ssam  * associated with address sin.  Call the
1796583Ssam  * protocol specific routine to clean up the
1806583Ssam  * mess afterwards.
1816583Ssam  */
1826583Ssam in_pcbnotify(head, dst, errno, abort)
1836583Ssam 	struct inpcb *head;
1846583Ssam 	register struct in_addr *dst;
1856583Ssam 	int errno, (*abort)();
1866583Ssam {
1876583Ssam 	register struct inpcb *inp, *oinp;
1886583Ssam 	int s = splimp();
1896583Ssam 
1906583Ssam 	for (inp = head->inp_next; inp != head;) {
1916583Ssam 		if (inp->inp_faddr.s_addr != dst->s_addr) {
1926583Ssam 	next:
1936583Ssam 			inp = inp->inp_next;
1946583Ssam 			continue;
1956583Ssam 		}
1966583Ssam 		if (inp->inp_socket == 0)
1976583Ssam 			goto next;
1986583Ssam 		inp->inp_socket->so_error = errno;
1996583Ssam 		oinp = inp;
2006583Ssam 		inp = inp->inp_next;
2016583Ssam 		(*abort)(oinp);
2026583Ssam 	}
2036583Ssam 	splx(s);
2046583Ssam }
2056583Ssam 
2064907Swnj struct inpcb *
2076028Sroot in_pcblookup(head, faddr, fport, laddr, lport, flags)
2084905Swnj 	struct inpcb *head;
2094951Swnj 	struct in_addr faddr, laddr;
2104905Swnj 	u_short fport, lport;
2116028Sroot 	int flags;
2124905Swnj {
2135994Swnj 	register struct inpcb *inp, *match = 0;
2145994Swnj 	int matchwild = 3, wildcard;
2154905Swnj 
2165161Swnj 	for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
2175994Swnj 		if (inp->inp_lport != lport)
2185161Swnj 			continue;
2195994Swnj 		wildcard = 0;
2205994Swnj 		if (inp->inp_laddr.s_addr != 0) {
2216116Swnj 			if (laddr.s_addr == 0)
2226116Swnj 				wildcard++;
2236116Swnj 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
2245994Swnj 				continue;
2255994Swnj 		} else {
2265994Swnj 			if (laddr.s_addr != 0)
2275994Swnj 				wildcard++;
2285994Swnj 		}
2295994Swnj 		if (inp->inp_faddr.s_addr != 0) {
2306116Swnj 			if (faddr.s_addr == 0)
2316116Swnj 				wildcard++;
2326116Swnj 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
2336028Sroot 			    inp->inp_fport != fport)
2345994Swnj 				continue;
2355994Swnj 		} else {
2365994Swnj 			if (faddr.s_addr != 0)
2375994Swnj 				wildcard++;
2385994Swnj 		}
2396028Sroot 		if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
2405994Swnj 			continue;
2415994Swnj 		if (wildcard < matchwild) {
2425161Swnj 			match = inp;
2435994Swnj 			matchwild = wildcard;
2445994Swnj 			if (matchwild == 0)
2455994Swnj 				break;
2465161Swnj 		}
2475161Swnj 	}
2485161Swnj 	return (match);
2494905Swnj }
250