finished polling system change

This commit is contained in:
Friedrich Schöller 2009-07-24 03:28:21 +02:00
parent 63209c41df
commit e8d388515f
11 changed files with 117 additions and 62 deletions

View File

@ -58,7 +58,7 @@ server.o: server.cpp server.h client.h utility.h config.h worker.h auth.h time.h
auth.o: auth.cpp auth.h sha1.h utility.h
g++ -c auth.cpp $(CFLAGS)
worker.o: worker.cpp worker.h tun.h exception.h time.h echo.h tun_dev.h
worker.o: worker.cpp worker.h tun.h exception.h time.h echo.h tun_dev.h config.h
g++ -c worker.cpp $(CFLAGS)
time.o: time.cpp time.h

View File

@ -31,12 +31,16 @@ using namespace std;
const Worker::TunnelHeader::Magic Client::magic("9967");
Client::Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, int maxPolls, const char *passphrase, uid_t uid, gid_t gid)
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)
{
this->serverIp = serverIp;
this->maxPolls = maxPolls;
this->nextEchoId = Utility::rand();
this->changeEchoId = changeEchoId;
this->changeEchoSeq = changeEchoSeq;
this->nextEchoSequence = Utility::rand();
state = STATE_CLOSED;
}
@ -48,7 +52,7 @@ Client::~Client()
void Client::sendConnectionRequest()
{
Server::ClientConnectData *connectData = (Server::ClientConnectData *)payloadBuffer();
Server::ClientConnectData *connectData = (Server::ClientConnectData *)echoSendPayloadBuffer();
connectData->maxPolls = maxPolls;
syslog(LOG_DEBUG, "sending connection request");
@ -70,11 +74,11 @@ void Client::sendChallengeResponse(int dataLength)
vector<char> challenge;
challenge.resize(dataLength);
memcpy(&challenge[0], payloadBuffer(), dataLength);
memcpy(&challenge[0], echoReceivePayloadBuffer(), dataLength);
Auth::Response response = auth.getResponse(challenge);
memcpy(payloadBuffer(), (char *)&response, sizeof(Auth::Response));
memcpy(echoSendPayloadBuffer(), (char *)&response, sizeof(Auth::Response));
sendEchoToServer(TunnelHeader::TYPE_CHALLENGE_RESPONSE, sizeof(Auth::Response));
setTimeout(5000);
@ -123,7 +127,7 @@ bool Client::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t
syslog(LOG_INFO, "connection established");
tun->setIp(ntohl(*(uint32_t *)payloadBuffer()));
tun->setIp(ntohl(*(uint32_t *)echoReceivePayloadBuffer()));
state = STATE_ESTABLISHED;
dropPrivileges();
@ -157,10 +161,12 @@ void Client::sendEchoToServer(int type, int dataLength)
if (maxPolls == 0 && state == STATE_ESTABLISHED)
setTimeout(KEEP_ALIVE_INTERVAL);
sendEcho(magic, type, dataLength, serverIp, false, nextEchoId, 0);
sendEcho(magic, type, dataLength, serverIp, false, nextEchoId, nextEchoSequence);
if (maxPolls > 0)
if (changeEchoId)
nextEchoId = nextEchoId + 38543; // some random prime
if (changeEchoSeq)
nextEchoSequence = nextEchoSequence + 38543; // some random prime
}
void Client::startPolling()
@ -179,6 +185,12 @@ void Client::startPolling()
void Client::handleDataFromServer(int dataLength)
{
if (dataLength == 0)
{
syslog(LOG_WARNING, "received empty data packet");
return;
}
sendToTun(dataLength);
if (maxPolls != 0)

View File

@ -28,7 +28,8 @@
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);
Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, int maxPolls,
const char *passphrase, uid_t uid, gid_t gid, bool changeEchoId, bool changeEchoSeq);
virtual ~Client();
virtual void run();
@ -62,7 +63,10 @@ protected:
int maxPolls;
int pollTimeoutNr;
bool changeEchoId, changeEchoSeq;
uint16_t nextEchoId;
uint16_t nextEchoSequence;
State state;
};

