xref: /original-bsd/sys/netinet/in_pcb.c (revision 7e5a9c77)
1 /*
2  * Copyright (c) 1982 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  *
6  *	@(#)in_pcb.c	6.14 (Berkeley) 02/23/86
7  */
8 
9 #include "param.h"
10 #include "systm.h"
11 #include "dir.h"
12 #include "user.h"
13 #include "mbuf.h"
14 #include "socket.h"
15 #include "socketvar.h"
16 #include "ioctl.h"
17 #include "in.h"
18 #include "in_systm.h"
19 #include "../net/if.h"
20 #include "../net/route.h"
21 #include "in_pcb.h"
22 #include "in_var.h"
23 #include "protosw.h"
24 
25 struct	in_addr zeroin_addr;
26 
27 in_pcballoc(so, head)
28 	struct socket *so;
29 	struct inpcb *head;
30 {
31 	struct mbuf *m;
32 	register struct inpcb *inp;
33 
34 	m = m_getclr(M_DONTWAIT, MT_PCB);
35 	if (m == NULL)
36 		return (ENOBUFS);
37 	inp = mtod(m, struct inpcb *);
38 	inp->inp_head = head;
39 	inp->inp_socket = so;
40 	insque(inp, head);
41 	so->so_pcb = (caddr_t)inp;
42 	return (0);
43 }
44 
45 in_pcbbind(inp, nam)
46 	register struct inpcb *inp;
47 	struct mbuf *nam;
48 {
49 	register struct socket *so = inp->inp_socket;
50 	register struct inpcb *head = inp->inp_head;
51 	register struct sockaddr_in *sin;
52 	u_short lport = 0;
53 
54 	if (in_ifaddr == 0)
55 		return (EADDRNOTAVAIL);
56 	if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
57 		return (EINVAL);
58 	if (nam == 0)
59 		goto noname;
60 	sin = mtod(nam, struct sockaddr_in *);
61 	if (nam->m_len != sizeof (*sin))
62 		return (EINVAL);
63 	if (sin->sin_addr.s_addr != INADDR_ANY) {
64 		int tport = sin->sin_port;
65 
66 		sin->sin_port = 0;		/* yech... */
67 		if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
68 			return (EADDRNOTAVAIL);
69 		sin->sin_port = tport;
70 	}
71 	lport = sin->sin_port;
72 	if (lport) {
73 		u_short aport = ntohs(lport);
74 		int wild = 0;
75 
76 		/* GROSS */
77 		if (aport < IPPORT_RESERVED && u.u_uid != 0)
78 			return (EACCES);
79 		/* even GROSSER, but this is the Internet */
80 		if ((so->so_options & SO_REUSEADDR) == 0 &&
81 		    ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
82 		     (so->so_options & SO_ACCEPTCONN) == 0))
83 			wild = INPLOOKUP_WILDCARD;
84 		if (in_pcblookup(head,
85 		    zeroin_addr, 0, sin->sin_addr, lport, wild))
86 			return (EADDRINUSE);
87 	}
88 	inp->inp_laddr = sin->sin_addr;
89 noname:
90 	if (lport == 0)
91 		do {
92 			if (head->inp_lport++ < IPPORT_RESERVED ||
93 			    head->inp_lport > IPPORT_USERRESERVED)
94 				head->inp_lport = IPPORT_RESERVED;
95 			lport = htons(head->inp_lport);
96 		} while (in_pcblookup(head,
97 			    zeroin_addr, 0, inp->inp_laddr, lport, 0));
98 	inp->inp_lport = lport;
99 	return (0);
100 }
101 
102 /*
103  * Connect from a socket to a specified address.
104  * Both address and port must be specified in argument sin.
105  * If don't have a local address for this socket yet,
106  * then pick one.
107  */
108 in_pcbconnect(inp, nam)
109 	struct inpcb *inp;
110 	struct mbuf *nam;
111 {
112 	struct in_ifaddr *ia;
113 	struct sockaddr_in *ifaddr;
114 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
115 
116 	if (nam->m_len != sizeof (*sin))
117 		return (EINVAL);
118 	if (sin->sin_family != AF_INET)
119 		return (EAFNOSUPPORT);
120 	if (sin->sin_port == 0)
121 		return (EADDRNOTAVAIL);
122 	if (in_ifaddr) {
123 		/*
124 		 * If the destination address is INADDR_ANY,
125 		 * use the primary local address.
126 		 * If the supplied address is INADDR_BROADCAST,
127 		 * and the primary interface supports broadcast,
128 		 * choose the broadcast address for that interface.
129 		 */
130 #define	satosin(sa)	((struct sockaddr_in *)(sa))
131 		if (sin->sin_addr.s_addr == INADDR_ANY)
132 		    sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
133 		else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
134 		  (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
135 		    sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
136 	}
137 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
138 		ia = in_iaonnetof(in_netof(sin->sin_addr));
139 		if (ia == (struct in_ifaddr *)0 ||
140 		    (ia->ia_ifp->if_flags & IFF_UP) == 0) {
141 			register struct route *ro;
142 			struct ifnet *ifp;
143 
144 			ia = (struct in_ifaddr *)0;
145 			/*
146 			 * If route is known or can be allocated now,
147 			 * our src addr is taken from the i/f, else punt.
148 			 */
149 			ro = &inp->inp_route;
150 			if (ro->ro_rt &&
151 			    satosin(&ro->ro_dst)->sin_addr.s_addr !=
152 			    sin->sin_addr.s_addr) {
153 				RTFREE(ro->ro_rt);
154 				ro->ro_rt = (struct rtentry *)0;
155 			}
156 			if ((ro->ro_rt == (struct rtentry *)0) ||
157 			    (ifp = ro->ro_rt->rt_ifp) == (struct ifnet *)0) {
158 				/* No route yet, so try to acquire one */
159 				ro->ro_dst.sa_family = AF_INET;
160 				((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
161 					sin->sin_addr;
162 				rtalloc(ro);
163 				if (ro->ro_rt == (struct rtentry *)0)
164 					ifp = (struct ifnet *)0;
165 				else
166 					ifp = ro->ro_rt->rt_ifp;
167 			}
168 			if (ifp) {
169 				for (ia = in_ifaddr; ia; ia = ia->ia_next)
170 					if (ia->ia_ifp == ifp)
171 						break;
172 			}
173 			if (ia == 0)
174 				ia = in_ifaddr;
175 			if (ia == 0)
176 				return (EADDRNOTAVAIL);
177 		}
178 		ifaddr = (struct sockaddr_in *)&ia->ia_addr;
179 	}
180 	if (in_pcblookup(inp->inp_head,
181 	    sin->sin_addr,
182 	    sin->sin_port,
183 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
184 	    inp->inp_lport,
185 	    0))
186 		return (EADDRINUSE);
187 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
188 		if (inp->inp_lport == 0)
189 			(void)in_pcbbind(inp, (struct mbuf *)0);
190 		inp->inp_laddr = ifaddr->sin_addr;
191 	}
192 	inp->inp_faddr = sin->sin_addr;
193 	inp->inp_fport = sin->sin_port;
194 	return (0);
195 }
196 
197 in_pcbdisconnect(inp)
198 	struct inpcb *inp;
199 {
200 
201 	inp->inp_faddr.s_addr = INADDR_ANY;
202 	inp->inp_fport = 0;
203 	if (inp->inp_socket->so_state & SS_NOFDREF)
204 		in_pcbdetach(inp);
205 }
206 
207 in_pcbdetach(inp)
208 	struct inpcb *inp;
209 {
210 	struct socket *so = inp->inp_socket;
211 
212 	so->so_pcb = 0;
213 	sofree(so);
214 	if (inp->inp_options)
215 		(void)m_free(inp->inp_options);
216 	if (inp->inp_route.ro_rt)
217 		rtfree(inp->inp_route.ro_rt);
218 	remque(inp);
219 	(void) m_free(dtom(inp));
220 }
221 
222 in_setsockaddr(inp, nam)
223 	register struct inpcb *inp;
224 	struct mbuf *nam;
225 {
226 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
227 
228 	nam->m_len = sizeof (*sin);
229 	sin = mtod(nam, struct sockaddr_in *);
230 	bzero((caddr_t)sin, sizeof (*sin));
231 	sin->sin_family = AF_INET;
232 	sin->sin_port = inp->inp_lport;
233 	sin->sin_addr = inp->inp_laddr;
234 }
235 
236 in_setpeeraddr(inp, nam)
237 	register struct inpcb *inp;
238 	struct mbuf *nam;
239 {
240 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
241 
242 	nam->m_len = sizeof (*sin);
243 	sin = mtod(nam, struct sockaddr_in *);
244 	bzero((caddr_t)sin, sizeof (*sin));
245 	sin->sin_family = AF_INET;
246 	sin->sin_port = inp->inp_fport;
247 	sin->sin_addr = inp->inp_faddr;
248 }
249 
250 /*
251  * Pass some notification to all connections of a protocol
252  * associated with address dst.  Call the protocol specific
253  * routine (if any) to handle each connection.
254  */
255 in_pcbnotify(head, dst, errno, notify)
256 	struct inpcb *head;
257 	register struct in_addr *dst;
258 	int errno, (*notify)();
259 {
260 	register struct inpcb *inp, *oinp;
261 	int s = splimp();
262 
263 	for (inp = head->inp_next; inp != head;) {
264 		if (inp->inp_faddr.s_addr != dst->s_addr ||
265 		    inp->inp_socket == 0) {
266 			inp = inp->inp_next;
267 			continue;
268 		}
269 		if (errno)
270 			inp->inp_socket->so_error = errno;
271 		oinp = inp;
272 		inp = inp->inp_next;
273 		if (notify)
274 			(*notify)(oinp);
275 	}
276 	splx(s);
277 }
278 
279 /*
280  * Check for alternatives when higher level complains
281  * about service problems.  For now, invalidate cached
282  * routing information.  If the route was created dynamically
283  * (by a redirect), time to try a default gateway again.
284  */
285 in_losing(inp)
286 	struct inpcb *inp;
287 {
288 	register struct rtentry *rt;
289 
290 	if ((rt = inp->inp_route.ro_rt)) {
291 		if (rt->rt_flags & RTF_DYNAMIC)
292 			(void) rtrequest(SIOCDELRT, rt);
293 		rtfree(rt);
294 		inp->inp_route.ro_rt = 0;
295 		/*
296 		 * A new route can be allocated
297 		 * the next time output is attempted.
298 		 */
299 	}
300 }
301 
302 /*
303  * After a routing change, flush old routing
304  * and allocate a (hopefully) better one.
305  */
306 in_rtchange(inp)
307 	register struct inpcb *inp;
308 {
309 	if (inp->inp_route.ro_rt) {
310 		rtfree(inp->inp_route.ro_rt);
311 		inp->inp_route.ro_rt = 0;
312 		/*
313 		 * A new route can be allocated the next time
314 		 * output is attempted.
315 		 */
316 	}
317 }
318 
319 struct inpcb *
320 in_pcblookup(head, faddr, fport, laddr, lport, flags)
321 	struct inpcb *head;
322 	struct in_addr faddr, laddr;
323 	u_short fport, lport;
324 	int flags;
325 {
326 	register struct inpcb *inp, *match = 0;
327 	int matchwild = 3, wildcard;
328 
329 	for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
330 		if (inp->inp_lport != lport)
331 			continue;
332 		wildcard = 0;
333 		if (inp->inp_laddr.s_addr != INADDR_ANY) {
334 			if (laddr.s_addr == INADDR_ANY)
335 				wildcard++;
336 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
337 				continue;
338 		} else {
339 			if (laddr.s_addr != INADDR_ANY)
340 				wildcard++;
341 		}
342 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
343 			if (faddr.s_addr == INADDR_ANY)
344 				wildcard++;
345 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
346 			    inp->inp_fport != fport)
347 				continue;
348 		} else {
349 			if (faddr.s_addr != INADDR_ANY)
350 				wildcard++;
351 		}
352 		if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
353 			continue;
354 		if (wildcard < matchwild) {
355 			match = inp;
356 			matchwild = wildcard;
357 			if (matchwild == 0)
358 				break;
359 		}
360 	}
361 	return (match);
362 }
363