xref: /original-bsd/sys/netinet/in_pcb.c (revision 4d7221e2)
1 /*
2  * Copyright (c) 1982, 1986, 1991, 1993, 1995
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)in_pcb.c	8.4 (Berkeley) 05/24/95
8  */
9 
10 #include <sys/param.h>
11 #include <sys/systm.h>
12 #include <sys/malloc.h>
13 #include <sys/mbuf.h>
14 #include <sys/protosw.h>
15 #include <sys/socket.h>
16 #include <sys/socketvar.h>
17 #include <sys/ioctl.h>
18 #include <sys/errno.h>
19 #include <sys/time.h>
20 #include <sys/proc.h>
21 
22 #include <net/if.h>
23 #include <net/route.h>
24 
25 #include <netinet/in.h>
26 #include <netinet/in_systm.h>
27 #include <netinet/ip.h>
28 #include <netinet/in_pcb.h>
29 #include <netinet/in_var.h>
30 #include <netinet/ip_var.h>
31 
32 struct	in_addr zeroin_addr;
33 
34 int
in_pcballoc(so,head)35 in_pcballoc(so, head)
36 	struct socket *so;
37 	struct inpcb *head;
38 {
39 	register struct inpcb *inp;
40 
41 	MALLOC(inp, struct inpcb *, sizeof(*inp), M_PCB, M_NOWAIT);
42 	if (inp == NULL)
43 		return (ENOBUFS);
44 	bzero((caddr_t)inp, sizeof(*inp));
45 	inp->inp_head = head;
46 	inp->inp_socket = so;
47 	insque(inp, head);
48 	so->so_pcb = (caddr_t)inp;
49 	return (0);
50 }
51 
52 int
in_pcbbind(inp,nam)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 	struct proc *p = curproc;		/* XXX */
61 	u_short lport = 0;
62 	int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
63 	int error;
64 
65 	if (in_ifaddr == 0)
66 		return (EADDRNOTAVAIL);
67 	if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
68 		return (EINVAL);
69 	if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
70 	    ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
71 	     (so->so_options & SO_ACCEPTCONN) == 0))
72 		wild = INPLOOKUP_WILDCARD;
73 	if (nam) {
74 		sin = mtod(nam, struct sockaddr_in *);
75 		if (nam->m_len != sizeof (*sin))
76 			return (EINVAL);
77 #ifdef notdef
78 		/*
79 		 * We should check the family, but old programs
80 		 * incorrectly fail to initialize it.
81 		 */
82 		if (sin->sin_family != AF_INET)
83 			return (EAFNOSUPPORT);
84 #endif
85 		lport = sin->sin_port;
86 		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
87 			/*
88 			 * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
89 			 * allow complete duplication of binding if
90 			 * SO_REUSEPORT is set, or if SO_REUSEADDR is set
91 			 * and a multicast address is bound on both
92 			 * new and duplicated sockets.
93 			 */
94 			if (so->so_options & SO_REUSEADDR)
95 				reuseport = SO_REUSEADDR|SO_REUSEPORT;
96 		} else if (sin->sin_addr.s_addr != INADDR_ANY) {
97 			sin->sin_port = 0;		/* yech... */
98 			if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
99 				return (EADDRNOTAVAIL);
100 		}
101 		if (lport) {
102 			struct inpcb *t;
103 
104 			/* GROSS */
105 			if (ntohs(lport) < IPPORT_RESERVED &&
106 			    (error = suser(p->p_ucred, &p->p_acflag)))
107 				return (EACCES);
108 			t = in_pcblookup(head, zeroin_addr, 0,
109 			    sin->sin_addr, lport, wild);
110 			if (t && (reuseport & t->inp_socket->so_options) == 0)
111 				return (EADDRINUSE);
112 		}
113 		inp->inp_laddr = sin->sin_addr;
114 	}
115 	if (lport == 0)
116 		do {
117 			if (head->inp_lport++ < IPPORT_RESERVED ||
118 			    head->inp_lport > IPPORT_USERRESERVED)
119 				head->inp_lport = IPPORT_RESERVED;
120 			lport = htons(head->inp_lport);
121 		} while (in_pcblookup(head,
122 			    zeroin_addr, 0, inp->inp_laddr, lport, wild));
123 	inp->inp_lport = lport;
124 	return (0);
125 }
126 
127 /*
128  * Connect from a socket to a specified address.
129  * Both address and port must be specified in argument sin.
130  * If don't have a local address for this socket yet,
131  * then pick one.
132  */
133 int
in_pcbconnect(inp,nam)134 in_pcbconnect(inp, nam)
135 	register struct inpcb *inp;
136 	struct mbuf *nam;
137 {
138 	struct in_ifaddr *ia;
139 	struct sockaddr_in *ifaddr;
140 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
141 
142 	if (nam->m_len != sizeof (*sin))
143 		return (EINVAL);
144 	if (sin->sin_family != AF_INET)
145 		return (EAFNOSUPPORT);
146 	if (sin->sin_port == 0)
147 		return (EADDRNOTAVAIL);
148 	if (in_ifaddr) {
149 		/*
150 		 * If the destination address is INADDR_ANY,
151 		 * use the primary local address.
152 		 * If the supplied address is INADDR_BROADCAST,
153 		 * and the primary interface supports broadcast,
154 		 * choose the broadcast address for that interface.
155 		 */
156 #define	satosin(sa)	((struct sockaddr_in *)(sa))
157 #define sintosa(sin)	((struct sockaddr *)(sin))
158 #define ifatoia(ifa)	((struct in_ifaddr *)(ifa))
159 		if (sin->sin_addr.s_addr == INADDR_ANY)
160 		    sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
161 		else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
162 		  (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
163 		    sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
164 	}
165 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
166 		register struct route *ro;
167 
168 		ia = (struct in_ifaddr *)0;
169 		/*
170 		 * If route is known or can be allocated now,
171 		 * our src addr is taken from the i/f, else punt.
172 		 */
173 		ro = &inp->inp_route;
174 		if (ro->ro_rt &&
175 		    (satosin(&ro->ro_dst)->sin_addr.s_addr !=
176 			sin->sin_addr.s_addr ||
177 		    inp->inp_socket->so_options & SO_DONTROUTE)) {
178 			RTFREE(ro->ro_rt);
179 			ro->ro_rt = (struct rtentry *)0;
180 		}
181 		if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
182 		    (ro->ro_rt == (struct rtentry *)0 ||
183 		    ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
184 			/* No route yet, so try to acquire one */
185 			ro->ro_dst.sa_family = AF_INET;
186 			ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
187 			((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
188 				sin->sin_addr;
189 			rtalloc(ro);
190 		}
191 		/*
192 		 * If we found a route, use the address
193 		 * corresponding to the outgoing interface
194 		 * unless it is the loopback (in case a route
195 		 * to our address on another net goes to loopback).
196 		 */
197 		if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
198 			ia = ifatoia(ro->ro_rt->rt_ifa);
199 		if (ia == 0) {
200 			u_short fport = sin->sin_port;
201 
202 			sin->sin_port = 0;
203 			ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
204 			if (ia == 0)
205 				ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
206 			sin->sin_port = fport;
207 			if (ia == 0)
208 				ia = in_ifaddr;
209 			if (ia == 0)
210 				return (EADDRNOTAVAIL);
211 		}
212 		/*
213 		 * If the destination address is multicast and an outgoing
214 		 * interface has been set as a multicast option, use the
215 		 * address of that interface as our source address.
216 		 */
217 		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
218 		    inp->inp_moptions != NULL) {
219 			struct ip_moptions *imo;
220 			struct ifnet *ifp;
221 
222 			imo = inp->inp_moptions;
223 			if (imo->imo_multicast_ifp != NULL) {
224 				ifp = imo->imo_multicast_ifp;
225 				for (ia = in_ifaddr; ia; ia = ia->ia_next)
226 					if (ia->ia_ifp == ifp)
227 						break;
228 				if (ia == 0)
229 					return (EADDRNOTAVAIL);
230 			}
231 		}
232 		ifaddr = (struct sockaddr_in *)&ia->ia_addr;
233 	}
234 	if (in_pcblookup(inp->inp_head,
235 	    sin->sin_addr,
236 	    sin->sin_port,
237 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
238 	    inp->inp_lport,
239 	    0))
240 		return (EADDRINUSE);
241 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
242 		if (inp->inp_lport == 0)
243 			(void)in_pcbbind(inp, (struct mbuf *)0);
244 		inp->inp_laddr = ifaddr->sin_addr;
245 	}
246 	inp->inp_faddr = sin->sin_addr;
247 	inp->inp_fport = sin->sin_port;
248 	return (0);
249 }
250 
251 int
in_pcbdisconnect(inp)252 in_pcbdisconnect(inp)
253 	struct inpcb *inp;
254 {
255 
256 	inp->inp_faddr.s_addr = INADDR_ANY;
257 	inp->inp_fport = 0;
258 	if (inp->inp_socket->so_state & SS_NOFDREF)
259 		in_pcbdetach(inp);
260 }
261 
262 int
in_pcbdetach(inp)263 in_pcbdetach(inp)
264 	struct inpcb *inp;
265 {
266 	struct socket *so = inp->inp_socket;
267 
268 	so->so_pcb = 0;
269 	sofree(so);
270 	if (inp->inp_options)
271 		(void)m_free(inp->inp_options);
272 	if (inp->inp_route.ro_rt)
273 		rtfree(inp->inp_route.ro_rt);
274 	ip_freemoptions(inp->inp_moptions);
275 	remque(inp);
276 	FREE(inp, M_PCB);
277 }
278 
279 int
in_setsockaddr(inp,nam)280 in_setsockaddr(inp, nam)
281 	register struct inpcb *inp;
282 	struct mbuf *nam;
283 {
284 	register struct sockaddr_in *sin;
285 
286 	nam->m_len = sizeof (*sin);
287 	sin = mtod(nam, struct sockaddr_in *);
288 	bzero((caddr_t)sin, sizeof (*sin));
289 	sin->sin_family = AF_INET;
290 	sin->sin_len = sizeof(*sin);
291 	sin->sin_port = inp->inp_lport;
292 	sin->sin_addr = inp->inp_laddr;
293 }
294 
295 int
in_setpeeraddr(inp,nam)296 in_setpeeraddr(inp, nam)
297 	struct inpcb *inp;
298 	struct mbuf *nam;
299 {
300 	register struct sockaddr_in *sin;
301 
302 	nam->m_len = sizeof (*sin);
303 	sin = mtod(nam, struct sockaddr_in *);
304 	bzero((caddr_t)sin, sizeof (*sin));
305 	sin->sin_family = AF_INET;
306 	sin->sin_len = sizeof(*sin);
307 	sin->sin_port = inp->inp_fport;
308 	sin->sin_addr = inp->inp_faddr;
309 }
310 
311 /*
312  * Pass some notification to all connections of a protocol
313  * associated with address dst.  The local address and/or port numbers
314  * may be specified to limit the search.  The "usual action" will be
315  * taken, depending on the ctlinput cmd.  The caller must filter any
316  * cmds that are uninteresting (e.g., no error in the map).
317  * Call the protocol specific routine (if any) to report
318  * any errors for each matching socket.
319  *
320  * Must be called at splnet.
321  */
322 int
in_pcbnotify(head,dst,fport_arg,laddr,lport_arg,cmd,notify)323 in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify)
324 	struct inpcb *head;
325 	struct sockaddr *dst;
326 	u_int fport_arg, lport_arg;
327 	struct in_addr laddr;
328 	int cmd;
329 	void (*notify) __P((struct inpcb *, int));
330 {
331 	extern u_char inetctlerrmap[];
332 	register struct inpcb *inp, *oinp;
333 	struct in_addr faddr;
334 	u_short fport = fport_arg, lport = lport_arg;
335 	int errno;
336 
337 	if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
338 		return;
339 	faddr = ((struct sockaddr_in *)dst)->sin_addr;
340 	if (faddr.s_addr == INADDR_ANY)
341 		return;
342 
343 	/*
344 	 * Redirects go to all references to the destination,
345 	 * and use in_rtchange to invalidate the route cache.
346 	 * Dead host indications: notify all references to the destination.
347 	 * Otherwise, if we have knowledge of the local port and address,
348 	 * deliver only to that socket.
349 	 */
350 	if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
351 		fport = 0;
352 		lport = 0;
353 		laddr.s_addr = 0;
354 		if (cmd != PRC_HOSTDEAD)
355 			notify = in_rtchange;
356 	}
357 	errno = inetctlerrmap[cmd];
358 	for (inp = head->inp_next; inp != head;) {
359 		if (inp->inp_faddr.s_addr != faddr.s_addr ||
360 		    inp->inp_socket == 0 ||
361 		    (lport && inp->inp_lport != lport) ||
362 		    (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
363 		    (fport && inp->inp_fport != fport)) {
364 			inp = inp->inp_next;
365 			continue;
366 		}
367 		oinp = inp;
368 		inp = inp->inp_next;
369 		if (notify)
370 			(*notify)(oinp, errno);
371 	}
372 }
373 
374 /*
375  * Check for alternatives when higher level complains
376  * about service problems.  For now, invalidate cached
377  * routing information.  If the route was created dynamically
378  * (by a redirect), time to try a default gateway again.
379  */
380 int
in_losing(inp)381 in_losing(inp)
382 	struct inpcb *inp;
383 {
384 	register struct rtentry *rt;
385 	struct rt_addrinfo info;
386 
387 	if ((rt = inp->inp_route.ro_rt)) {
388 		inp->inp_route.ro_rt = 0;
389 		bzero((caddr_t)&info, sizeof(info));
390 		info.rti_info[RTAX_DST] =
391 			(struct sockaddr *)&inp->inp_route.ro_dst;
392 		info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
393 		info.rti_info[RTAX_NETMASK] = rt_mask(rt);
394 		rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
395 		if (rt->rt_flags & RTF_DYNAMIC)
396 			(void) rtrequest(RTM_DELETE, rt_key(rt),
397 				rt->rt_gateway, rt_mask(rt), rt->rt_flags,
398 				(struct rtentry **)0);
399 		else
400 		/*
401 		 * A new route can be allocated
402 		 * the next time output is attempted.
403 		 */
404 			rtfree(rt);
405 	}
406 }
407 
408 /*
409  * After a routing change, flush old routing
410  * and allocate a (hopefully) better one.
411  */
412 void
in_rtchange(inp,errno)413 in_rtchange(inp, errno)
414 	register struct inpcb *inp;
415 	int errno;
416 {
417 	if (inp->inp_route.ro_rt) {
418 		rtfree(inp->inp_route.ro_rt);
419 		inp->inp_route.ro_rt = 0;
420 		/*
421 		 * A new route can be allocated the next time
422 		 * output is attempted.
423 		 */
424 	}
425 }
426 
427 struct inpcb *
in_pcblookup(head,faddr,fport_arg,laddr,lport_arg,flags)428 in_pcblookup(head, faddr, fport_arg, laddr, lport_arg, flags)
429 	struct inpcb *head;
430 	struct in_addr faddr, laddr;
431 	u_int fport_arg, lport_arg;
432 	int flags;
433 {
434 	register struct inpcb *inp, *match = 0;
435 	int matchwild = 3, wildcard;
436 	u_short fport = fport_arg, lport = lport_arg;
437 
438 	for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
439 		if (inp->inp_lport != lport)
440 			continue;
441 		wildcard = 0;
442 		if (inp->inp_laddr.s_addr != INADDR_ANY) {
443 			if (laddr.s_addr == INADDR_ANY)
444 				wildcard++;
445 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
446 				continue;
447 		} else {
448 			if (laddr.s_addr != INADDR_ANY)
449 				wildcard++;
450 		}
451 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
452 			if (faddr.s_addr == INADDR_ANY)
453 				wildcard++;
454 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
455 			    inp->inp_fport != fport)
456 				continue;
457 		} else {
458 			if (faddr.s_addr != INADDR_ANY)
459 				wildcard++;
460 		}
461 		if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
462 			continue;
463 		if (wildcard < matchwild) {
464 			match = inp;
465 			matchwild = wildcard;
466 			if (matchwild == 0)
467 				break;
468 		}
469 	}
470 	return (match);
471 }
472