View File

@ -23,3 +23,6 @@
#define POLL_INTERVAL 2000
#define CHALLENGE_SIZE 20
//#define DEBUG_ONLY(a) a
#define DEBUG_ONLY(a)

View File

@ -31,13 +31,16 @@ Echo::Echo(int maxPayloadSize)
throw Exception("creating icmp socket", true);
bufferSize = maxPayloadSize + headerSize();
buffer = new char[bufferSize];
sendBuffer = new char[bufferSize];
receiveBuffer = new char[bufferSize];
}
Echo::~Echo()
{
close(fd);
delete[] buffer;
delete[] sendBuffer;
delete[] receiveBuffer;
}
int Echo::headerSize()
@ -54,15 +57,15 @@ void Echo::send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uin
if (payloadLength + sizeof(IpHeader) + sizeof(EchoHeader) > bufferSize)
throw Exception("packet too big");
EchoHeader *header = (EchoHeader *)(buffer + sizeof(IpHeader));
EchoHeader *header = (EchoHeader *)(sendBuffer + sizeof(IpHeader));
header->type = reply ? 0: 8;
header->code = 0;
header->id = htons(id);
header->seq = htons(seq);
header->chksum = 0;
header->chksum = icmpChecksum(buffer + sizeof(IpHeader), payloadLength + sizeof(EchoHeader));
header->chksum = icmpChecksum(sendBuffer + sizeof(IpHeader), payloadLength + sizeof(EchoHeader));
int result = sendto(fd, buffer + 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)
throw Exception("sendto", true);
}
@ -72,14 +75,14 @@ int Echo::receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq)
struct sockaddr_in source;
int source_addr_len = sizeof(struct sockaddr_in);
int dataLength = recvfrom(fd, buffer, 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)
throw Exception("recvfrom", true);
if (dataLength < sizeof(IpHeader) + sizeof(EchoHeader))
return -1;
EchoHeader *header = (EchoHeader *)(buffer + sizeof(IpHeader));
EchoHeader *header = (EchoHeader *)(receiveBuffer + sizeof(IpHeader));
if ((header->type != 0 && header->type != 8) || header->code != 0)
return -1;

5
echo.h
View File

@ -34,7 +34,8 @@ public:
void send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
int receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq);
char *payloadBuffer() { return buffer + headerSize(); }
char *sendPayloadBuffer() { return sendBuffer + headerSize(); }
char *receivePayloadBuffer() { return receiveBuffer + headerSize(); }
static int headerSize();
protected:
@ -51,7 +52,7 @@ protected:
int fd;
int bufferSize;
char *buffer;
char *sendBuffer, *receiveBuffer;
};
#endif

View File

