summaryrefslogtreecommitdiff
path: root/sys/netinet
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 /sys/netinet
parent7ee4a93a62c620e1b692cf6c06f901330a29d865 (diff)
Move port picking away from in_pcbbind()
ok sthen@
Diffstat (limited to 'sys/netinet')
-rw-r--r--sys/netinet/in_pcb.c109
-rw-r--r--sys/netinet/in_pcb.h4
2 files changed, 67 insertions, 46 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_ */