summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorbz <bz@FreeBSD.org>2008-10-20 18:43:59 +0000
committerbz <bz@FreeBSD.org>2008-10-20 18:43:59 +0000
commit0991899a98572c9cfb8466895fc7966cdd80fa66 (patch)
tree46345a122f5598cb6a5912d8f36bb4a9d8912bb4 /sys
parent732eb6cbef318aa86bee198ed954ddbf74ad853a (diff)
downloadFreeBSD-src-0991899a98572c9cfb8466895fc7966cdd80fa66.zip
FreeBSD-src-0991899a98572c9cfb8466895fc7966cdd80fa66.tar.gz
Bring over the change switching from using sequential to random
ephemeral port allocation as implemented in netinet/in_pcb.c rev. 1.143 (initially from OpenBSD) and follow-up commits during the last four and a half years including rev. 1.157, 1.162 and 1.199. This now is relying on the same infrastructure as has been implemented in in_pcb.c since rev. 1.199. Reviewed by: silby, rpaulo, mlaier MFC after: 2 months
Diffstat (limited to 'sys')
-rw-r--r--sys/netinet/in_pcb.h3
-rw-r--r--sys/netinet6/in6_src.c99
2 files changed, 55 insertions, 47 deletions
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
index b393c18..9a43511 100644
--- a/sys/netinet/in_pcb.h
+++ b/sys/netinet/in_pcb.h
@@ -449,6 +449,9 @@ extern int ipport_firstauto;
extern int ipport_lastauto;
extern int ipport_hifirstauto;
extern int ipport_hilastauto;
+extern int ipport_randomized;
+extern int ipport_stoprandom;
+extern int ipport_tcpallocs;
extern struct callout ipport_tick_callout;
void in_pcbpurgeif0(struct inpcbinfo *, struct ifnet *);
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c
index cb2ad89..7daa76f 100644
--- a/sys/netinet6/in6_src.c
+++ b/sys/netinet6/in6_src.c
@@ -95,6 +95,9 @@ __FBSDID("$FreeBSD$");
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/in6_pcb.h>
@@ -774,7 +777,7 @@ in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct ucred *cred)
INIT_VNET_INET(curvnet);
struct socket *so = inp->inp_socket;
u_int16_t lport = 0, first, last, *lastport;
- int count, error = 0, wild = 0;
+ int count, error = 0, wild = 0, dorandom;
struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
INP_INFO_WLOCK_ASSERT(pcbinfo);
@@ -802,57 +805,59 @@ in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct ucred *cred)
last = V_ipport_lastauto;
lastport = &pcbinfo->ipi_lastport;
}
+
+ /*
+ * For UDP, use random port allocation as long as the user
+ * allows it. For TCP (and as of yet unknown) connections,
+ * use random port allocation only if the user allows it AND
+ * ipport_tick() allows it.
+ */
+ if (V_ipport_randomized &&
+ (!V_ipport_stoprandom || pcbinfo == &V_udbinfo))
+ dorandom = 1;
+ else
+ dorandom = 0;
+ /*
+ * It makes no sense to do random port allocation if
+ * we have the only port available.
+ */
+ if (first == last)
+ dorandom = 0;
+ /* Make sure to not include UDP packets in the count. */
+ if (pcbinfo != &V_udbinfo)
+ V_ipport_tcpallocs++;
+
/*
- * Simple check to ensure all ports are not used up causing
- * a deadlock here.
- *
- * We split the two cases (up and down) so that the direction
- * is not being tested on each round of the loop.
+ * Instead of having two loops further down counting up or down
+ * make sure that first is always <= last and go with only one
+ * code path implementing all logic.
*/
if (first > last) {
- /*
- * counting down
- */
- count = first - last;
-
- do {
- if (count-- < 0) { /* completely used? */
- /*
- * Undo any address bind that may have
- * occurred above.
- */
- inp->in6p_laddr = in6addr_any;
- return (EAGAIN);
- }
- --*lastport;
- if (*lastport > first || *lastport < last)
- *lastport = first;
- lport = htons(*lastport);
- } while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr,
- lport, wild, cred));
- } else {
- /*
- * counting up
- */
- count = last - first;
-
- do {
- if (count-- < 0) { /* completely used? */
- /*
- * Undo any address bind that may have
- * occurred above.
- */
- inp->in6p_laddr = in6addr_any;
- return (EAGAIN);
- }
- ++*lastport;
- if (*lastport < first || *lastport > last)
- *lastport = first;
- lport = htons(*lastport);
- } while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr,
- lport, wild, cred));
+ u_int16_t aux;
+
+ aux = first;
+ first = last;
+ last = aux;
}
+ if (dorandom)
+ *lastport = first + (arc4random() % (last - first));
+
+ count = last - first;
+
+ do {
+ if (count-- < 0) { /* completely used? */
+ /* Undo an address bind that may have occurred. */
+ inp->in6p_laddr = in6addr_any;
+ return (EADDRNOTAVAIL);
+ }
+ ++*lastport;
+ if (*lastport < first || *lastport > last)
+ *lastport = first;
+ lport = htons(*lastport);
+ } while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr,
+ lport, wild, cred));
+
inp->inp_lport = lport;
if (in_pcbinshash(inp) != 0) {
inp->in6p_laddr = in6addr_any;
OpenPOWER on IntegriCloud