diff --git a/README.md b/README.md
index 449024b..034a632 100644
--- a/README.md
+++ b/README.md
@@ -26,12 +26,16 @@ Configurating the server and client are are relatively easy. Currently (since th
- **SERVER_PUBLIC_KEY** (Hex String): The public key to be used
- **SERVER_PRIVATE_KEY** (Hex String): The private key to be used
+- **NETWORK** (IPv4 Format): The network IPv4 to be used (Server Interface still needs to be configured manually)
+- **SUBNET_MASK** (Integer): The subnet mask to be used (ensure proper length, it will not be checked)
**Example:**
```
SERVER_PUBLIC_KEY=787B648046F10DDD0B77A6303BE42D859AA65C52F5708CC3C58EB5691F217C7B
SERVER_PRIVATE_KEY=778604245F57B847E63BD85DE8208FF1A127FB559895195928C3987E246B77B8787B648046F10DDD0B77A6303BE42D859AA65C52F5708CC3C58EB5691F217C7B
+NETWORK=10.10.0.0
+SUBNET_MASK=24
```
diff --git a/include/columnlynx/common/net/session_registry.hpp b/include/columnlynx/common/net/session_registry.hpp
index 20939ba..25617e6 100644
--- a/include/columnlynx/common/net/session_registry.hpp
+++ b/include/columnlynx/common/net/session_registry.hpp
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -109,22 +110,23 @@ namespace ColumnLynx::Net {
return static_cast(mSessions.size());
}
- // IP management (simple for /24 subnet)
+ // IP management
// Get the lowest available IPv4 address; Returns 0 if none available
- uint32_t getFirstAvailableIP() const {
+ uint32_t getFirstAvailableIP(uint32_t baseIP, uint8_t mask) const {
std::shared_lock lock(mMutex);
- uint32_t baseIP = 0x0A0A0002; // 10.10.0.2
+
+ uint32_t hostSpace = (1u << (32 - mask)) - 2; // Usable hosts
- // TODO: Expand to support larger subnets
- for (uint32_t offset = 0; offset < 254; offset++) {
+ // Skip 0 (network) and 1 (server reserved), start at 2
+ for (uint32_t offset = 2; offset <= hostSpace; offset++) {
uint32_t candidateIP = baseIP + offset;
if (mSessionIPs.find(candidateIP) == mSessionIPs.end()) {
return candidateIP;
}
}
-
- return 0; // Unavailable
+
+ return 0; // No available IPs
}
// Lock an IP as assigned to a specific session
diff --git a/include/columnlynx/common/net/virtual_interface.hpp b/include/columnlynx/common/net/virtual_interface.hpp
index cf3d2d4..1b19bff 100644
--- a/include/columnlynx/common/net/virtual_interface.hpp
+++ b/include/columnlynx/common/net/virtual_interface.hpp
@@ -60,6 +60,16 @@ namespace ColumnLynx::Net {
return std::string(buf);
}
+ static inline uint32_t stringToIpv4(const std::string &ipStr) {
+ struct in_addr addr;
+
+ if (inet_pton(AF_INET, ipStr.c_str(), &addr) != 1) {
+ return 0; // "0.0.0.0"
+ }
+
+ return ntohl(addr.s_addr);
+ }
+
static inline uint32_t prefixLengthToNetmask(uint8_t prefixLen) {
if (prefixLen == 0) return 0;
uint32_t mask = (0xFFFFFFFF << (32 - prefixLen)) & 0xFFFFFFFF;
diff --git a/include/columnlynx/common/utils.hpp b/include/columnlynx/common/utils.hpp
index ad84710..97014fa 100644
--- a/include/columnlynx/common/utils.hpp
+++ b/include/columnlynx/common/utils.hpp
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
#include
#ifdef _WIN32
@@ -94,5 +95,5 @@ namespace ColumnLynx::Utils {
}
// Returns the config file in an unordered_map format. This purely reads the config file, you still need to parse it manually.
- std::unordered_map getConfigMap(std::string path);
+ std::unordered_map getConfigMap(std::string path, std::vector requiredKeys = {});
};
\ No newline at end of file
diff --git a/include/columnlynx/server/net/tcp/tcp_connection.hpp b/include/columnlynx/server/net/tcp/tcp_connection.hpp
index 771c2e7..4b93140 100644
--- a/include/columnlynx/server/net/tcp/tcp_connection.hpp
+++ b/include/columnlynx/server/net/tcp/tcp_connection.hpp
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
namespace ColumnLynx::Net::TCP {
class TCPConnection : public std::enable_shared_from_this {
@@ -26,9 +27,10 @@ namespace ColumnLynx::Net::TCP {
static pointer create(
asio::ip::tcp::socket socket,
std::shared_ptr sodiumWrapper,
+ std::unordered_map* serverConfig,
std::function onDisconnect)
{
- auto conn = pointer(new TCPConnection(std::move(socket), sodiumWrapper));
+ auto conn = pointer(new TCPConnection(std::move(socket), sodiumWrapper, serverConfig));
conn->mOnDisconnect = std::move(onDisconnect);
return conn;
}
@@ -48,10 +50,11 @@ namespace ColumnLynx::Net::TCP {
std::array getAESKey() const;
private:
- TCPConnection(asio::ip::tcp::socket socket, std::shared_ptr sodiumWrapper)
+ TCPConnection(asio::ip::tcp::socket socket, std::shared_ptr sodiumWrapper, std::unordered_map* serverConfig)
:
mHandler(std::make_shared(std::move(socket))),
mLibSodiumWrapper(sodiumWrapper),
+ mRawServerConfig(serverConfig),
mHeartbeatTimer(mHandler->socket().get_executor()),
mLastHeartbeatReceived(std::chrono::steady_clock::now()),
mLastHeartbeatSent(std::chrono::steady_clock::now())
@@ -65,6 +68,7 @@ namespace ColumnLynx::Net::TCP {
std::shared_ptr mHandler;
std::function)> mOnDisconnect;
std::shared_ptr mLibSodiumWrapper;
+ std::unordered_map* mRawServerConfig;
std::array mConnectionAESKey;
uint64_t mConnectionSessionID;
AsymPublicKey mConnectionPublicKey;
diff --git a/include/columnlynx/server/net/tcp/tcp_server.hpp b/include/columnlynx/server/net/tcp/tcp_server.hpp
index 5a0fd70..364a727 100644
--- a/include/columnlynx/server/net/tcp/tcp_server.hpp
+++ b/include/columnlynx/server/net/tcp/tcp_server.hpp
@@ -32,7 +32,7 @@ namespace ColumnLynx::Net::TCP {
mHostRunning(hostRunning)
{
// Preload the config map
- mRawServerConfig = Utils::getConfigMap("server_config");
+ mRawServerConfig = Utils::getConfigMap("server_config", {"NETWORK", "SUBNET_MASK"});
asio::error_code ec;
diff --git a/src/common/utils.cpp b/src/common/utils.cpp
index eff056b..03e0797 100644
--- a/src/common/utils.cpp
+++ b/src/common/utils.cpp
@@ -118,7 +118,7 @@ namespace ColumnLynx::Utils {
return out;
}
- std::unordered_map getConfigMap(std::string path) {
+ std::unordered_map getConfigMap(std::string path, std::vector requiredKeys) {
// TODO: Currently re-reads every time.
std::vector readLines;
@@ -129,6 +129,16 @@ namespace ColumnLynx::Utils {
readLines.push_back(line);
}
+ if (!requiredKeys.empty()) {
+ // Check if they exist using unordered_set magic
+ std::unordered_set setA(readLines.begin(), readLines.end());
+ for (std::string x : requiredKeys) {
+ if (!setA.count(x)) {
+ throw std::runtime_error("Config doesn't contain all required keys! (Missing: '" + x + "')");
+ }
+ }
+ }
+
// Parse them into the struct
std::unordered_map config;
char delimiter = '=';
diff --git a/src/server/server/net/tcp/tcp_connection.cpp b/src/server/server/net/tcp/tcp_connection.cpp
index 891cf63..71d2f0d 100644
--- a/src/server/server/net/tcp/tcp_connection.cpp
+++ b/src/server/server/net/tcp/tcp_connection.cpp
@@ -202,7 +202,18 @@ namespace ColumnLynx::Net::TCP {
// Encrypt the Session ID with the established AES key (using symmetric encryption, nonce can be all zeros for this purpose)
Nonce symNonce{}; // All zeros
- uint32_t clientIP = SessionRegistry::getInstance().getFirstAvailableIP();
+ std::string networkString = mRawServerConfig->find("NETWORK")->second; // The load check guarantees that this value exists
+ uint8_t configMask = std::stoi(mRawServerConfig->find("SUBNET_MASK")->second); // Same deal here
+
+ uint32_t baseIP = Net::VirtualInterface::stringToIpv4(networkString);
+
+ if (baseIP == 0) {
+ Utils::warn("Your NETWORK value in the server configuration is malformed! I will not be able to accept connections! (Connection " + reqAddr + " was killed)");
+ disconnect();
+ return;
+ }
+
+ uint32_t clientIP = SessionRegistry::getInstance().getFirstAvailableIP(baseIP, configMask);
if (clientIP == 0) {
Utils::warn("Out of available IPs! Disconnecting client " + reqAddr);
@@ -214,8 +225,8 @@ namespace ColumnLynx::Net::TCP {
tunConfig.version = Utils::protocolVersion();
tunConfig.prefixLength = 24;
tunConfig.mtu = 1420;
- tunConfig.serverIP = htonl(0x0A0A0001); // 10.10.0.1
- tunConfig.clientIP = htonl(clientIP); // 10.10.0.X
+ tunConfig.serverIP = htonl(baseIP + 1); // e.g. 10.10.0.1
+ tunConfig.clientIP = htonl(clientIP); // e.g. 10.10.0.X
tunConfig.dns1 = htonl(0x08080808); // 8.8.8.8
tunConfig.dns2 = 0;
diff --git a/src/server/server/net/tcp/tcp_server.cpp b/src/server/server/net/tcp/tcp_server.cpp
index 3f71670..05735a9 100644
--- a/src/server/server/net/tcp/tcp_server.cpp
+++ b/src/server/server/net/tcp/tcp_server.cpp
@@ -31,10 +31,11 @@ namespace ColumnLynx::Net::TCP {
mStartAccept();
return;
}
-
+
auto client = TCPConnection::create(
std::move(socket),
mSodiumWrapper,
+ &mRawServerConfig,
[this](std::shared_ptr c) {
mClients.erase(c);
Utils::log("Client removed.");