summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Gross <vgross@cvs.openbsd.org>2015-12-02 22:13:45 +0000
committerVincent Gross <vgross@cvs.openbsd.org>2015-12-02 22:13:45 +0000
commitac8f843affa87cba40901ca5cee8d384d2703b29 (patch)
tree68dc15495b0648bbc5db72d8bbbb7bb2f979d856
parent7ee4a93a62c620e1b692cf6c06f901330a29d865 (diff)
Move port picking away from in_pcbbind()
ok sthen@
-rw-r--r--sys/netinet/in_pcb.c109
-rw-r--r--sys/netinet/in_pcb.h4
-rw-r--r--sys/netinet6/in6_pcb.c80
-rw-r--r--sys/netinet6/udp6_output.c12
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;