1 | /* |
---|
2 | * Copyright (c) 1982, 1986, 1991, 1993, 1995 |
---|
3 | * The Regents of the University of California. All rights reserved. |
---|
4 | * |
---|
5 | * Redistribution and use in source and binary forms, with or without |
---|
6 | * modification, are permitted provided that the following conditions |
---|
7 | * are met: |
---|
8 | * 1. Redistributions of source code must retain the above copyright |
---|
9 | * notice, this list of conditions and the following disclaimer. |
---|
10 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
11 | * notice, this list of conditions and the following disclaimer in the |
---|
12 | * documentation and/or other materials provided with the distribution. |
---|
13 | * 4. Neither the name of the University nor the names of its contributors |
---|
14 | * may be used to endorse or promote products derived from this software |
---|
15 | * without specific prior written permission. |
---|
16 | * |
---|
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
---|
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
---|
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
27 | * SUCH DAMAGE. |
---|
28 | * |
---|
29 | * @(#)in_pcb.c 8.4 (Berkeley) 5/24/95 |
---|
30 | * $FreeBSD: src/sys/netinet/in_pcb.c,v 1.158 2005/01/07 01:45:44 imp Exp $ |
---|
31 | */ |
---|
32 | |
---|
33 | /* |
---|
34 | * $Id$ |
---|
35 | */ |
---|
36 | |
---|
37 | #include <sys/param.h> |
---|
38 | #include <rtems/bsd/sys/queue.h> |
---|
39 | #include <sys/systm.h> |
---|
40 | #include <sys/malloc.h> |
---|
41 | #include <sys/mbuf.h> |
---|
42 | #include <sys/protosw.h> |
---|
43 | #include <sys/socket.h> |
---|
44 | #include <sys/socketvar.h> |
---|
45 | #include <sys/ioctl.h> |
---|
46 | #include <sys/errno.h> |
---|
47 | #include <sys/time.h> |
---|
48 | #include <sys/proc.h> |
---|
49 | #include <sys/kernel.h> |
---|
50 | #include <sys/sysctl.h> |
---|
51 | |
---|
52 | #include <net/if.h> |
---|
53 | #include <net/route.h> |
---|
54 | |
---|
55 | #include <netinet/in.h> |
---|
56 | #include <netinet/in_systm.h> |
---|
57 | #include <netinet/ip.h> |
---|
58 | #include <netinet/in_pcb.h> |
---|
59 | #include <netinet/in_var.h> |
---|
60 | #include <netinet/ip_var.h> |
---|
61 | |
---|
62 | struct in_addr zeroin_addr; |
---|
63 | |
---|
64 | static void in_pcbinshash(struct inpcb *); |
---|
65 | static void in_rtchange(struct inpcb *, int); |
---|
66 | |
---|
67 | /* |
---|
68 | * These configure the range of local port addresses assigned to |
---|
69 | * "unspecified" outgoing connections/packets/whatever. |
---|
70 | */ |
---|
71 | static int ipport_lowfirstauto = IPPORT_RESERVED - 1; /* 1023 */ |
---|
72 | static int ipport_lowlastauto = IPPORT_RESERVEDSTART; /* 600 */ |
---|
73 | static int ipport_firstauto = IPPORT_RESERVED; /* 1024 */ |
---|
74 | static int ipport_lastauto = IPPORT_USERRESERVED; /* 5000 */ |
---|
75 | static int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 40000 */ |
---|
76 | static int ipport_hilastauto = IPPORT_HILASTAUTO; /* 44999 */ |
---|
77 | |
---|
78 | #define RANGECHK(var, min, max) \ |
---|
79 | if ((var) < (min)) { (var) = (min); } \ |
---|
80 | else if ((var) > (max)) { (var) = (max); } |
---|
81 | |
---|
82 | static int |
---|
83 | sysctl_net_ipport_check(SYSCTL_HANDLER_ARGS) |
---|
84 | { |
---|
85 | int error = sysctl_handle_int(oidp, |
---|
86 | oidp->oid_arg1, oidp->oid_arg2, req); |
---|
87 | if (!error) { |
---|
88 | RANGECHK(ipport_lowfirstauto, 1, IPPORT_RESERVED - 1); |
---|
89 | RANGECHK(ipport_lowlastauto, 1, IPPORT_RESERVED - 1); |
---|
90 | RANGECHK(ipport_firstauto, IPPORT_RESERVED, USHRT_MAX); |
---|
91 | RANGECHK(ipport_lastauto, IPPORT_RESERVED, USHRT_MAX); |
---|
92 | RANGECHK(ipport_hifirstauto, IPPORT_RESERVED, USHRT_MAX); |
---|
93 | RANGECHK(ipport_hilastauto, IPPORT_RESERVED, USHRT_MAX); |
---|
94 | } |
---|
95 | return error; |
---|
96 | } |
---|
97 | |
---|
98 | #undef RANGECHK |
---|
99 | |
---|
100 | SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW, 0, "IP Ports"); |
---|
101 | |
---|
102 | SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst, CTLTYPE_INT|CTLFLAG_RW, |
---|
103 | &ipport_lowfirstauto, 0, &sysctl_net_ipport_check, "I", ""); |
---|
104 | SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowlast, CTLTYPE_INT|CTLFLAG_RW, |
---|
105 | &ipport_lowlastauto, 0, &sysctl_net_ipport_check, "I", ""); |
---|
106 | SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, first, CTLTYPE_INT|CTLFLAG_RW, |
---|
107 | &ipport_firstauto, 0, &sysctl_net_ipport_check, "I", ""); |
---|
108 | SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, last, CTLTYPE_INT|CTLFLAG_RW, |
---|
109 | &ipport_lastauto, 0, &sysctl_net_ipport_check, "I", ""); |
---|
110 | SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hifirst, CTLTYPE_INT|CTLFLAG_RW, |
---|
111 | &ipport_hifirstauto, 0, &sysctl_net_ipport_check, "I", ""); |
---|
112 | SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hilast, CTLTYPE_INT|CTLFLAG_RW, |
---|
113 | &ipport_hilastauto, 0, &sysctl_net_ipport_check, "I", ""); |
---|
114 | |
---|
115 | int |
---|
116 | in_pcballoc(struct socket *so, struct inpcbinfo *pcbinfo) |
---|
117 | { |
---|
118 | register struct inpcb *inp; |
---|
119 | int s; |
---|
120 | |
---|
121 | MALLOC(inp, struct inpcb *, sizeof(*inp), M_PCB, M_NOWAIT); |
---|
122 | if (inp == NULL) |
---|
123 | return (ENOBUFS); |
---|
124 | bzero((caddr_t)inp, sizeof(*inp)); |
---|
125 | inp->inp_gencnt = ++pcbinfo->ipi_gencnt; |
---|
126 | inp->inp_pcbinfo = pcbinfo; |
---|
127 | inp->inp_socket = so; |
---|
128 | s = splnet(); |
---|
129 | LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list); |
---|
130 | pcbinfo->ipi_count++; |
---|
131 | in_pcbinshash(inp); |
---|
132 | splx(s); |
---|
133 | so->so_pcb = (caddr_t)inp; |
---|
134 | return (0); |
---|
135 | } |
---|
136 | |
---|
137 | int |
---|
138 | in_pcbbind(struct inpcb *inp, struct mbuf *nam) |
---|
139 | { |
---|
140 | register struct socket *so = inp->inp_socket; |
---|
141 | unsigned short *lastport; |
---|
142 | struct sockaddr_in *sin; |
---|
143 | u_short lport = 0; |
---|
144 | int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); |
---|
145 | int error; |
---|
146 | |
---|
147 | if (in_ifaddr == 0) |
---|
148 | return (EADDRNOTAVAIL); |
---|
149 | if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) |
---|
150 | return (EINVAL); |
---|
151 | if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 && |
---|
152 | ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || |
---|
153 | (so->so_options & SO_ACCEPTCONN) == 0)) |
---|
154 | wild = 1; |
---|
155 | if (nam) { |
---|
156 | sin = mtod(nam, struct sockaddr_in *); |
---|
157 | if (nam->m_len != sizeof (*sin)) |
---|
158 | return (EINVAL); |
---|
159 | #ifdef notdef |
---|
160 | /* |
---|
161 | * We should check the family, but old programs |
---|
162 | * incorrectly fail to initialize it. |
---|
163 | */ |
---|
164 | if (sin->sin_family != AF_INET) |
---|
165 | return (EAFNOSUPPORT); |
---|
166 | #endif |
---|
167 | lport = sin->sin_port; |
---|
168 | if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { |
---|
169 | /* |
---|
170 | * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; |
---|
171 | * allow complete duplication of binding if |
---|
172 | * SO_REUSEPORT is set, or if SO_REUSEADDR is set |
---|
173 | * and a multicast address is bound on both |
---|
174 | * new and duplicated sockets. |
---|
175 | */ |
---|
176 | if (so->so_options & SO_REUSEADDR) |
---|
177 | reuseport = SO_REUSEADDR|SO_REUSEPORT; |
---|
178 | } else if (sin->sin_addr.s_addr != INADDR_ANY) { |
---|
179 | sin->sin_port = 0; /* yech... */ |
---|
180 | if (ifa_ifwithaddr((struct sockaddr *)sin) == 0) |
---|
181 | return (EADDRNOTAVAIL); |
---|
182 | } |
---|
183 | if (lport) { |
---|
184 | struct inpcb *t; |
---|
185 | |
---|
186 | /* GROSS */ |
---|
187 | if (ntohs(lport) < IPPORT_RESERVED && |
---|
188 | (error = suser(p->p_ucred, &p->p_acflag))) |
---|
189 | return (EACCES); |
---|
190 | if (so->so_uid) { |
---|
191 | t = in_pcblookup(inp->inp_pcbinfo, zeroin_addr, |
---|
192 | 0, sin->sin_addr, lport, |
---|
193 | INPLOOKUP_WILDCARD); |
---|
194 | if (t && (so->so_uid != t->inp_socket->so_uid)) |
---|
195 | return (EADDRINUSE); |
---|
196 | } |
---|
197 | t = in_pcblookup(inp->inp_pcbinfo, zeroin_addr, 0, |
---|
198 | sin->sin_addr, lport, wild); |
---|
199 | if (t && (reuseport & t->inp_socket->so_options) == 0) |
---|
200 | return (EADDRINUSE); |
---|
201 | } |
---|
202 | inp->inp_laddr = sin->sin_addr; |
---|
203 | } |
---|
204 | if (lport == 0) { |
---|
205 | ushort first, last; |
---|
206 | int count; |
---|
207 | |
---|
208 | inp->inp_flags |= INP_ANONPORT; |
---|
209 | |
---|
210 | if (inp->inp_flags & INP_HIGHPORT) { |
---|
211 | first = ipport_hifirstauto; /* sysctl */ |
---|
212 | last = ipport_hilastauto; |
---|
213 | lastport = &inp->inp_pcbinfo->lasthi; |
---|
214 | } else if (inp->inp_flags & INP_LOWPORT) { |
---|
215 | if ((error = suser(p->p_ucred, &p->p_acflag))) |
---|
216 | return (EACCES); |
---|
217 | first = ipport_lowfirstauto; /* 1023 */ |
---|
218 | last = ipport_lowlastauto; /* 600 */ |
---|
219 | lastport = &inp->inp_pcbinfo->lastlow; |
---|
220 | } else { |
---|
221 | first = ipport_firstauto; /* sysctl */ |
---|
222 | last = ipport_lastauto; |
---|
223 | lastport = &inp->inp_pcbinfo->lastport; |
---|
224 | } |
---|
225 | /* |
---|
226 | * Simple check to ensure all ports are not used up causing |
---|
227 | * a deadlock here. |
---|
228 | * |
---|
229 | * We split the two cases (up and down) so that the direction |
---|
230 | * is not being tested on each round of the loop. |
---|
231 | */ |
---|
232 | if (first > last) { |
---|
233 | /* |
---|
234 | * counting down |
---|
235 | */ |
---|
236 | count = first - last; |
---|
237 | |
---|
238 | do { |
---|
239 | if (count-- <= 0) /* completely used? */ |
---|
240 | return (EADDRNOTAVAIL); |
---|
241 | --*lastport; |
---|
242 | if (*lastport > first || *lastport < last) |
---|
243 | *lastport = first; |
---|
244 | lport = htons(*lastport); |
---|
245 | } while (in_pcblookup(inp->inp_pcbinfo, |
---|
246 | zeroin_addr, 0, inp->inp_laddr, lport, wild)); |
---|
247 | } else { |
---|
248 | /* |
---|
249 | * counting up |
---|
250 | */ |
---|
251 | count = last - first; |
---|
252 | |
---|
253 | do { |
---|
254 | if (count-- <= 0) /* completely used? */ |
---|
255 | return (EADDRNOTAVAIL); |
---|
256 | ++*lastport; |
---|
257 | if (*lastport < first || *lastport > last) |
---|
258 | *lastport = first; |
---|
259 | lport = htons(*lastport); |
---|
260 | } while (in_pcblookup(inp->inp_pcbinfo, |
---|
261 | zeroin_addr, 0, inp->inp_laddr, lport, wild)); |
---|
262 | } |
---|
263 | } |
---|
264 | inp->inp_lport = lport; |
---|
265 | in_pcbrehash(inp); |
---|
266 | return (0); |
---|
267 | } |
---|
268 | |
---|
269 | /* |
---|
270 | * Transform old in_pcbconnect() into an inner subroutine for new |
---|
271 | * in_pcbconnect(): Do some validity-checking on the remote |
---|
272 | * address (in mbuf 'nam') and then determine local host address |
---|
273 | * (i.e., which interface) to use to access that remote host. |
---|
274 | * |
---|
275 | * This preserves definition of in_pcbconnect(), while supporting a |
---|
276 | * slightly different version for T/TCP. (This is more than |
---|
277 | * a bit of a kludge, but cleaning up the internal interfaces would |
---|
278 | * have forced minor changes in every protocol). |
---|
279 | */ |
---|
280 | |
---|
281 | int |
---|
282 | in_pcbladdr(struct inpcb *inp, struct mbuf *nam, struct sockaddr_in **plocal_sin) |
---|
283 | { |
---|
284 | struct in_ifaddr *ia; |
---|
285 | register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); |
---|
286 | |
---|
287 | if (nam->m_len != sizeof (*sin)) |
---|
288 | return (EINVAL); |
---|
289 | if (sin->sin_family != AF_INET) |
---|
290 | return (EAFNOSUPPORT); |
---|
291 | if (sin->sin_port == 0) |
---|
292 | return (EADDRNOTAVAIL); |
---|
293 | if (in_ifaddr) { |
---|
294 | /* |
---|
295 | * If the destination address is INADDR_ANY, |
---|
296 | * use the primary local address. |
---|
297 | * If the supplied address is INADDR_BROADCAST, |
---|
298 | * and the primary interface supports broadcast, |
---|
299 | * choose the broadcast address for that interface. |
---|
300 | */ |
---|
301 | #define satosin(sa) ((struct sockaddr_in *)(sa)) |
---|
302 | #define sintosa(sin) ((struct sockaddr *)(sin)) |
---|
303 | #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) |
---|
304 | if (sin->sin_addr.s_addr == INADDR_ANY) |
---|
305 | sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr; |
---|
306 | else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST && |
---|
307 | (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST)) |
---|
308 | sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr; |
---|
309 | } |
---|
310 | if (inp->inp_laddr.s_addr == INADDR_ANY) { |
---|
311 | register struct route *ro; |
---|
312 | |
---|
313 | ia = (struct in_ifaddr *)0; |
---|
314 | /* |
---|
315 | * If route is known or can be allocated now, |
---|
316 | * our src addr is taken from the i/f, else punt. |
---|
317 | */ |
---|
318 | ro = &inp->inp_route; |
---|
319 | if (ro->ro_rt && |
---|
320 | (satosin(&ro->ro_dst)->sin_addr.s_addr != |
---|
321 | sin->sin_addr.s_addr || |
---|
322 | inp->inp_socket->so_options & SO_DONTROUTE)) { |
---|
323 | RTFREE(ro->ro_rt); |
---|
324 | ro->ro_rt = (struct rtentry *)0; |
---|
325 | } |
---|
326 | if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ |
---|
327 | (ro->ro_rt == (struct rtentry *)0 || |
---|
328 | ro->ro_rt->rt_ifp == (struct ifnet *)0)) { |
---|
329 | /* No route yet, so try to acquire one */ |
---|
330 | ro->ro_dst.sa_family = AF_INET; |
---|
331 | ro->ro_dst.sa_len = sizeof(struct sockaddr_in); |
---|
332 | ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = |
---|
333 | sin->sin_addr; |
---|
334 | rtalloc(ro); |
---|
335 | } |
---|
336 | /* |
---|
337 | * If we found a route, use the address |
---|
338 | * corresponding to the outgoing interface |
---|
339 | * unless it is the loopback (in case a route |
---|
340 | * to our address on another net goes to loopback). |
---|
341 | */ |
---|
342 | if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK)) |
---|
343 | ia = ifatoia(ro->ro_rt->rt_ifa); |
---|
344 | if (ia == 0) { |
---|
345 | u_short fport = sin->sin_port; |
---|
346 | |
---|
347 | sin->sin_port = 0; |
---|
348 | ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin))); |
---|
349 | if (ia == 0) |
---|
350 | ia = ifatoia(ifa_ifwithnet(sintosa(sin))); |
---|
351 | sin->sin_port = fport; |
---|
352 | if (ia == 0) |
---|
353 | ia = in_ifaddr; |
---|
354 | if (ia == 0) |
---|
355 | return (EADDRNOTAVAIL); |
---|
356 | } |
---|
357 | /* |
---|
358 | * If the destination address is multicast and an outgoing |
---|
359 | * interface has been set as a multicast option, use the |
---|
360 | * address of that interface as our source address. |
---|
361 | */ |
---|
362 | if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && |
---|
363 | inp->inp_moptions != NULL) { |
---|
364 | struct ip_moptions *imo; |
---|
365 | struct ifnet *ifp; |
---|
366 | |
---|
367 | imo = inp->inp_moptions; |
---|
368 | if (imo->imo_multicast_ifp != NULL) { |
---|
369 | ifp = imo->imo_multicast_ifp; |
---|
370 | for (ia = in_ifaddr; ia; ia = ia->ia_next) |
---|
371 | if (ia->ia_ifp == ifp) |
---|
372 | break; |
---|
373 | if (ia == 0) |
---|
374 | return (EADDRNOTAVAIL); |
---|
375 | } |
---|
376 | } |
---|
377 | /* |
---|
378 | * Don't do pcblookup call here; return interface in plocal_sin |
---|
379 | * and exit to caller, that will do the lookup. |
---|
380 | */ |
---|
381 | *plocal_sin = &ia->ia_addr; |
---|
382 | |
---|
383 | } |
---|
384 | return(0); |
---|
385 | } |
---|
386 | |
---|
387 | /* |
---|
388 | * Outer subroutine: |
---|
389 | * Connect from a socket to a specified address. |
---|
390 | * Both address and port must be specified in argument sin. |
---|
391 | * If don't have a local address for this socket yet, |
---|
392 | * then pick one. |
---|
393 | */ |
---|
394 | int |
---|
395 | in_pcbconnect(struct inpcb *inp, struct mbuf *nam) |
---|
396 | { |
---|
397 | struct sockaddr_in *ifaddr; |
---|
398 | register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); |
---|
399 | int error; |
---|
400 | |
---|
401 | /* |
---|
402 | * Call inner routine, to assign local interface address. |
---|
403 | */ |
---|
404 | if ((error = in_pcbladdr(inp, nam, &ifaddr))) |
---|
405 | return(error); |
---|
406 | |
---|
407 | if (in_pcblookuphash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port, |
---|
408 | inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, |
---|
409 | inp->inp_lport, 0) != NULL) |
---|
410 | return (EADDRINUSE); |
---|
411 | if (inp->inp_laddr.s_addr == INADDR_ANY) { |
---|
412 | if (inp->inp_lport == 0) |
---|
413 | (void)in_pcbbind(inp, (struct mbuf *)0); |
---|
414 | inp->inp_laddr = ifaddr->sin_addr; |
---|
415 | } |
---|
416 | inp->inp_faddr = sin->sin_addr; |
---|
417 | inp->inp_fport = sin->sin_port; |
---|
418 | in_pcbrehash(inp); |
---|
419 | return (0); |
---|
420 | } |
---|
421 | |
---|
422 | void |
---|
423 | in_pcbdisconnect(struct inpcb *inp) |
---|
424 | { |
---|
425 | |
---|
426 | inp->inp_faddr.s_addr = INADDR_ANY; |
---|
427 | inp->inp_fport = 0; |
---|
428 | in_pcbrehash(inp); |
---|
429 | if (inp->inp_socket->so_state & SS_NOFDREF) |
---|
430 | in_pcbdetach(inp); |
---|
431 | } |
---|
432 | |
---|
433 | void |
---|
434 | in_pcbdetach(struct inpcb *inp) |
---|
435 | { |
---|
436 | struct socket *so = inp->inp_socket; |
---|
437 | struct inpcbinfo *ipi = inp->inp_pcbinfo; |
---|
438 | int s; |
---|
439 | |
---|
440 | inp->inp_gencnt = ++ipi->ipi_gencnt; |
---|
441 | so->so_pcb = 0; |
---|
442 | sofree(so); |
---|
443 | if (inp->inp_options) |
---|
444 | (void)m_free(inp->inp_options); |
---|
445 | if (inp->inp_route.ro_rt) |
---|
446 | rtfree(inp->inp_route.ro_rt); |
---|
447 | ip_freemoptions(inp->inp_moptions); |
---|
448 | s = splnet(); |
---|
449 | LIST_REMOVE(inp, inp_hash); |
---|
450 | LIST_REMOVE(inp, inp_list); |
---|
451 | splx(s); |
---|
452 | FREE(inp, M_PCB); |
---|
453 | } |
---|
454 | |
---|
455 | void |
---|
456 | in_setsockaddr(struct inpcb *inp, struct mbuf *nam) |
---|
457 | { |
---|
458 | register struct sockaddr_in *sin; |
---|
459 | |
---|
460 | nam->m_len = sizeof (*sin); |
---|
461 | sin = mtod(nam, struct sockaddr_in *); |
---|
462 | bzero((caddr_t)sin, sizeof (*sin)); |
---|
463 | sin->sin_family = AF_INET; |
---|
464 | sin->sin_len = sizeof(*sin); |
---|
465 | sin->sin_port = inp->inp_lport; |
---|
466 | sin->sin_addr = inp->inp_laddr; |
---|
467 | } |
---|
468 | |
---|
469 | void |
---|
470 | in_setpeeraddr(struct inpcb *inp, struct mbuf *nam) |
---|
471 | { |
---|
472 | register struct sockaddr_in *sin; |
---|
473 | |
---|
474 | nam->m_len = sizeof (*sin); |
---|
475 | sin = mtod(nam, struct sockaddr_in *); |
---|
476 | bzero((caddr_t)sin, sizeof (*sin)); |
---|
477 | sin->sin_family = AF_INET; |
---|
478 | sin->sin_len = sizeof(*sin); |
---|
479 | sin->sin_port = inp->inp_fport; |
---|
480 | sin->sin_addr = inp->inp_faddr; |
---|
481 | } |
---|
482 | |
---|
483 | /* |
---|
484 | * Pass some notification to all connections of a protocol |
---|
485 | * associated with address dst. The local address and/or port numbers |
---|
486 | * may be specified to limit the search. The "usual action" will be |
---|
487 | * taken, depending on the ctlinput cmd. The caller must filter any |
---|
488 | * cmds that are uninteresting (e.g., no error in the map). |
---|
489 | * Call the protocol specific routine (if any) to report |
---|
490 | * any errors for each matching socket. |
---|
491 | * |
---|
492 | * Must be called at splnet. |
---|
493 | */ |
---|
494 | void |
---|
495 | in_pcbnotify(struct inpcbhead *head, struct sockaddr *dst, u_int fport_arg, |
---|
496 | struct in_addr laddr, u_int lport_arg, int cmd, |
---|
497 | void (*notify)(struct inpcb *, int)) |
---|
498 | { |
---|
499 | register struct inpcb *inp, *oinp; |
---|
500 | struct in_addr faddr; |
---|
501 | u_short fport = fport_arg, lport = lport_arg; |
---|
502 | int errnum, s; |
---|
503 | |
---|
504 | if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET) |
---|
505 | return; |
---|
506 | faddr = ((struct sockaddr_in *)dst)->sin_addr; |
---|
507 | if (faddr.s_addr == INADDR_ANY) |
---|
508 | return; |
---|
509 | |
---|
510 | /* |
---|
511 | * Redirects go to all references to the destination, |
---|
512 | * and use in_rtchange to invalidate the route cache. |
---|
513 | * Dead host indications: notify all references to the destination. |
---|
514 | * Otherwise, if we have knowledge of the local port and address, |
---|
515 | * deliver only to that socket. |
---|
516 | */ |
---|
517 | if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { |
---|
518 | fport = 0; |
---|
519 | lport = 0; |
---|
520 | laddr.s_addr = 0; |
---|
521 | if (cmd != PRC_HOSTDEAD) |
---|
522 | notify = in_rtchange; |
---|
523 | } |
---|
524 | errnum = inetctlerrmap[cmd]; |
---|
525 | s = splnet(); |
---|
526 | for (inp = head->lh_first; inp != NULL;) { |
---|
527 | if (inp->inp_faddr.s_addr != faddr.s_addr || |
---|
528 | inp->inp_socket == 0 || |
---|
529 | (lport && inp->inp_lport != lport) || |
---|
530 | (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) || |
---|
531 | (fport && inp->inp_fport != fport)) { |
---|
532 | inp = inp->inp_list.le_next; |
---|
533 | continue; |
---|
534 | } |
---|
535 | oinp = inp; |
---|
536 | inp = inp->inp_list.le_next; |
---|
537 | if (notify) |
---|
538 | (*notify)(oinp, errnum); |
---|
539 | } |
---|
540 | splx(s); |
---|
541 | } |
---|
542 | |
---|
543 | /* |
---|
544 | * Check for alternatives when higher level complains |
---|
545 | * about service problems. For now, invalidate cached |
---|
546 | * routing information. If the route was created dynamically |
---|
547 | * (by a redirect), time to try a default gateway again. |
---|
548 | */ |
---|
549 | void |
---|
550 | in_losing(struct inpcb *inp) |
---|
551 | { |
---|
552 | register struct rtentry *rt; |
---|
553 | struct rt_addrinfo info; |
---|
554 | |
---|
555 | if ((rt = inp->inp_route.ro_rt)) { |
---|
556 | inp->inp_route.ro_rt = 0; |
---|
557 | bzero((caddr_t)&info, sizeof(info)); |
---|
558 | info.rti_info[RTAX_DST] = |
---|
559 | (struct sockaddr *)&inp->inp_route.ro_dst; |
---|
560 | info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; |
---|
561 | info.rti_info[RTAX_NETMASK] = rt_mask(rt); |
---|
562 | rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0); |
---|
563 | if (rt->rt_flags & RTF_DYNAMIC) |
---|
564 | (void) rtrequest(RTM_DELETE, rt_key(rt), |
---|
565 | rt->rt_gateway, rt_mask(rt), rt->rt_flags, |
---|
566 | (struct rtentry **)0); |
---|
567 | else |
---|
568 | /* |
---|
569 | * A new route can be allocated |
---|
570 | * the next time output is attempted. |
---|
571 | */ |
---|
572 | rtfree(rt); |
---|
573 | } |
---|
574 | } |
---|
575 | |
---|
576 | /* |
---|
577 | * After a routing change, flush old routing |
---|
578 | * and allocate a (hopefully) better one. |
---|
579 | */ |
---|
580 | static void |
---|
581 | in_rtchange(struct inpcb *inp, int errnum) |
---|
582 | { |
---|
583 | if (inp->inp_route.ro_rt) { |
---|
584 | rtfree(inp->inp_route.ro_rt); |
---|
585 | inp->inp_route.ro_rt = 0; |
---|
586 | /* |
---|
587 | * A new route can be allocated the next time |
---|
588 | * output is attempted. |
---|
589 | */ |
---|
590 | } |
---|
591 | } |
---|
592 | |
---|
593 | struct inpcb * |
---|
594 | in_pcblookup(struct inpcbinfo *pcbinfo, |
---|
595 | struct in_addr faddr, u_int fport_arg, |
---|
596 | struct in_addr laddr, u_int lport_arg, |
---|
597 | int wild_okay) |
---|
598 | { |
---|
599 | register struct inpcb *inp, *match = NULL; |
---|
600 | int matchwild = 3, wildcard; |
---|
601 | u_short fport = fport_arg, lport = lport_arg; |
---|
602 | int s; |
---|
603 | |
---|
604 | s = splnet(); |
---|
605 | |
---|
606 | for (inp = pcbinfo->listhead->lh_first; inp != NULL; inp = inp->inp_list.le_next) { |
---|
607 | if (inp->inp_lport != lport) |
---|
608 | continue; |
---|
609 | wildcard = 0; |
---|
610 | if (inp->inp_faddr.s_addr != INADDR_ANY) { |
---|
611 | if (faddr.s_addr == INADDR_ANY) |
---|
612 | wildcard++; |
---|
613 | else if (inp->inp_faddr.s_addr != faddr.s_addr || |
---|
614 | inp->inp_fport != fport) |
---|
615 | continue; |
---|
616 | } else { |
---|
617 | if (faddr.s_addr != INADDR_ANY) |
---|
618 | wildcard++; |
---|
619 | } |
---|
620 | if (inp->inp_laddr.s_addr != INADDR_ANY) { |
---|
621 | if (laddr.s_addr == INADDR_ANY) |
---|
622 | wildcard++; |
---|
623 | else if (inp->inp_laddr.s_addr != laddr.s_addr) |
---|
624 | continue; |
---|
625 | } else { |
---|
626 | if (laddr.s_addr != INADDR_ANY) |
---|
627 | wildcard++; |
---|
628 | } |
---|
629 | if (wildcard && wild_okay == 0) |
---|
630 | continue; |
---|
631 | if (wildcard < matchwild) { |
---|
632 | match = inp; |
---|
633 | matchwild = wildcard; |
---|
634 | if (matchwild == 0) { |
---|
635 | break; |
---|
636 | } |
---|
637 | } |
---|
638 | } |
---|
639 | splx(s); |
---|
640 | return (match); |
---|
641 | } |
---|
642 | |
---|
643 | /* |
---|
644 | * Lookup PCB in hash list. |
---|
645 | */ |
---|
646 | struct inpcb * |
---|
647 | in_pcblookuphash(struct inpcbinfo *pcbinfo, |
---|
648 | struct in_addr faddr, u_int fport_arg, |
---|
649 | struct in_addr laddr, u_int lport_arg, |
---|
650 | int wildcard) |
---|
651 | { |
---|
652 | struct inpcbhead *head; |
---|
653 | register struct inpcb *inp; |
---|
654 | u_short fport = fport_arg, lport = lport_arg; |
---|
655 | int s; |
---|
656 | |
---|
657 | s = splnet(); |
---|
658 | /* |
---|
659 | * First look for an exact match. |
---|
660 | */ |
---|
661 | head = &pcbinfo->hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->hashmask)]; |
---|
662 | for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { |
---|
663 | if (inp->inp_faddr.s_addr == faddr.s_addr && |
---|
664 | inp->inp_laddr.s_addr == laddr.s_addr && |
---|
665 | inp->inp_fport == fport && |
---|
666 | inp->inp_lport == lport) |
---|
667 | goto found; |
---|
668 | } |
---|
669 | if (wildcard) { |
---|
670 | struct inpcb *local_wild = NULL; |
---|
671 | |
---|
672 | head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; |
---|
673 | for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { |
---|
674 | if (inp->inp_faddr.s_addr == INADDR_ANY && |
---|
675 | inp->inp_fport == 0 && inp->inp_lport == lport) { |
---|
676 | if (inp->inp_laddr.s_addr == laddr.s_addr) |
---|
677 | goto found; |
---|
678 | else if (inp->inp_laddr.s_addr == INADDR_ANY) |
---|
679 | local_wild = inp; |
---|
680 | } |
---|
681 | } |
---|
682 | if (local_wild != NULL) { |
---|
683 | inp = local_wild; |
---|
684 | goto found; |
---|
685 | } |
---|
686 | } |
---|
687 | splx(s); |
---|
688 | return (NULL); |
---|
689 | |
---|
690 | found: |
---|
691 | /* |
---|
692 | * Move PCB to head of this hash chain so that it can be |
---|
693 | * found more quickly in the future. |
---|
694 | * XXX - this is a pessimization on machines with few |
---|
695 | * concurrent connections. |
---|
696 | */ |
---|
697 | if (inp != head->lh_first) { |
---|
698 | LIST_REMOVE(inp, inp_hash); |
---|
699 | LIST_INSERT_HEAD(head, inp, inp_hash); |
---|
700 | } |
---|
701 | splx(s); |
---|
702 | return (inp); |
---|
703 | } |
---|
704 | |
---|
705 | /* |
---|
706 | * Insert PCB into hash chain. Must be called at splnet. |
---|
707 | */ |
---|
708 | static void |
---|
709 | in_pcbinshash(struct inpcb *inp) |
---|
710 | { |
---|
711 | struct inpcbhead *head; |
---|
712 | |
---|
713 | head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr, |
---|
714 | inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)]; |
---|
715 | |
---|
716 | LIST_INSERT_HEAD(head, inp, inp_hash); |
---|
717 | } |
---|
718 | |
---|
719 | void |
---|
720 | in_pcbrehash(struct inpcb *inp) |
---|
721 | { |
---|
722 | struct inpcbhead *head; |
---|
723 | int s; |
---|
724 | |
---|
725 | s = splnet(); |
---|
726 | LIST_REMOVE(inp, inp_hash); |
---|
727 | |
---|
728 | head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr, |
---|
729 | inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)]; |
---|
730 | |
---|
731 | LIST_INSERT_HEAD(head, inp, inp_hash); |
---|
732 | inp->inp_pcbinfo->ipi_count--; |
---|
733 | splx(s); |
---|
734 | } |
---|