From 98327991f1dfda06f5ba0ae3deab22093f29756b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedrich=20Sch=C3=B6ller?= Date: Sat, 12 Dec 2009 13:46:31 +0100 Subject: [PATCH] improved ip assignment more stable error handling --- client.cpp | 25 +++++++++++++++++-------- client.h | 7 +++++-- echo.cpp | 10 ++++++++-- main.cpp | 13 +++++++++---- server.cpp | 43 ++++++++++++++++++++++++++++--------------- server.h | 8 +++++--- tun.cpp | 11 ++--------- worker.cpp | 3 ++- 8 files changed, 76 insertions(+), 44 deletions(-) diff --git a/client.cpp b/client.cpp index 29db981..5f841c5 100644 --- a/client.cpp +++ b/client.cpp @@ -31,11 +31,14 @@ using namespace std; const Worker::TunnelHeader::Magic Client::magic("hanc"); -Client::Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, int maxPolls, - const char *passphrase, uid_t uid, gid_t gid, bool changeEchoId, bool changeEchoSeq) - : Worker(tunnelMtu, deviceName, false, uid, gid), auth(passphrase) +Client::Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, + int maxPolls, const char *passphrase, uid_t uid, gid_t gid, + bool changeEchoId, bool changeEchoSeq, uint32_t desiredIp) +: Worker(tunnelMtu, deviceName, false, uid, gid), auth(passphrase) { this->serverIp = serverIp; + this->clientIp = INADDR_NONE; + this->desiredIp = desiredIp; this->maxPolls = maxPolls; this->nextEchoId = Utility::rand(); this->changeEchoId = changeEchoId; @@ -47,13 +50,14 @@ Client::Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, int max Client::~Client() { - + } void Client::sendConnectionRequest() { Server::ClientConnectData *connectData = (Server::ClientConnectData *)echoSendPayloadBuffer(); connectData->maxPolls = maxPolls; + connectData->desiredIp = desiredIp; syslog(LOG_DEBUG, "sending connection request"); @@ -97,9 +101,6 @@ bool Client::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t case TunnelHeader::TYPE_RESET_CONNECTION: syslog(LOG_DEBUG, "reset reveiced"); - if (privilegesDropped) - throw Exception("cannot reconnect without root privileges"); - sendConnectionRequest(); return true; case TunnelHeader::TYPE_SERVER_FULL: @@ -128,7 +129,15 @@ bool Client::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t syslog(LOG_INFO, "connection established"); uint32_t ip = ntohl(*(uint32_t *)echoReceivePayloadBuffer()); - tun->setIp(ip, (ip & 0xffffff00) + 1, false); + if (ip != clientIp) + { + if (privilegesDropped) + throw Exception("could not get the same ip address, so root privileges are required to change it"); + + clientIp = ip; + desiredIp = ip; + tun->setIp(ip, (ip & 0xffffff00) + 1, false); + } state = STATE_ESTABLISHED; dropPrivileges(); diff --git a/client.h b/client.h index 5b2b3f1..beee329 100644 --- a/client.h +++ b/client.h @@ -28,8 +28,9 @@ class Client : public Worker { public: - Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, int maxPolls, - const char *passphrase, uid_t uid, gid_t gid, bool changeEchoId, bool changeEchoSeq); + Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, + int maxPolls, const char *passphrase, uid_t uid, gid_t gid, + bool changeEchoId, bool changeEchoSeq, uint32_t desiredIp); virtual ~Client(); virtual void run(); @@ -59,6 +60,8 @@ protected: Auth auth; uint32_t serverIp; + uint32_t clientIp; + uint32_t desiredIp; int maxPolls; int pollTimeoutNr; diff --git a/echo.cpp b/echo.cpp index 5ae9173..ec8fd94 100644 --- a/echo.cpp +++ b/echo.cpp @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include typedef ip IpHeader; @@ -72,7 +75,7 @@ void Echo::send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uin int result = sendto(fd, sendBuffer + sizeof(IpHeader), payloadLength + sizeof(EchoHeader), 0, (struct sockaddr *)&target, sizeof(struct sockaddr_in)); if (result == -1) - throw Exception("sendto", true); + syslog(LOG_ERR, "error sending icmp packet: %s", strerror(errno)); } int Echo::receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq) @@ -82,7 +85,10 @@ int Echo::receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq) int dataLength = recvfrom(fd, receiveBuffer, bufferSize, 0, (struct sockaddr *)&source, (socklen_t *)&source_addr_len); if (dataLength == -1) - throw Exception("recvfrom", true); + { + syslog(LOG_ERR, "error receiving icmp packet: %s", strerror(errno)); + return -1; + } if (dataLength < sizeof(IpHeader) + sizeof(EchoHeader)) return -1; diff --git a/main.cpp b/main.cpp index ca38256..242bef0 100644 --- a/main.cpp +++ b/main.cpp @@ -36,7 +36,7 @@ void usage() printf( "Hans - IP over ICMP version 0.3.1\n\n" "RUN AS SERVER\n" - " hans -s network [-fvr] [-p password] [-u unprivileged_user] [-d tun_device] [-m reference_mtu]\n\n" + " hans -s network [-fvr] [-p password] [-u unprivileged_user] [-d tun_device] [-m reference_mtu] [-a ip]\n\n" "RUN AS CLIENT\n" " hans -c server [-fv] [-p password] [-u unprivileged_user] [-d tun_device] [-m reference_mtu] [-w polls]\n\n" "ARGUMENTS\n" @@ -54,7 +54,8 @@ void usage() " -w polls Number of echo requests the client sends to the server for polling.\n" " 0 disables polling. Defaults to 10.\n" " -i Change the echo id for every echo request.\n" - " -q Change the echo sequence number for every echo request.\n" + " -q Change the echo sequence number for every echo request.\n" + " -a ip Try to get assigned the given tunnel ip address.\n" ); } @@ -70,6 +71,7 @@ int main(int argc, char *argv[]) int mtu = 1500; int maxPolls = 10; uint32_t network = INADDR_NONE; + uint32_t clientIp = INADDR_NONE; bool answerPing = false; uid_t uid = 0; gid_t gid = 0; @@ -80,7 +82,7 @@ int main(int argc, char *argv[]) openlog(argv[0], LOG_PERROR, LOG_DAEMON); int c; - while ((c = getopt(argc, argv, "fru:d:p:s:c:m:w:qiv")) != -1) + while ((c = getopt(argc, argv, "fru:d:p:s:c:m:w:qiva:")) != -1) { switch(c) { case 'f': @@ -124,6 +126,9 @@ int main(int argc, char *argv[]) case 'v': verbose = true; break; + case 'a': + clientIp = ntohl(inet_addr(optarg)); + break; default: usage(); return 1; @@ -190,7 +195,7 @@ int main(int argc, char *argv[]) serverIp = *(uint32_t *)he->h_addr; } - worker = new Client(mtu, device, ntohl(serverIp), maxPolls, password, uid, gid, changeEchoId, changeEchoSeq); + worker = new Client(mtu, device, ntohl(serverIp), maxPolls, password, uid, gid, changeEchoId, changeEchoSeq, clientIp); } if (!foreground) diff --git a/server.cpp b/server.cpp index a76e8e2..dce368f 100644 --- a/server.cpp +++ b/server.cpp @@ -28,6 +28,8 @@ using namespace std; +#define FIRST_ASSIGNED_IP_OFFSET 100 + const Worker::TunnelHeader::Magic Server::magic("hans"); Server::Server(int tunnelMtu, const char *deviceName, const char *passphrase, uint32_t network, bool answerEcho, uid_t uid, gid_t gid, int pollTimeout) @@ -35,6 +37,7 @@ Server::Server(int tunnelMtu, const char *deviceName, const char *passphrase, ui { this->network = network & 0xffffff00; this->pollTimeout = pollTimeout; + this->latestAssignedIpOffset = FIRST_ASSIGNED_IP_OFFSET - 1; tun->setIp(this->network + 1, this->network + 2, true); @@ -65,7 +68,7 @@ void Server::handleUnknownClient(const TunnelHeader &header, int dataLength, uin client.maxPolls = connectData->maxPolls; client.state = ClientData::STATE_NEW; - client.tunnelIp = reserveTunnelIp(); + client.tunnelIp = reserveTunnelIp(connectData->desiredIp); syslog(LOG_DEBUG, "new client: %s (%s)\n", Utility::formatIp(client.realIp).c_str(), Utility::formatIp(client.tunnelIp).c_str()); @@ -186,7 +189,6 @@ bool Server::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t if (dataLength == 0) { syslog(LOG_WARNING, "received empty data packet"); - throw 0; return true; } @@ -294,7 +296,7 @@ void Server::sendEchoToClient(ClientData *client, int type, int dataLength) void Server::releaseTunnelIp(uint32_t tunnelIp) { - usedIps.remove(tunnelIp); + usedIps.erase(tunnelIp); } void Server::handleTimeout() @@ -314,23 +316,34 @@ void Server::handleTimeout() setTimeout(KEEP_ALIVE_INTERVAL); } -uint32_t Server::reserveTunnelIp() +uint32_t Server::reserveTunnelIp(uint32_t desiredIp) { - uint32_t ip = network + 2; + if (desiredIp > network + 1 && desiredIp < network + 255 && !usedIps.count(desiredIp)) + { + usedIps.insert(desiredIp); + return desiredIp; + } - list::iterator i; - for (i = usedIps.begin(); i != usedIps.end(); ++i) - { - if (*i > ip) - break; - ip = ip + 1; - } + bool ipAvailable = false; - if (ip - network >= 255) + for (int i = 0; i < 255 - FIRST_ASSIGNED_IP_OFFSET; i++) + { + latestAssignedIpOffset++; + if (latestAssignedIpOffset == 255) + latestAssignedIpOffset = FIRST_ASSIGNED_IP_OFFSET; + + if (!usedIps.count(network + latestAssignedIpOffset)) + { + ipAvailable = true; + break; + } + } + + if (!ipAvailable) return 0; - usedIps.insert(i, ip); - return ip; + usedIps.insert(network + latestAssignedIpOffset); + return network + latestAssignedIpOffset; } void Server::run() diff --git a/server.h b/server.h index 078f389..f374849 100644 --- a/server.h +++ b/server.h @@ -26,7 +26,7 @@ #include #include #include -#include +#include class Server : public Worker { @@ -37,6 +37,7 @@ public: struct ClientConnectData { uint8_t maxPolls; + uint32_t desiredIp; }; static const Worker::TunnelHeader::Magic magic; @@ -101,7 +102,7 @@ protected: void pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq); - uint32_t reserveTunnelIp(); + uint32_t reserveTunnelIp(uint32_t desiredIp); void releaseTunnelIp(uint32_t tunnelIp); ClientData *getClientByTunnelIp(uint32_t ip); @@ -110,7 +111,8 @@ protected: Auth auth; uint32_t network; - std::list usedIps; + std::set usedIps; + uint32_t latestAssignedIpOffset; Time pollTimeout; diff --git a/tun.cpp b/tun.cpp index b15c1e2..b5dd6d8 100644 --- a/tun.cpp +++ b/tun.cpp @@ -89,21 +89,14 @@ void Tun::setIp(uint32_t ip, uint32_t destIp, bool includeSubnet) void Tun::write(const char *buffer, int length) { if (tun_write(fd, (char *)buffer, length) == -1) - { - syslog(LOG_ERR, "error writing %d bytes to tun", length); - if (errno != EINVAL) // can be caused by invalid data packet - throw Exception("writing to tun", true); - } + syslog(LOG_ERR, "error writing %d bytes to tun: %s", length, strerror(errno)); } int Tun::read(char *buffer) { int length = tun_read(fd, buffer, mtu); if (length == -1) - { - syslog(LOG_ERR, "error reading from tun", length); - throw Exception("reading from tun", true); - } + syslog(LOG_ERR, "error reading from tun: %s", strerror(errno)); return length; } diff --git a/worker.cpp b/worker.cpp index 407dea7..d5be42b 100644 --- a/worker.cpp +++ b/worker.cpp @@ -175,7 +175,8 @@ void Worker::run() if (dataLength == 0) throw Exception("tunnel closed"); - handleTunData(dataLength, sourceIp, destIp); + if (dataLength != -1) + handleTunData(dataLength, sourceIp, destIp); } } }