Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/net/inet_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ namespace net {
static constexpr uint16_t USER_START {1024};
static constexpr uint16_t USER_END {49151};
static constexpr uint16_t DYNAMIC_START {49152};
static constexpr uint16_t DYNAMIC_END {65535}; // 65535 should never be assigned
static constexpr uint16_t DYNAMIC_END {65535};

static constexpr bool is_dynamic(const uint16_t port) noexcept
{ return port > USER_END; }
Expand Down
146 changes: 146 additions & 0 deletions api/net/port_util.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2017 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once
#ifndef NET_PORT_UTIL_HPP
#define NET_PORT_UTIL_HPP

#include "inet_common.hpp"
#include <util/fixed_bitmap.hpp>

namespace net {

struct Port_error : public std::runtime_error {
using base = std::runtime_error;
using base::base;
};

/**
* @brief Class for handling a full range of network ports.
* Generates ephemeral ports and track what ports are bound or not.
* 1 means free, 0 means bound (occupied)
*/
class Port_util {
public:
/**
* @brief Construct a port util with a new generated ephemeral port
* and a empty port list.
*/
Port_util()
: ports(),
eph_view{ // set the ephemeral view to be between 49152-65535
ports.data() + port_ranges::DYNAMIC_START,
(port_ranges::DYNAMIC_END - port_ranges::DYNAMIC_START + 1) / sizeof(MemBitmap::word)
},
ephemeral_(net::new_ephemeral_port()),
eph_count(0)
{
// all ports are free
ports.set_all();
}

/**
* @brief Gets the next ephemeral port.
* increment_ephemeral may throw
*
* @return The next ephemeral port.
*/
uint16_t get_next_ephemeral()
{
increment_ephemeral();
return ephemeral_;
}

/**
* @brief Bind a port, making it reserved.
*
* @param[in] port The port
*/
void bind(const uint16_t port) noexcept
{
ports.reset(port);

if(port_ranges::is_dynamic(port)) ++eph_count;
}

/**
* @brief Unbind a port, making it available.
*
* @param[in] port The port
*/
void unbind(const uint16_t port) noexcept
{
ports.set(port);

if(port_ranges::is_dynamic(port)) --eph_count;
}

/**
* @brief Determines if the port is bound.
*
* @param[in] port The port
*
* @return True if bound, False otherwise.
*/
bool is_bound(const uint16_t port) const noexcept
{
return !ports[port];
}

/**
* @brief Determines if it has any free ephemeral ports.
*
* @return True if has free ephemeral, False otherwise.
*/
bool has_free_ephemeral() const noexcept
{ return eph_count < (port_ranges::DYNAMIC_END - port_ranges::DYNAMIC_START); }

private:
Fixed_bitmap<65536> ports;
MemBitmap eph_view;
uint16_t ephemeral_;
uint16_t eph_count;

/**
* @brief Increment the ephemeral port by one.
* Throws if there are no more free ephemeral ports available.
*/
void increment_ephemeral()
{
if(UNLIKELY( not has_free_ephemeral() ))
throw Port_error{"All ephemeral ports are taken"};

ephemeral_++;

// wrap around to dynamic start if end
if(UNLIKELY(ephemeral_ == 0))
ephemeral_ = port_ranges::DYNAMIC_START;

if(UNLIKELY( is_bound(ephemeral_) ))
{
auto i = eph_view.first_set();
Ensures(i != -1 && "Did not found a free ephemeral even tho has_free_ephemeral() == true...");
ephemeral_ = port_ranges::DYNAMIC_START + i;
}

Expects(not is_bound(ephemeral_) && "Generated ephemeral port is already bound. Please fix me!");
}
}; // < class Port_util

} // < namespace net

#endif
85 changes: 1 addition & 84 deletions api/net/tcp/tcp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
#include <queue> // writeq
#include <net/inet.hpp>
#include <net/socket.hpp>
#include <bitset>
#include <net/port_util.hpp>

namespace net {

Expand All @@ -54,89 +54,6 @@ namespace net {
private:
using Listeners = std::map<Socket, std::unique_ptr<tcp::Listener>>;
using Connections = std::map<tcp::Connection::Tuple, tcp::Connection_ptr>;

/**
* @brief Class for port utility.
*/
class Port_util {
public:
/**
* @brief Construct a port util with a new generated ephemeral port
* and a empty port list.
*/
Port_util();

/**
* @brief Gets the next ephemeral port.
* increment_ephemeral may throw
*
* @return The next ephemeral port.
*/
uint16_t get_next_ephemeral()
{
increment_ephemeral();
return ephemeral_;
}

/**
* @brief Bind a port, making it reserved.
*
* @param[in] port The port
*/
void bind(const uint16_t port) noexcept
{
Expects(port < port_ranges::DYNAMIC_END);
ports.set(port);

if(port_ranges::is_dynamic(port)) ++eph_count;
}

/**
* @brief Unbind a port, making it available.
*
* @param[in] port The port
*/
void unbind(const uint16_t port) noexcept
{
Expects(port < port_ranges::DYNAMIC_END);
ports.reset(port);

if(port_ranges::is_dynamic(port)) --eph_count;
}

/**
* @brief Determines if the port is bound.
*
* @param[in] port The port
*
* @return True if bound, False otherwise.
*/
bool is_bound(const uint16_t port) const noexcept
{
Expects(port < port_ranges::DYNAMIC_END);
return ports[port];
}

/**
* @brief Determines if it has any free ephemeral ports.
*
* @return True if has free ephemeral, False otherwise.
*/
bool has_free_ephemeral() const noexcept
{ return eph_count < (port_ranges::DYNAMIC_END - port_ranges::DYNAMIC_START); }

private:
std::bitset<65536> ports;
uint16_t ephemeral_;
uint16_t eph_count;

/**
* @brief Increment the ephemeral port by one.
* Throws if there are no more free ephemeral ports available.
*/
void increment_ephemeral();

}; // < class Port_util
using Port_lists = std::map<tcp::Address, Port_util>;

public:
Expand Down
50 changes: 50 additions & 0 deletions api/util/fixed_bitmap.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2017 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once
#ifndef UTIL_FIXED_BITMAP_HPP
#define UTIL_FIXED_BITMAP_HPP

#include "membitmap.hpp"
#include <array>

/**
* @brief A membitmap with a fixed amount of bits and storage.
*
* @tparam N Number of bits. Needs to be divisable by sizeof(Storage)
*/
template <size_t N>
class Fixed_bitmap : public MemBitmap {
public:
using Storage = MemBitmap::word;
static_assert(N >= sizeof(Storage), "Number of bits need to be atleast sizeof(Storage)");
static_assert(N % sizeof(Storage) == 0, "Number of bits need to be divisable by sizeof(Storage)");

public:
Fixed_bitmap() :
MemBitmap{},
storage{}
{
set_location(storage.data(), N / sizeof(Storage));
}

private:
std::array<Storage, N / sizeof(Storage)> storage;

}; // < class Fixed_bitmap

#endif
25 changes: 0 additions & 25 deletions src/net/tcp/tcp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,31 +79,6 @@ void TCP::smp_process_writeq(size_t packets)
SMP::signal(this->cpu_id);
}

TCP::Port_util::Port_util()
: ports{},
ephemeral_(new_ephemeral_port()),
eph_count(0)
{
ports.set(port_ranges::DYNAMIC_END);
}

void TCP::Port_util::increment_ephemeral()
{
if(UNLIKELY(! has_free_ephemeral() ))
throw TCP_error{"All ephemeral ports are taken"};

ephemeral_++;

if(UNLIKELY(ephemeral_ == port_ranges::DYNAMIC_END))
ephemeral_ = port_ranges::DYNAMIC_START;

// TODO: Avoid wrap around, increment ephemeral to next free port.
// while(is_bound(ephemeral_)) ++ephemeral_; // worst case is like 16k iterations :D
// need a solution that checks each word of the subset (the dynamic range)
// FIXME: this may happen...
Ensures(is_bound(ephemeral_) == false && "Hoped I wouldn't see the day...");
}

/*
Note: There is different approaches to how to handle listeners & connections.
Need to discuss and decide for the best one.
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ set(TEST_SOURCES
${TEST}/net/unit/ip4_addr.cpp
${TEST}/net/unit/ip4.cpp
${TEST}/net/unit/packets.cpp
${TEST}/net/unit/port_util_test.cpp
${TEST}/net/unit/socket.cpp
${TEST}/net/unit/tcp_packet_test.cpp
${TEST}/net/unit/tcp_write_queue.cpp
Expand Down
5 changes: 3 additions & 2 deletions test/net/unit/dhcp_message_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,17 @@ struct test_opt
// Creates a DHCP DISCOVERY message on the buffer
net::dhcp::Message* create_discovery_msg(uint8_t* buffer)
{
using namespace net;
using namespace net::dhcp;
auto* msg = reinterpret_cast<Message*>(buffer);

msg->op = static_cast<uint8_t>(op_code::BOOTREQUEST);
msg->htype = static_cast<uint8_t>(htype::ETHER);
msg->hlen = ETH_ALEN;
msg->hops = 0;
msg->xid = net::htonl(322420);
msg->xid = htonl(322420);
msg->secs = 0;
msg->flags = net::htons(static_cast<uint16_t>(flag::BOOTP_BROADCAST));
msg->flags = htons(static_cast<uint16_t>(flag::BOOTP_BROADCAST));
msg->ciaddr = 0;
msg->yiaddr = 0;
msg->siaddr = 0;
Expand Down
Loading