diff --git a/Kernel/IPv4Socket.cpp b/Kernel/IPv4Socket.cpp index 1388b8e8d0..63ad0ae80a 100644 --- a/Kernel/IPv4Socket.cpp +++ b/Kernel/IPv4Socket.cpp @@ -112,11 +112,15 @@ bool IPv4Socket::can_write(SocketRole) const return true; } -void IPv4Socket::allocate_source_port_if_needed() +int IPv4Socket::allocate_source_port_if_needed() { if (m_source_port) - return; - protocol_allocate_source_port(); + return m_source_port; + int port = protocol_allocate_source_port(); + if (port < 0) + return port; + m_source_port = (word)port; + return port; } ssize_t IPv4Socket::sendto(const void* data, size_t data_length, int flags, const sockaddr* addr, socklen_t addr_length) @@ -142,7 +146,9 @@ ssize_t IPv4Socket::sendto(const void* data, size_t data_length, int flags, cons m_destination_port = ntohs(ia.sin_port); } - allocate_source_port_if_needed(); + int rc = allocate_source_port_if_needed(); + if (rc < 0) + return rc; kprintf("sendto: destination=%s:%u\n", m_destination_address.to_string().characters(), m_destination_port); diff --git a/Kernel/IPv4Socket.h b/Kernel/IPv4Socket.h index dd7cb5b957..d4729dd7f0 100644 --- a/Kernel/IPv4Socket.h +++ b/Kernel/IPv4Socket.h @@ -45,12 +45,12 @@ public: protected: IPv4Socket(int type, int protocol); - void allocate_source_port_if_needed(); + int allocate_source_port_if_needed(); virtual int protocol_receive(const ByteBuffer&, void*, size_t, int, sockaddr*, socklen_t*) { return -ENOTIMPL; } virtual int protocol_send(const void*, int) { return -ENOTIMPL; } virtual KResult protocol_connect() { return KSuccess; } - virtual void protocol_allocate_source_port() { } + virtual int protocol_allocate_source_port() { return 0; } virtual bool protocol_is_disconnected() const { return false; } private: diff --git a/Kernel/RandomDevice.cpp b/Kernel/RandomDevice.cpp index 156c3d582d..7f4face5cb 100644 --- a/Kernel/RandomDevice.cpp +++ b/Kernel/RandomDevice.cpp @@ -16,12 +16,17 @@ RandomDevice::~RandomDevice() static unsigned long next = 1; #define MY_RAND_MAX 32767 -static int myrand() +int RandomDevice::random_value() { next = next * 1103515245 + 12345; return((unsigned)(next/((MY_RAND_MAX + 1) * 2)) % (MY_RAND_MAX + 1)); } +float RandomDevice::random_percentage() +{ + return (float)random_value() / (float)MY_RAND_MAX; +} + #if 0 static void mysrand(unsigned seed) { @@ -39,7 +44,7 @@ ssize_t RandomDevice::read(Process&, byte* buffer, ssize_t size) const int range = 'z' - 'a'; ssize_t nread = min(size, GoodBufferSize); for (ssize_t i = 0; i < nread; ++i) { - dword r = myrand() % range; + dword r = random_value() % range; buffer[i] = (byte)('a' + r); } return nread; diff --git a/Kernel/RandomDevice.h b/Kernel/RandomDevice.h index e2113b7cf3..9983d1fdc3 100644 --- a/Kernel/RandomDevice.h +++ b/Kernel/RandomDevice.h @@ -8,6 +8,9 @@ public: RandomDevice(); virtual ~RandomDevice() override; + static int random_value(); + static float random_percentage(); + private: // ^CharacterDevice virtual ssize_t read(Process&, byte*, ssize_t) override; diff --git a/Kernel/TCPSocket.cpp b/Kernel/TCPSocket.cpp index 9a37cd658a..159c838827 100644 --- a/Kernel/TCPSocket.cpp +++ b/Kernel/TCPSocket.cpp @@ -2,6 +2,7 @@ #include #include #include +#include Lockable>& TCPSocket::sockets_by_port() { @@ -173,19 +174,28 @@ KResult TCPSocket::protocol_connect() return KSuccess; } -void TCPSocket::protocol_allocate_source_port() +int TCPSocket::protocol_allocate_source_port() { - // This is not a very efficient allocation algorithm. - // FIXME: Replace it with a bitmap or some other fast-paced looker-upper. + static const word first_ephemeral_port = 32768; + static const word last_ephemeral_port = 60999; + static const word ephemeral_port_range_size = last_ephemeral_port - first_ephemeral_port; + word first_scan_port = first_ephemeral_port + (word)(RandomDevice::random_percentage() * ephemeral_port_range_size); + LOCKER(sockets_by_port().lock()); - for (word port = 2000; port < 60000; ++port) { + for (word port = first_scan_port;;) { auto it = sockets_by_port().resource().find(port); if (it == sockets_by_port().resource().end()) { set_source_port(port); sockets_by_port().resource().set(port, this); - return; + return port; } + ++port; + if (port > last_ephemeral_port) + port = first_ephemeral_port; + if (port == first_scan_port) + break; } + return -EADDRINUSE; } bool TCPSocket::protocol_is_disconnected() const diff --git a/Kernel/TCPSocket.h b/Kernel/TCPSocket.h index a193a8e18b..1d59a41e83 100644 --- a/Kernel/TCPSocket.h +++ b/Kernel/TCPSocket.h @@ -35,7 +35,7 @@ private: virtual int protocol_receive(const ByteBuffer&, void* buffer, size_t buffer_size, int flags, sockaddr* addr, socklen_t* addr_length) override; virtual int protocol_send(const void*, int) override; virtual KResult protocol_connect() override; - virtual void protocol_allocate_source_port() override; + virtual int protocol_allocate_source_port() override; virtual bool protocol_is_disconnected() const override; dword m_sequence_number { 0 }; diff --git a/Kernel/UDPSocket.cpp b/Kernel/UDPSocket.cpp index 1214eae465..61cfed1202 100644 --- a/Kernel/UDPSocket.cpp +++ b/Kernel/UDPSocket.cpp @@ -2,6 +2,7 @@ #include #include #include +#include Lockable>& UDPSocket::sockets_by_port() { @@ -83,17 +84,26 @@ KResult UDPSocket::protocol_connect() return KSuccess; } -void UDPSocket::protocol_allocate_source_port() +int UDPSocket::protocol_allocate_source_port() { - // This is not a very efficient allocation algorithm. - // FIXME: Replace it with a bitmap or some other fast-paced looker-upper. + static const word first_ephemeral_port = 32768; + static const word last_ephemeral_port = 60999; + static const word ephemeral_port_range_size = last_ephemeral_port - first_ephemeral_port; + word first_scan_port = first_ephemeral_port + (word)(RandomDevice::random_percentage() * ephemeral_port_range_size); + LOCKER(sockets_by_port().lock()); - for (word port = 2000; port < 60000; ++port) { + for (word port = first_scan_port;;) { auto it = sockets_by_port().resource().find(port); if (it == sockets_by_port().resource().end()) { set_source_port(port); sockets_by_port().resource().set(port, this); - return; + return port; } + ++port; + if (port > last_ephemeral_port) + port = first_ephemeral_port; + if (port == first_scan_port) + break; } + return -EADDRINUSE; } diff --git a/Kernel/UDPSocket.h b/Kernel/UDPSocket.h index 4bfe6ee712..69c3a66a24 100644 --- a/Kernel/UDPSocket.h +++ b/Kernel/UDPSocket.h @@ -18,7 +18,7 @@ private: virtual int protocol_receive(const ByteBuffer&, void* buffer, size_t buffer_size, int flags, sockaddr* addr, socklen_t* addr_length) override; virtual int protocol_send(const void*, int) override; virtual KResult protocol_connect() override; - virtual void protocol_allocate_source_port() override; + virtual int protocol_allocate_source_port() override; }; class UDPSocketHandle : public SocketHandle {