@ -43,20 +43,17 @@ void usage()
" -c server Connect to a server.\n"
" -f Run in foreground.\n"
" -r Respond to ordinary pings. Only in server mode.\n"
" Use this when you disable echo replies of your operating system, which is a good idea.\n"
" -p password Use a password.\n"
" -u username Set the user under which the program should run.\n"
" -d device Use the given tun device.\n"
" -m mtu Use this mtu to calculate the tunnel mtu.\n"
" The generated ICMP packets will not be bigger than this value.\n"
" Has to be the same on client and server.\n"
" In most cases you don't want to set this. Defaults to 1500.\n"
" Has to be the same on client and server. Defaults to 1500.\n"
" -w polls Number of echo requests the client sends to the server for polling.\n"
" If your network allows unlimited echo replies set this to 0 to disable polling.\n"
" The default value of 10 is regarded as pretty high.\n"
" Set this to a lower value if you experience packet loss through the tunnel.\n"
" Set this to 1 in extreme cases, when your network allows only one echo reply per request.\n"
" A low value will decrease the performance of the tunnel.\n");
" 0 disables polling. Defaults to 10.\n"
" -i Change the echo id for every packet.\n"
" -q Change the echo sequence number for every packet.\n"
);
}
int main(int argc, char *argv[])
@ -74,11 +71,13 @@ int main(int argc, char *argv[])
bool answerPing = false;
uid_t uid = 0;
gid_t gid = 0;
bool changeEchoId = false;
bool changeEchoSeq = false;
openlog(argv[0], LOG_PERROR, LOG_DAEMON);
int c;
while ((c = getopt(argc, argv, "fru:d:p:s:c:m:w:")) != -1)
while ((c = getopt(argc, argv, "fru:d:p:s:c:m:w:qi")) != -1)
{
switch(c) {
case 'f':
@ -101,6 +100,8 @@ int main(int argc, char *argv[])
case 's':
isServer = true;
network = ntohl(inet_addr(optarg));
if (network == INADDR_NONE)
printf("invalid network\n");
break;
case 'm':
mtu = atoi(optarg);
@ -111,6 +112,12 @@ int main(int argc, char *argv[])
case 'r':
answerPing = true;
break;
case 'q':
changeEchoSeq = true;
break;
case 'i':
changeEchoId = true;
break;
default:
usage();
return 1;
@ -128,7 +135,8 @@ int main(int argc, char *argv[])
if ((isClient == isServer) ||
(isServer && network == INADDR_NONE) ||
(maxPolls < 0 || maxPolls > 255))
(maxPolls < 0 || maxPolls > 255) ||
(isServer && (changeEchoSeq || changeEchoId)))
{
usage();
return 1;
@ -173,7 +181,7 @@ int main(int argc, char *argv[])
serverIp = *(uint32_t *)he->h_addr;
}
worker = new Client(mtu, device, ntohl(serverIp), maxPolls, password, uid, gid);
worker = new Client(mtu, device, ntohl(serverIp), maxPolls, password, uid, gid, changeEchoId, changeEchoSeq);
}
if (!foreground)

View File

