Skip to content

Commit dc319ab

Browse files
authored
Merge pull request #1394 from AndreasAakesson/dev
Moved Port_util to separate module + unittest
2 parents 5f96af3 + f38d7f8 commit dc319ab

8 files changed

Lines changed: 271 additions & 112 deletions

File tree

api/net/inet_common.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ namespace net {
161161
static constexpr uint16_t USER_START {1024};
162162
static constexpr uint16_t USER_END {49151};
163163
static constexpr uint16_t DYNAMIC_START {49152};
164-
static constexpr uint16_t DYNAMIC_END {65535}; // 65535 should never be assigned
164+
static constexpr uint16_t DYNAMIC_END {65535};
165165

166166
static constexpr bool is_dynamic(const uint16_t port) noexcept
167167
{ return port > USER_END; }

api/net/port_util.hpp

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// This file is a part of the IncludeOS unikernel - www.includeos.org
2+
//
3+
// Copyright 2017 Oslo and Akershus University College of Applied Sciences
4+
// and Alfred Bratterud
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
#pragma once
19+
#ifndef NET_PORT_UTIL_HPP
20+
#define NET_PORT_UTIL_HPP
21+
22+
#include "inet_common.hpp"
23+
#include <util/fixed_bitmap.hpp>
24+
25+
namespace net {
26+
27+
struct Port_error : public std::runtime_error {
28+
using base = std::runtime_error;
29+
using base::base;
30+
};
31+
32+
/**
33+
* @brief Class for handling a full range of network ports.
34+
* Generates ephemeral ports and track what ports are bound or not.
35+
* 1 means free, 0 means bound (occupied)
36+
*/
37+
class Port_util {
38+
public:
39+
/**
40+
* @brief Construct a port util with a new generated ephemeral port
41+
* and a empty port list.
42+
*/
43+
Port_util()
44+
: ports(),
45+
eph_view{ // set the ephemeral view to be between 49152-65535
46+
ports.data() + port_ranges::DYNAMIC_START,
47+
(port_ranges::DYNAMIC_END - port_ranges::DYNAMIC_START + 1) / sizeof(MemBitmap::word)
48+
},
49+
ephemeral_(net::new_ephemeral_port()),
50+
eph_count(0)
51+
{
52+
// all ports are free
53+
ports.set_all();
54+
}
55+
56+
/**
57+
* @brief Gets the next ephemeral port.
58+
* increment_ephemeral may throw
59+
*
60+
* @return The next ephemeral port.
61+
*/
62+
uint16_t get_next_ephemeral()
63+
{
64+
increment_ephemeral();
65+
return ephemeral_;
66+
}
67+
68+
/**
69+
* @brief Bind a port, making it reserved.
70+
*
71+
* @param[in] port The port
72+
*/
73+
void bind(const uint16_t port) noexcept
74+
{
75+
ports.reset(port);
76+
77+
if(port_ranges::is_dynamic(port)) ++eph_count;
78+
}
79+
80+
/**
81+
* @brief Unbind a port, making it available.
82+
*
83+
* @param[in] port The port
84+
*/
85+
void unbind(const uint16_t port) noexcept
86+
{
87+
ports.set(port);
88+
89+
if(port_ranges::is_dynamic(port)) --eph_count;
90+
}
91+
92+
/**
93+
* @brief Determines if the port is bound.
94+
*
95+
* @param[in] port The port
96+
*
97+
* @return True if bound, False otherwise.
98+
*/
99+
bool is_bound(const uint16_t port) const noexcept
100+
{
101+
return !ports[port];
102+
}
103+
104+
/**
105+
* @brief Determines if it has any free ephemeral ports.
106+
*
107+
* @return True if has free ephemeral, False otherwise.
108+
*/
109+
bool has_free_ephemeral() const noexcept
110+
{ return eph_count < (port_ranges::DYNAMIC_END - port_ranges::DYNAMIC_START); }
111+
112+
private:
113+
Fixed_bitmap<65536> ports;
114+
MemBitmap eph_view;
115+
uint16_t ephemeral_;
116+
uint16_t eph_count;
117+
118+
/**
119+
* @brief Increment the ephemeral port by one.
120+
* Throws if there are no more free ephemeral ports available.
121+
*/
122+
void increment_ephemeral()
123+
{
124+
if(UNLIKELY( not has_free_ephemeral() ))
125+
throw Port_error{"All ephemeral ports are taken"};
126+
127+
ephemeral_++;
128+
129+
// wrap around to dynamic start if end
130+
if(UNLIKELY(ephemeral_ == 0))
131+
ephemeral_ = port_ranges::DYNAMIC_START;
132+
133+
if(UNLIKELY( is_bound(ephemeral_) ))
134+
{
135+
auto i = eph_view.first_set();
136+
Ensures(i != -1 && "Did not found a free ephemeral even tho has_free_ephemeral() == true...");
137+
ephemeral_ = port_ranges::DYNAMIC_START + i;
138+
}
139+
140+
Expects(not is_bound(ephemeral_) && "Generated ephemeral port is already bound. Please fix me!");
141+
}
142+
}; // < class Port_util
143+
144+
} // < namespace net
145+
146+
#endif

api/net/tcp/tcp.hpp

Lines changed: 1 addition & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
#include <queue> // writeq
3030
#include <net/inet.hpp>
3131
#include <net/socket.hpp>
32-
#include <bitset>
32+
#include <net/port_util.hpp>
3333

3434
namespace net {
3535

@@ -54,89 +54,6 @@ namespace net {
5454
private:
5555
using Listeners = std::map<Socket, std::unique_ptr<tcp::Listener>>;
5656
using Connections = std::map<tcp::Connection::Tuple, tcp::Connection_ptr>;
57-
58-
/**
59-
* @brief Class for port utility.
60-
*/
61-
class Port_util {
62-
public:
63-
/**
64-
* @brief Construct a port util with a new generated ephemeral port
65-
* and a empty port list.
66-
*/
67-
Port_util();
68-
69-
/**
70-
* @brief Gets the next ephemeral port.
71-
* increment_ephemeral may throw
72-
*
73-
* @return The next ephemeral port.
74-
*/
75-
uint16_t get_next_ephemeral()
76-
{
77-
increment_ephemeral();
78-
return ephemeral_;
79-
}
80-
81-
/**
82-
* @brief Bind a port, making it reserved.
83-
*
84-
* @param[in] port The port
85-
*/
86-
void bind(const uint16_t port) noexcept
87-
{
88-
Expects(port < port_ranges::DYNAMIC_END);
89-
ports.set(port);
90-
91-
if(port_ranges::is_dynamic(port)) ++eph_count;
92-
}
93-
94-
/**
95-
* @brief Unbind a port, making it available.
96-
*
97-
* @param[in] port The port
98-
*/
99-
void unbind(const uint16_t port) noexcept
100-
{
101-
Expects(port < port_ranges::DYNAMIC_END);
102-
ports.reset(port);
103-
104-
if(port_ranges::is_dynamic(port)) --eph_count;
105-
}
106-
107-
/**
108-
* @brief Determines if the port is bound.
109-
*
110-
* @param[in] port The port
111-
*
112-
* @return True if bound, False otherwise.
113-
*/
114-
bool is_bound(const uint16_t port) const noexcept
115-
{
116-
Expects(port < port_ranges::DYNAMIC_END);
117-
return ports[port];
118-
}
119-
120-
/**
121-
* @brief Determines if it has any free ephemeral ports.
122-
*
123-
* @return True if has free ephemeral, False otherwise.
124-
*/
125-
bool has_free_ephemeral() const noexcept
126-
{ return eph_count < (port_ranges::DYNAMIC_END - port_ranges::DYNAMIC_START); }
127-
128-
private:
129-
std::bitset<65536> ports;
130-
uint16_t ephemeral_;
131-
uint16_t eph_count;
132-
133-
/**
134-
* @brief Increment the ephemeral port by one.
135-
* Throws if there are no more free ephemeral ports available.
136-
*/
137-
void increment_ephemeral();
138-
139-
}; // < class Port_util
14057
using Port_lists = std::map<tcp::Address, Port_util>;
14158

14259
public:

api/util/fixed_bitmap.hpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// This file is a part of the IncludeOS unikernel - www.includeos.org
2+
//
3+
// Copyright 2017 Oslo and Akershus University College of Applied Sciences
4+
// and Alfred Bratterud
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
#pragma once
19+
#ifndef UTIL_FIXED_BITMAP_HPP
20+
#define UTIL_FIXED_BITMAP_HPP
21+
22+
#include "membitmap.hpp"
23+
#include <array>
24+
25+
/**
26+
* @brief A membitmap with a fixed amount of bits and storage.
27+
*
28+
* @tparam N Number of bits. Needs to be divisable by sizeof(Storage)
29+
*/
30+
template <size_t N>
31+
class Fixed_bitmap : public MemBitmap {
32+
public:
33+
using Storage = MemBitmap::word;
34+
static_assert(N >= sizeof(Storage), "Number of bits need to be atleast sizeof(Storage)");
35+
static_assert(N % sizeof(Storage) == 0, "Number of bits need to be divisable by sizeof(Storage)");
36+
37+
public:
38+
Fixed_bitmap() :
39+
MemBitmap{},
40+
storage{}
41+
{
42+
set_location(storage.data(), N / sizeof(Storage));
43+
}
44+
45+
private:
46+
std::array<Storage, N / sizeof(Storage)> storage;
47+
48+
}; // < class Fixed_bitmap
49+
50+
#endif

src/net/tcp/tcp.cpp

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -79,31 +79,6 @@ void TCP::smp_process_writeq(size_t packets)
7979
SMP::signal(this->cpu_id);
8080
}
8181

82-
TCP::Port_util::Port_util()
83-
: ports{},
84-
ephemeral_(new_ephemeral_port()),
85-
eph_count(0)
86-
{
87-
ports.set(port_ranges::DYNAMIC_END);
88-
}
89-
90-
void TCP::Port_util::increment_ephemeral()
91-
{
92-
if(UNLIKELY(! has_free_ephemeral() ))
93-
throw TCP_error{"All ephemeral ports are taken"};
94-
95-
ephemeral_++;
96-
97-
if(UNLIKELY(ephemeral_ == port_ranges::DYNAMIC_END))
98-
ephemeral_ = port_ranges::DYNAMIC_START;
99-
100-
// TODO: Avoid wrap around, increment ephemeral to next free port.
101-
// while(is_bound(ephemeral_)) ++ephemeral_; // worst case is like 16k iterations :D
102-
// need a solution that checks each word of the subset (the dynamic range)
103-
// FIXME: this may happen...
104-
Ensures(is_bound(ephemeral_) == false && "Hoped I wouldn't see the day...");
105-
}
106-
10782
/*
10883
Note: There is different approaches to how to handle listeners & connections.
10984
Need to discuss and decide for the best one.

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ set(TEST_SOURCES
8585
${TEST}/net/unit/ip4_addr.cpp
8686
${TEST}/net/unit/ip4.cpp
8787
${TEST}/net/unit/packets.cpp
88+
${TEST}/net/unit/port_util_test.cpp
8889
${TEST}/net/unit/socket.cpp
8990
${TEST}/net/unit/tcp_packet_test.cpp
9091
${TEST}/net/unit/tcp_write_queue.cpp

test/net/unit/dhcp_message_test.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,17 @@ struct test_opt
3232
// Creates a DHCP DISCOVERY message on the buffer
3333
net::dhcp::Message* create_discovery_msg(uint8_t* buffer)
3434
{
35+
using namespace net;
3536
using namespace net::dhcp;
3637
auto* msg = reinterpret_cast<Message*>(buffer);
3738

3839
msg->op = static_cast<uint8_t>(op_code::BOOTREQUEST);
3940
msg->htype = static_cast<uint8_t>(htype::ETHER);
4041
msg->hlen = ETH_ALEN;
4142
msg->hops = 0;
42-
msg->xid = net::htonl(322420);
43+
msg->xid = htonl(322420);
4344
msg->secs = 0;
44-
msg->flags = net::htons(static_cast<uint16_t>(flag::BOOTP_BROADCAST));
45+
msg->flags = htons(static_cast<uint16_t>(flag::BOOTP_BROADCAST));
4546
msg->ciaddr = 0;
4647
msg->yiaddr = 0;
4748
msg->siaddr = 0;

0 commit comments

Comments
 (0)