diff options
author | Vincent Gross <vgross@cvs.openbsd.org> | 2015-12-02 22:13:45 +0000 |
---|---|---|
committer | Vincent Gross <vgross@cvs.openbsd.org> | 2015-12-02 22:13:45 +0000 |
commit | ac8f843affa87cba40901ca5cee8d384d2703b29 (patch) | |
tree | 68dc15495b0648bbc5db72d8bbbb7bb2f979d856 | |
parent | 7ee4a93a62c620e1b692cf6c06f901330a29d865 (diff) |
Move port picking away from in_pcbbind()
ok sthen@
-rw-r--r-- | sys/netinet/in_pcb.c | 109 | ||||
-rw-r--r-- | sys/netinet/in_pcb.h | 4 | ||||
-rw-r--r-- | sys/netinet6/in6_pcb.c | 80 | ||||
-rw-r--r-- | sys/netinet6/udp6_output.c | 12 |
4 files changed, 79 insertions, 126 deletions
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index cb95f7208f9..fdc063a92c7 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.c,v 1.189 2015/12/02 16:00:42 sashan Exp $ */ +/* $OpenBSD: in_pcb.c,v 1.190 2015/12/02 22:13:44 vgross Exp $ */ /* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */ /* @@ -106,6 +106,11 @@ struct in_addr zeroin_addr; +union { + struct in_addr za_in; + struct in6_addr za_in6; +} zeroin46_addr; + /* * These configure the range of local port addresses assigned to * "unspecified" outgoing connections/packets/whatever. @@ -281,7 +286,6 @@ in_pcbbind(struct inpcb *inp, struct mbuf *nam, struct proc *p) struct socket *so = inp->inp_socket; struct inpcbtable *table = inp->inp_table; struct sockaddr_in *sin; - u_int16_t lastport = 0; u_int16_t lport = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); int error; @@ -359,54 +363,71 @@ in_pcbbind(struct inpcb *inp, struct mbuf *nam, struct proc *p) } inp->inp_laddr = sin->sin_addr; } - if (lport == 0) { - u_int16_t bound_a, bound_b, first, last; - int count; - - if (inp->inp_flags & INP_HIGHPORT) { - bound_a = ipport_hifirstauto; /* sysctl */ - bound_b = ipport_hilastauto; - } else if (inp->inp_flags & INP_LOWPORT) { - if ((error = suser(p, 0))) - return (EACCES); - bound_a = IPPORT_RESERVED-1; /* 1023 */ - bound_b = 600; /* not IPPORT_RESERVED/2 */ - } else { - bound_a = ipport_firstauto; /* sysctl */ - bound_b = ipport_lastauto; - } - if (bound_a < bound_b) { - first = bound_a; - last = bound_b; - } else { - first = bound_b; - last = bound_a; - } - - /* - * Simple check to ensure all ports are not used up causing - * a deadlock here. - */ - - count = last - first; - lastport = first + arc4random_uniform(count); - do { - if (count-- < 0) /* completely used? */ - return (EADDRNOTAVAIL); - ++lastport; - if (lastport < first || lastport > last) - lastport = first; - lport = htons(lastport); - } while (in_baddynamic(lastport, so->so_proto->pr_protocol) || - in_pcblookup(table, &zeroin_addr, 0, - &inp->inp_laddr, lport, wild, inp->inp_rtableid)); - } + if (lport == 0) + if ((error = in_pcbpickport(&lport, wild, inp, p))) + return (error); inp->inp_lport = lport; in_pcbrehash(inp); return (0); } +int +in_pcbpickport(u_int16_t *lport, int wild, struct inpcb *inp, struct proc *p) +{ + struct socket *so = inp->inp_socket; + struct inpcbtable *table = inp->inp_table; + u_int16_t first, last, lower, higher, candidate, localport; + void *laddr; + int count; + + if (inp->inp_flags & INP_HIGHPORT) { + first = ipport_hifirstauto; /* sysctl */ + last = ipport_hilastauto; + } else if (inp->inp_flags & INP_LOWPORT) { + if (suser(p, 0)) + return (EACCES); + first = IPPORT_RESERVED-1; /* 1023 */ + last = 600; /* not IPPORT_RESERVED/2 */ + } else { + first = ipport_firstauto; /* sysctl */ + last = ipport_lastauto; + } + if (first < last) { + lower = first; + higher = last; + } else { + lower = last; + higher = first; + } + + /* + * Simple check to ensure all ports are not used up causing + * a deadlock here. + */ + + count = higher - lower; + candidate = lower + arc4random_uniform(count); + if (sotopf(so) == PF_INET6) + laddr = &inp->inp_laddr6; + else + laddr = &inp->inp_laddr; + + do { + if (count-- < 0) /* completely used? */ + return (EADDRNOTAVAIL); + ++candidate; + if (candidate < lower || candidate > higher) + candidate = lower; + localport = htons(candidate); + } while (in_baddynamic(localport, so->so_proto->pr_protocol) || + in_pcblookup(table, &zeroin46_addr, 0, + laddr, localport, wild, inp->inp_rtableid)); + *lport = localport; + + return (0); +} + /* * Connect from a socket to a specified address. * Both address and port must be specified in argument sin. diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index 9ac0f5bd0fe..030947cad88 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.h,v 1.91 2015/10/24 16:08:48 mpi Exp $ */ +/* $OpenBSD: in_pcb.h,v 1.92 2015/12/02 22:13:44 vgross Exp $ */ /* $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos Exp $ */ /* @@ -288,6 +288,6 @@ int in6_pcbnotify(struct inpcbtable *, struct sockaddr_in6 *, u_int, const struct sockaddr_in6 *, u_int, u_int, int, void *, void (*)(struct inpcb *, int)); int in6_selecthlim(struct inpcb *); -int in6_pcbsetport(struct in6_addr *, struct inpcb *, struct proc *); +int in_pcbpickport(u_int16_t *, int, struct inpcb *, struct proc *); #endif /* _KERNEL */ #endif /* _NETINET_IN_PCB_H_ */ diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 4e5e1c72e22..12fbb4f4c2b 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_pcb.c,v 1.82 2015/10/24 16:08:48 mpi Exp $ */ +/* $OpenBSD: in6_pcb.c,v 1.83 2015/12/02 22:13:44 vgross Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -276,83 +276,11 @@ in6_pcbbind(struct inpcb *inp, struct mbuf *nam, struct proc *p) inp->inp_laddr6 = sin6->sin6_addr; } - if (lport == 0) { - error = in6_pcbsetport(&inp->inp_laddr6, inp, p); - if (error != 0) - return error; - } else { - inp->inp_lport = lport; - in_pcbrehash(inp); - } - - return 0; -} - -int -in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct proc *p) -{ - struct socket *so = inp->inp_socket; - struct inpcbtable *table = inp->inp_table; - u_int16_t bound_a, bound_b, first, last; - u_int16_t lastport = 0; - u_int16_t lport = 0; - int count; - int wild = INPLOOKUP_IPV6; - int error; - - /* XXX we no longer support IPv4 mapped address, so no tweaks here */ - - if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 && - ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || - (so->so_options & SO_ACCEPTCONN) == 0)) - wild |= INPLOOKUP_WILDCARD; - - if (inp->inp_flags & INP_HIGHPORT) { - bound_a = ipport_hifirstauto; /* sysctl */ - bound_b = ipport_hilastauto; - } else if (inp->inp_flags & INP_LOWPORT) { - if ((error = suser(p, 0))) - return (EACCES); - bound_a = IPPORT_RESERVED-1; /* 1023 */ - bound_b = 600; /* not IPPORT_RESERVED/2 */ - } else { - bound_a = ipport_firstauto; /* sysctl */ - bound_b = ipport_lastauto; - } - if (bound_a < bound_b) { - first = bound_a; - last = bound_b; - } else { - first = bound_b; - last = bound_a; - } - - /* - * Simple check to ensure all ports are not used up causing - * a deadlock here. - */ - - count = last - first; - lastport = first + arc4random_uniform(count); - - do { - if (count-- < 0) /* completely used? */ - return (EADDRNOTAVAIL); - ++lastport; - if (lastport < first || lastport > last) - lastport = first; - lport = htons(lastport); - } while (in_baddynamic(lastport, so->so_proto->pr_protocol) || - in_pcblookup(table, &zeroin6_addr, 0, - &inp->inp_laddr6, lport, wild, inp->inp_rtableid)); - + if (lport == 0) + if ((error = in_pcbpickport(&lport, wild, inp, p))) + return (error); inp->inp_lport = lport; in_pcbrehash(inp); - -#if 0 - inp->inp_flowinfo = 0; /* XXX */ -#endif - return 0; } diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c index fd75e426255..4abaf4c6d5a 100644 --- a/sys/netinet6/udp6_output.c +++ b/sys/netinet6/udp6_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: udp6_output.c,v 1.40 2015/11/24 13:37:16 mpi Exp $ */ +/* $OpenBSD: udp6_output.c,v 1.41 2015/12/02 22:13:44 vgross Exp $ */ /* $KAME: udp6_output.c,v 1.21 2001/02/07 11:51:54 itojun Exp $ */ /* @@ -160,9 +160,13 @@ udp6_output(struct inpcb *in6p, struct mbuf *m, struct mbuf *addr6, if (error) goto release; - if (in6p->inp_lport == 0 && - (error = in6_pcbsetport(laddr, in6p, p)) != 0) - goto release; + if (in6p->inp_lport == 0){ + int s = splsoftnet(); + error = in6_pcbbind(in6p, NULL, p); + splx(s); + if (error) + goto release; + } } else { if (IN6_IS_ADDR_UNSPECIFIED(&in6p->inp_faddr6)) { error = ENOTCONN; |