@ -28,9 +28,6 @@
using namespace std;
//#define DEBUG_ONLY(a) a
#define DEBUG_ONLY(a)
const Worker::TunnelHeader::Magic Server::magic("9973");
Server::Server(int tunnelMtu, const char *deviceName, const char *passphrase, uint32_t network, bool answerEcho, uid_t uid, gid_t gid, int pollTimeout)
@ -49,13 +46,13 @@ Server::~Server()
}
void Server::handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId)
void Server::handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId, uint16_t echoSeq)
{
ClientData client;
client.realIp = realIp;
client.maxPolls = 1;
pollReceived(&client, echoId);
pollReceived(&client, echoId, echoSeq);
if (header.type != TunnelHeader::TYPE_CONNECTION_REQUEST || dataLength != sizeof(ClientConnectData))
{
@ -64,7 +61,7 @@ void Server::handleUnknownClient(const TunnelHeader &header, int dataLength, uin
return;
}
ClientConnectData *connectData = (ClientConnectData *)payloadBuffer();
ClientConnectData *connectData = (ClientConnectData *)echoReceivePayloadBuffer();
client.maxPolls = connectData->maxPolls;
client.state = ClientData::STATE_NEW;
@ -93,7 +90,7 @@ void Server::sendChallenge(ClientData *client)
{
syslog(LOG_DEBUG, "sending challenge to: %s\n", Utility::formatIp(client->realIp).c_str());
memcpy(payloadBuffer(), &client->challenge[0], client->challenge.size());
memcpy(echoSendPayloadBuffer(), &client->challenge[0], client->challenge.size());
sendEchoToClient(client, TunnelHeader::TYPE_CHALLENGE, client->challenge.size());
client->state = ClientData::STATE_CHALLENGE_SENT;
@ -117,7 +114,7 @@ void Server::checkChallenge(ClientData *client, int length)
{
Auth::Response rightResponse = auth.getResponse(client->challenge);
if (length != sizeof(Auth::Response) || memcmp(&rightResponse, payloadBuffer(), length) != 0)
if (length != sizeof(Auth::Response) || memcmp(&rightResponse, echoReceivePayloadBuffer(), length) != 0)
{
syslog(LOG_DEBUG, "wrong challenge response\n");
@ -127,7 +124,7 @@ void Server::checkChallenge(ClientData *client, int length)
return;
}
uint32_t *ip = (uint32_t *)payloadBuffer();
uint32_t *ip = (uint32_t *)echoSendPayloadBuffer();
*ip = htonl(client->tunnelIp);
sendEchoToClient(client, TunnelHeader::TYPE_CONNECTION_ACCEPT, sizeof(uint32_t));
@ -154,11 +151,11 @@ bool Server::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t
ClientData *client = getClientByRealIp(realIp);
if (client == NULL)
{
handleUnknownClient(header, dataLength, realIp, id);
handleUnknownClient(header, dataLength, realIp, id, seq);
return true;
}
pollReceived(client, id);
pollReceived(client, id, seq);
switch (header.type)
{
@ -186,6 +183,13 @@ bool Server::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t
case TunnelHeader::TYPE_DATA:
if (client->state == ClientData::STATE_ESTABLISHED)
{
if (dataLength == 0)
{
syslog(LOG_WARNING, "received empty data packet");
throw 0;
return true;
}
sendToTun(dataLength);
return true;
}
@ -230,19 +234,19 @@ void Server::handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp)
sendEchoToClient(client, TunnelHeader::TYPE_DATA, dataLength);
}
void Server::pollReceived(ClientData *client, uint16_t echoId)
void Server::pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq)
{
unsigned int maxSavedPolls = client->maxPolls != 0 ? client->maxPolls : 1;
client->pollIds.push(echoId);
client->pollIds.push(ClientData::EchoId(echoId, echoSeq));
if (client->pollIds.size() > maxSavedPolls)
client->pollIds.pop();
DEBUG_ONLY(printf("poll (%d) -> %d\n", echoId, client->pollIds.size()));
DEBUG_ONLY(printf("poll -> %d\n", client->pollIds.size()));
if (client->pendingPackets.size() > 0)
{
Packet &packet = client->pendingPackets.front();
memcpy(payloadBuffer(), &packet.data[0], packet.data.size());
memcpy(echoSendPayloadBuffer(), &packet.data[0], packet.data.size());
client->pendingPackets.pop();
DEBUG_ONLY(printf("pending packet: %d bytes\n", packet.data.size()));
@ -256,22 +260,20 @@ void Server::sendEchoToClient(ClientData *client, int type, int dataLength)
{
if (client->maxPolls == 0)
{
sendEcho(magic, type, dataLength, client->realIp, true, client->pollIds.front(), 0);
sendEcho(magic, type, dataLength, client->realIp, true, client->pollIds.front().id, client->pollIds.front().seq);
return;
}
if (client->pollIds.size() != 0)
{
uint16_t id = client->pollIds.front();
ClientData::EchoId echoId = client->pollIds.front();
client->pollIds.pop();
DEBUG_ONLY(printf("sending (%d) -> %d\n", id, client->pollIds.size()));
sendEcho(magic, type, dataLength, client->realIp, true, id, 0);
DEBUG_ONLY(printf("sending -> %d\n", client->pollIds.size()));
sendEcho(magic, type, dataLength, client->realIp, true, echoId.id, echoId.seq);
return;
}
DEBUG_ONLY(printf("queuing -> %d\n", client->pollIds.size()));
if (client->pendingPackets.size() == MAX_BUFFERED_PACKETS)
{
client->pendingPackets.pop();
@ -284,7 +286,7 @@ void Server::sendEchoToClient(ClientData *client, int type, int dataLength)
Packet &packet = client->pendingPackets.back();
packet.type = type;
packet.data.resize(dataLength);
memcpy(&packet.data[0], payloadBuffer(), dataLength);
memcpy(&packet.data[0], echoReceivePayloadBuffer(), dataLength);
}
void Server::releaseTunnelIp(uint32_t tunnelIp)

View File

@ -57,13 +57,21 @@ protected:
STATE_ESTABLISHED
};
struct EchoId
{
EchoId(uint16_t id, uint16_t seq) { this->id = id; this->seq = seq; }
uint16_t id;
uint16_t seq;
};
uint32_t realIp;
uint32_t tunnelIp;
std::queue<Packet> pendingPackets;
int maxPolls;
std::queue<uint16_t> pollIds;
std::queue<EchoId> pollIds;
Time lastActivity;
State state;
@ -82,7 +90,7 @@ protected:
void serveTun(ClientData *client);
void handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId);
void handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId, uint16_t echoSeq);
void removeClient(ClientData *client);
void sendChallenge(ClientData *client);
@ -91,7 +99,7 @@ protected:
void sendEchoToClient(ClientData *client, int type, int dataLength);
void pollReceived(ClientData *client, uint16_t echoId);
void pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq);
uint32_t reserveTunnelIp();
void releaseTunnelIp(uint32_t tunnelIp);

View File

@ -20,6 +20,7 @@
#include "worker.h"
#include "tun.h"
#include "exception.h"
#include "config.h"
#include <string.h>
#include <syslog.h>
@ -78,16 +79,18 @@ void Worker::sendEcho(const TunnelHeader::Magic &magic, int type, int length, ui
if (length > payloadBufferSize())
throw Exception("packet too big");
TunnelHeader *header = (TunnelHeader *)echo->payloadBuffer();
TunnelHeader *header = (TunnelHeader *)echo->sendPayloadBuffer();
header->magic = magic;
header->type = type;
DEBUG_ONLY(printf("sending: type %d, length %d, id %d, seq %d\n", type, length, id, seq));
echo->send(length + sizeof(TunnelHeader), realIp, reply, id, seq);
}
void Worker::sendToTun(int length)
{
tun->write(payloadBuffer(), length);
tun->write(echoReceivePayloadBuffer(), length);
}
void Worker::setTimeout(Time delta)
@ -145,10 +148,19 @@ void Worker::run()
bool valid = dataLength >= sizeof(TunnelHeader);
if (valid)
valid = handleEchoData(*(TunnelHeader *)(echo->payloadBuffer()), dataLength - sizeof(TunnelHeader), ip, reply, id, seq);
{
TunnelHeader header = *(TunnelHeader *)echo->receivePayloadBuffer(); // make a copy!
DEBUG_ONLY(printf("received: type %d, length %d, id %d, seq %d\n", header.type, dataLength - sizeof(TunnelHeader), id, seq));
valid = handleEchoData(header, dataLength - sizeof(TunnelHeader), ip, reply, id, seq);
}
if (!valid && !reply && answerEcho)
{
memcpy(echo->sendPayloadBuffer(), echo->receivePayloadBuffer(), dataLength);
echo->send(dataLength, ip, true, id, seq);
}
}
}
@ -157,7 +169,7 @@ void Worker::run()
{
uint32_t sourceIp, destIp;
int dataLength = tun->read(payloadBuffer(), sourceIp, destIp);
int dataLength = tun->read(echoSendPayloadBuffer(), sourceIp, destIp);
if (dataLength == 0)
throw Exception("tunnel closed");

View File

@ -68,15 +68,17 @@ protected:
}; // size = 5
virtual bool handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq) { return true; }
virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp) { }
virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp) { } // to echoSendPayloadBuffer
virtual void handleTimeout() { }
void sendEcho(const TunnelHeader::Magic &magic, int type, int length, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
void sendToTun(int length);
void sendToTun(int length); // from echoReceivePayloadBuffer
void setTimeout(Time delta);
char *payloadBuffer() { return echo->payloadBuffer() + sizeof(TunnelHeader); }
char *echoSendPayloadBuffer() { return echo->sendPayloadBuffer() + sizeof(TunnelHeader); }
char *echoReceivePayloadBuffer() { return echo->receivePayloadBuffer() + sizeof(TunnelHeader); }
int payloadBufferSize() { return tunnelMtu; }
void dropPrivileges();