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");
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();

View File

@ -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;

View File

@ -26,6 +26,9 @@
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>
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;

View File

@ -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)

View File

@ -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<uint32_t>::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()

View File

@ -26,7 +26,7 @@
#include <map>
#include <queue>
#include <vector>
#include <list>
#include <set>
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<uint32_t> usedIps;
std::set<uint32_t> usedIps;
uint32_t latestAssignedIpOffset;
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)
{
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;
}

View File

@ -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);
}
}
}