xref: /csrg-svn/sys/netinet/in_pcb.c (revision 8937)
1 /*	in_pcb.c	4.35	82/10/30	*/
2 
3 #include "../h/param.h"
4 #include "../h/systm.h"
5 #include "../h/dir.h"
6 #include "../h/user.h"
7 #include "../h/mbuf.h"
8 #include "../h/socket.h"
9 #include "../h/socketvar.h"
10 #include "../netinet/in.h"
11 #include "../netinet/in_systm.h"
12 #include "../net/if.h"
13 #include "../net/route.h"
14 #include "../netinet/in_pcb.h"
15 #include "../h/protosw.h"
16 
17 struct	in_addr zeroin_addr;
18 
19 in_pcbreserve(so, sndcc, rcvcc)
20 	struct socket *so;
21 	int sndcc, rcvcc;
22 {
23 
24 	if (sbreserve(&so->so_snd, sndcc) == 0)
25 		goto bad;
26 	if (sbreserve(&so->so_rcv, rcvcc) == 0)
27 		goto bad2;
28 	return (0);
29 bad2:
30 	sbrelease(&so->so_snd);
31 bad:
32 	return (ENOBUFS);
33 }
34 
35 in_pcballoc(so, head)
36 	struct socket *so;
37 	struct inpcb *head;
38 {
39 	struct mbuf *m;
40 	register struct inpcb *inp;
41 
42 	m = m_getclr(M_DONTWAIT);
43 	if (m == 0)
44 		return (ENOBUFS);
45 	inp = mtod(m, struct inpcb *);
46 	inp->inp_head = head;
47 	inp->inp_socket = so;
48 	insque(inp, head);
49 	so->so_pcb = (caddr_t)inp;
50 	return (0);
51 }
52 
53 in_pcbbind(inp, nam)
54 	register struct inpcb *inp;
55 	struct mbuf *nam;
56 {
57 	register struct socket *so = inp->inp_socket;
58 	register struct inpcb *head = inp->inp_head;
59 	register struct sockaddr_in *sin;
60 	u_short lport = 0;
61 
62 	if (ifnet == 0)
63 		return (EADDRNOTAVAIL);
64 	if (inp->inp_lport || inp->inp_laddr.s_addr)
65 		return (EINVAL);
66 	if (nam == 0)
67 		goto noname;
68 	sin = mtod(nam, struct sockaddr_in *);
69 	if (nam->m_len != sizeof (*sin))
70 		return (EINVAL);
71 	if (sin->sin_addr.s_addr) {
72 		int tport = sin->sin_port;
73 
74 		sin->sin_port = 0;		/* yech... */
75 		if (if_ifwithaddr((struct sockaddr *)sin) == 0)
76 			return (EADDRNOTAVAIL);
77 		sin->sin_port = tport;
78 	}
79 	lport = sin->sin_port;
80 	if (lport) {
81 		u_short aport = htons(lport);
82 		int wild = 0;
83 
84 		/* GROSS */
85 		if (aport < IPPORT_RESERVED && u.u_uid != 0)
86 			return (EACCES);
87 		if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0)
88 			wild = INPLOOKUP_WILDCARD;
89 		if (in_pcblookup(head,
90 		    zeroin_addr, 0, sin->sin_addr, lport, wild))
91 			return (EADDRINUSE);
92 	}
93 	inp->inp_laddr = sin->sin_addr;
94 noname:
95 	if (lport == 0)
96 		do {
97 			if (head->inp_lport++ < IPPORT_RESERVED)
98 				head->inp_lport = IPPORT_RESERVED;
99 			lport = htons(head->inp_lport);
100 		} while (in_pcblookup(head,
101 			    zeroin_addr, 0, inp->inp_laddr, lport, 0));
102 	inp->inp_lport = lport;
103 	return (0);
104 }
105 
106 /*
107  * Connect from a socket to a specified address.
108  * Both address and port must be specified in argument sin.
109  * If don't have a local address for this socket yet,
110  * then pick one.
111  */
112 in_pcbconnect(inp, nam)
113 	struct inpcb *inp;
114 	struct mbuf *nam;
115 {
116 	struct ifnet *ifp;
117 	struct sockaddr_in *ifaddr;
118 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
119 
120 	if (nam->m_len != sizeof (*sin))
121 		return (EINVAL);
122 	if (sin->sin_family != AF_INET)
123 		return (EAFNOSUPPORT);
124 	if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
125 		return (EADDRNOTAVAIL);
126 	if (inp->inp_laddr.s_addr == 0) {
127 		ifp = if_ifonnetof(in_netof(sin->sin_addr));
128 		if (ifp == 0) {
129 			/*
130 			 * We should select the interface based on
131 			 * the route to be used, but for udp this would
132 			 * result in two calls to rtalloc for each packet
133 			 * sent; hardly worthwhile...
134 			 */
135 			ifp = if_ifwithaf(AF_INET);
136 			if (ifp == 0)
137 				return (EADDRNOTAVAIL);
138 		}
139 		ifaddr = (struct sockaddr_in *)&ifp->if_addr;
140 	}
141 	if (in_pcblookup(inp->inp_head,
142 	    sin->sin_addr,
143 	    sin->sin_port,
144 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
145 	    inp->inp_lport,
146 	    0))
147 		return (EADDRINUSE);
148 	if (inp->inp_laddr.s_addr == 0)
149 		inp->inp_laddr = ifaddr->sin_addr;
150 	inp->inp_faddr = sin->sin_addr;
151 	inp->inp_fport = sin->sin_port;
152 	return (0);
153 }
154 
155 in_pcbdisconnect(inp)
156 	struct inpcb *inp;
157 {
158 
159 	inp->inp_faddr.s_addr = 0;
160 	inp->inp_fport = 0;
161 	if (inp->inp_socket->so_state & SS_NOFDREF)
162 		in_pcbdetach(inp);
163 }
164 
165 in_pcbdetach(inp)
166 	struct inpcb *inp;
167 {
168 	struct socket *so = inp->inp_socket;
169 
170 	so->so_pcb = 0;
171 	sofree(so);
172 	if (inp->inp_route.ro_rt)
173 		rtfree(inp->inp_route.ro_rt);
174 	remque(inp);
175 	(void) m_free(dtom(inp));
176 }
177 
178 in_setsockaddr(inp, nam)
179 	register struct inpcb *inp;
180 	struct mbuf *nam;
181 {
182 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
183 
184 	nam->m_len = sizeof (*sin);
185 	sin = mtod(nam, struct sockaddr_in *);
186 	bzero((caddr_t)sin, sizeof (*sin));
187 	sin->sin_family = AF_INET;
188 	sin->sin_port = inp->inp_lport;
189 	sin->sin_addr = inp->inp_laddr;
190 }
191 
192 /*
193  * Pass an error to all internet connections
194  * associated with address sin.  Call the
195  * protocol specific routine to clean up the
196  * mess afterwards.
197  */
198 in_pcbnotify(head, dst, errno, abort)
199 	struct inpcb *head;
200 	register struct in_addr *dst;
201 	int errno, (*abort)();
202 {
203 	register struct inpcb *inp, *oinp;
204 	int s = splimp();
205 
206 	for (inp = head->inp_next; inp != head;) {
207 		if (inp->inp_faddr.s_addr != dst->s_addr) {
208 	next:
209 			inp = inp->inp_next;
210 			continue;
211 		}
212 		if (inp->inp_socket == 0)
213 			goto next;
214 		inp->inp_socket->so_error = errno;
215 		oinp = inp;
216 		inp = inp->inp_next;
217 		(*abort)(oinp);
218 	}
219 	splx(s);
220 }
221 
222 struct inpcb *
223 in_pcblookup(head, faddr, fport, laddr, lport, flags)
224 	struct inpcb *head;
225 	struct in_addr faddr, laddr;
226 	u_short fport, lport;
227 	int flags;
228 {
229 	register struct inpcb *inp, *match = 0;
230 	int matchwild = 3, wildcard;
231 
232 	for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
233 		if (inp->inp_lport != lport)
234 			continue;
235 		wildcard = 0;
236 		if (inp->inp_laddr.s_addr != 0) {
237 			if (laddr.s_addr == 0)
238 				wildcard++;
239 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
240 				continue;
241 		} else {
242 			if (laddr.s_addr != 0)
243 				wildcard++;
244 		}
245 		if (inp->inp_faddr.s_addr != 0) {
246 			if (faddr.s_addr == 0)
247 				wildcard++;
248 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
249 			    inp->inp_fport != fport)
250 				continue;
251 		} else {
252 			if (faddr.s_addr != 0)
253 				wildcard++;
254 		}
255 		if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
256 			continue;
257 		if (wildcard < matchwild) {
258 			match = inp;
259 			matchwild = wildcard;
260 			if (matchwild == 0)
261 				break;
262 		}
263 	}
264 	return (match);
265 }
266