improved ip assignment

more stable error handling
This commit is contained in:
Friedrich Schöller 2009-12-12 13:46:31 +01:00
parent 6f211f8ee9
commit 98327991f1
8 changed files with 76 additions and 44 deletions

View File

@ -31,11 +31,14 @@ using namespace std;
const Worker::TunnelHeader::Magic Client::magic("hanc"); const Worker::TunnelHeader::Magic Client::magic("hanc");
Client::Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, int maxPolls, Client::Client(int tunnelMtu, const char *deviceName, uint32_t serverIp,
const char *passphrase, uid_t uid, gid_t gid, bool changeEchoId, bool changeEchoSeq) 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) : Worker(tunnelMtu, deviceName, false, uid, gid), auth(passphrase)
{ {
this->serverIp = serverIp; this->serverIp = serverIp;
this->clientIp = INADDR_NONE;
this->desiredIp = desiredIp;
this->maxPolls = maxPolls; this->maxPolls = maxPolls;
this->nextEchoId = Utility::rand(); this->nextEchoId = Utility::rand();
this->changeEchoId = changeEchoId; this->changeEchoId = changeEchoId;
@ -54,6 +57,7 @@ void Client::sendConnectionRequest()
{ {
Server::ClientConnectData *connectData = (Server::ClientConnectData *)echoSendPayloadBuffer(); Server::ClientConnectData *connectData = (Server::ClientConnectData *)echoSendPayloadBuffer();
connectData->maxPolls = maxPolls; connectData->maxPolls = maxPolls;
connectData->desiredIp = desiredIp;
syslog(LOG_DEBUG, "sending connection request"); 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: case TunnelHeader::TYPE_RESET_CONNECTION:
syslog(LOG_DEBUG, "reset reveiced"); syslog(LOG_DEBUG, "reset reveiced");
if (privilegesDropped)
throw Exception("cannot reconnect without root privileges");
sendConnectionRequest(); sendConnectionRequest();
return true; return true;
case TunnelHeader::TYPE_SERVER_FULL: case TunnelHeader::TYPE_SERVER_FULL:
@ -128,7 +129,15 @@ bool Client::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t
syslog(LOG_INFO, "connection established"); syslog(LOG_INFO, "connection established");
uint32_t ip = ntohl(*(uint32_t *)echoReceivePayloadBuffer()); uint32_t ip = ntohl(*(uint32_t *)echoReceivePayloadBuffer());
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); tun->setIp(ip, (ip & 0xffffff00) + 1, false);
}
state = STATE_ESTABLISHED; state = STATE_ESTABLISHED;
dropPrivileges(); dropPrivileges();

View File

@ -28,8 +28,9 @@
class Client : public Worker class Client : public Worker
{ {
public: public:
Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, int maxPolls, Client(int tunnelMtu, const char *deviceName, uint32_t serverIp,
const char *passphrase, uid_t uid, gid_t gid, bool changeEchoId, bool changeEchoSeq); int maxPolls, const char *passphrase, uid_t uid, gid_t gid,
bool changeEchoId, bool changeEchoSeq, uint32_t desiredIp);
virtual ~Client(); virtual ~Client();
virtual void run(); virtual void run();
@ -59,6 +60,8 @@ protected:
Auth auth; Auth auth;
uint32_t serverIp; uint32_t serverIp;
uint32_t clientIp;
uint32_t desiredIp;
int maxPolls; int maxPolls;
int pollTimeoutNr; int pollTimeoutNr;

View File

@ -26,6 +26,9 @@
#include <netinet/ip.h> #include <netinet/ip.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>
typedef ip IpHeader; 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)); int result = sendto(fd, sendBuffer + sizeof(IpHeader), payloadLength + sizeof(EchoHeader), 0, (struct sockaddr *)&target, sizeof(struct sockaddr_in));
if (result == -1) 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) 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); int dataLength = recvfrom(fd, receiveBuffer, bufferSize, 0, (struct sockaddr *)&source, (socklen_t *)&source_addr_len);
if (dataLength == -1) 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)) if (dataLength < sizeof(IpHeader) + sizeof(EchoHeader))
return -1; return -1;

View File

@ -36,7 +36,7 @@ void usage()
printf( printf(
"Hans - IP over ICMP version 0.3.1\n\n" "Hans - IP over ICMP version 0.3.1\n\n"
"RUN AS SERVER\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" "RUN AS CLIENT\n"
" hans -c server [-fv] [-p password] [-u unprivileged_user] [-d tun_device] [-m reference_mtu] [-w polls]\n\n" " hans -c server [-fv] [-p password] [-u unprivileged_user] [-d tun_device] [-m reference_mtu] [-w polls]\n\n"
"ARGUMENTS\n" "ARGUMENTS\n"
@ -55,6 +55,7 @@ void usage()
" 0 disables polling. Defaults to 10.\n" " 0 disables polling. Defaults to 10.\n"
" -i Change the echo id for every echo request.\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 mtu = 1500;
int maxPolls = 10; int maxPolls = 10;
uint32_t network = INADDR_NONE; uint32_t network = INADDR_NONE;
uint32_t clientIp = INADDR_NONE;
bool answerPing = false; bool answerPing = false;
uid_t uid = 0; uid_t uid = 0;
gid_t gid = 0; gid_t gid = 0;
@ -80,7 +82,7 @@ int main(int argc, char *argv[])
openlog(argv[0], LOG_PERROR, LOG_DAEMON); openlog(argv[0], LOG_PERROR, LOG_DAEMON);
int c; 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) { switch(c) {
case 'f': case 'f':
@ -124,6 +126,9 @@ int main(int argc, char *argv[])
case 'v': case 'v':
verbose = true; verbose = true;
break; break;
case 'a':
clientIp = ntohl(inet_addr(optarg));
break;
default: default:
usage(); usage();
return 1; return 1;
@ -190,7 +195,7 @@ int main(int argc, char *argv[])
serverIp = *(uint32_t *)he->h_addr; 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) if (!foreground)

View File

@ -28,6 +28,8 @@
using namespace std; using namespace std;
#define FIRST_ASSIGNED_IP_OFFSET 100
const Worker::TunnelHeader::Magic Server::magic("hans"); 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) 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->network = network & 0xffffff00;
this->pollTimeout = pollTimeout; this->pollTimeout = pollTimeout;
this->latestAssignedIpOffset = FIRST_ASSIGNED_IP_OFFSET - 1;
tun->setIp(this->network + 1, this->network + 2, true); 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.maxPolls = connectData->maxPolls;
client.state = ClientData::STATE_NEW; 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()); 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) if (dataLength == 0)
{ {
syslog(LOG_WARNING, "received empty data packet"); syslog(LOG_WARNING, "received empty data packet");
throw 0;
return true; return true;
} }
@ -294,7 +296,7 @@ void Server::sendEchoToClient(ClientData *client, int type, int dataLength)
void Server::releaseTunnelIp(uint32_t tunnelIp) void Server::releaseTunnelIp(uint32_t tunnelIp)
{ {
usedIps.remove(tunnelIp); usedIps.erase(tunnelIp);
} }
void Server::handleTimeout() void Server::handleTimeout()
@ -314,23 +316,34 @@ void Server::handleTimeout()
setTimeout(KEEP_ALIVE_INTERVAL); 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))
list<uint32_t>::iterator i;
for (i = usedIps.begin(); i != usedIps.end(); ++i)
{ {
if (*i > ip) usedIps.insert(desiredIp);
break; return desiredIp;
ip = ip + 1;
} }
if (ip - network >= 255) bool ipAvailable = false;
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; return 0;
usedIps.insert(i, ip); usedIps.insert(network + latestAssignedIpOffset);
return ip; return network + latestAssignedIpOffset;
} }
void Server::run() void Server::run()

View File

@ -26,7 +26,7 @@
#include <map> #include <map>
#include <queue> #include <queue>
#include <vector> #include <vector>
#include <list> #include <set>
class Server : public Worker class Server : public Worker
{ {
@ -37,6 +37,7 @@ public:
struct ClientConnectData struct ClientConnectData
{ {
uint8_t maxPolls; uint8_t maxPolls;
uint32_t desiredIp;
}; };
static const Worker::TunnelHeader::Magic magic; static const Worker::TunnelHeader::Magic magic;
@ -101,7 +102,7 @@ protected:
void pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq); void pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq);
uint32_t reserveTunnelIp(); uint32_t reserveTunnelIp(uint32_t desiredIp);
void releaseTunnelIp(uint32_t tunnelIp); void releaseTunnelIp(uint32_t tunnelIp);
ClientData *getClientByTunnelIp(uint32_t ip); ClientData *getClientByTunnelIp(uint32_t ip);
@ -110,7 +111,8 @@ protected:
Auth auth; Auth auth;
uint32_t network; uint32_t network;
std::list<uint32_t> usedIps; std::set<uint32_t> usedIps;
uint32_t latestAssignedIpOffset;
Time pollTimeout; Time pollTimeout;

11
tun.cpp
View File

@ -89,21 +89,14 @@ void Tun::setIp(uint32_t ip, uint32_t destIp, bool includeSubnet)
void Tun::write(const char *buffer, int length) void Tun::write(const char *buffer, int length)
{ {
if (tun_write(fd, (char *)buffer, length) == -1) if (tun_write(fd, (char *)buffer, length) == -1)
{ syslog(LOG_ERR, "error writing %d bytes to tun: %s", length, strerror(errno));
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);
}
} }
int Tun::read(char *buffer) int Tun::read(char *buffer)
{ {
int length = tun_read(fd, buffer, mtu); int length = tun_read(fd, buffer, mtu);
if (length == -1) if (length == -1)
{ syslog(LOG_ERR, "error reading from tun: %s", strerror(errno));
syslog(LOG_ERR, "error reading from tun", length);
throw Exception("reading from tun", true);
}
return length; return length;
} }

View File

@ -175,6 +175,7 @@ void Worker::run()
if (dataLength == 0) if (dataLength == 0)
throw Exception("tunnel closed"); throw Exception("tunnel closed");
if (dataLength != -1)
handleTunData(dataLength, sourceIp, destIp); handleTunData(dataLength, sourceIp, destIp);
} }
} }