Commit 0834f1f1 authored by Jonathon Duerig's avatar Jonathon Duerig

Added tcp options parsing and replay capabilities. It now compiles. Now to test it.

parent ed452a17
......@@ -18,8 +18,12 @@ namespace
unsigned char const * packet);
void handleTcp(struct pcap_pkthdr const * pcapInfo,
struct ip const * ipPacket,
struct tcphdr const * tcpPacket);
void handleKernel(Connection * conn, struct tcp_info * kernel);
struct tcphdr const * tcpPacket,
unsigned char const * tcpPacketStart,
list<Option> & ipOptions);
bool handleKernel(Connection * conn, struct tcp_info * kernel);
void parseOptions(unsigned char const * buffer, int size,
list<Option> * options);
}
pcap_t * KernelTcp::pcapDescriptor = NULL;
......@@ -438,11 +442,18 @@ namespace
logWrite(ERROR, "A non TCP packet was captured");
return;
}
list<Option> ipOptions;
unsigned char const * optionsBegin = packet + sizeof(struct ether_header)
+ sizeof(struct ip);
int optionsSize = ipHeaderLength*4 - sizeof(struct ip);
parseOptions(optionsBegin, optionsSize, &ipOptions);
// ipHeaderLength is multiplied by 4 because it is a
// length in 4-byte words.
tcpPacket = reinterpret_cast<struct tcphdr const *>
(packet + sizeof(struct ether_header)
+ ipHeaderLength*4);
unsigned char const * tcpPacketStart = packet + sizeof(struct ether_header)
+ ipHeaderLength*4;
tcpPacket = reinterpret_cast<struct tcphdr const *>(tcpPacketStart);
bytesRemaining -= ipHeaderLength*4;
if (bytesRemaining < sizeof(struct tcphdr))
{
......@@ -450,7 +461,7 @@ namespace
"a TCP header");
return;
}
handleTcp(pcapInfo, ipPacket, tcpPacket);
handleTcp(pcapInfo, ipPacket, tcpPacket, tcpPacketStart, ipOptions);
}
int getLinkLayer(struct pcap_pkthdr const * pcapInfo,
......@@ -473,7 +484,9 @@ namespace
void handleTcp(struct pcap_pkthdr const * pcapInfo,
struct ip const * ipPacket,
struct tcphdr const * tcpPacket)
struct tcphdr const * tcpPacket,
unsigned char const * tcpPacketStart,
list<Option> & ipOptions)
{
logWrite(PCAP, "Captured a TCP packet");
struct tcp_info kernelInfo;
......@@ -486,12 +499,21 @@ namespace
{
isAck = false;
}
list<Option> tcpOptions;
unsigned char const * tcpOptionsBegin = tcpPacketStart
+ sizeof(struct tcphdr);
int tcpOptionsSize = tcpPacket->doff*4 - sizeof(struct tcphdr);
parseOptions(tcpOptionsBegin, tcpOptionsSize, &tcpOptions);
PacketInfo packet;
packet.packetTime = Time(pcapInfo->ts);
packet.packetLength = pcapInfo->len;
packet.kernel = &kernelInfo;
packet.ip = ipPacket;
packet.ipOptions = &ipOptions;
packet.tcp = tcpPacket;
packet.tcpOptions = &tcpOptions;
Order key;
// Assume that this is an outgoing packet.
......@@ -502,15 +524,12 @@ namespace
map<Order, Connection *>::iterator pos;
pos = global::planetMap.find(key);
if (pos != global::planetMap.end())
// If it is an outgoing packet, and it is sending original data
// (not an ack), and we can successfully fill in the kernel data
if (pos != global::planetMap.end() && !isAck
&& handleKernel(pos->second, &kernelInfo))
{
// This is an outgoing packet.
if (!isAck)
{
// We only care about sent packets, not acked packets.
handleKernel(pos->second, &kernelInfo);
pos->second->captureSend(&packet);
}
pos->second->captureSend(&packet);
}
else
{
......@@ -521,21 +540,19 @@ namespace
key.remotePort = ntohs(tcpPacket->source);
pos = global::planetMap.find(key);
if (pos != global::planetMap.end())
// If this is an incoming packet, and it is an ack packet, and
// we can successfully fill in the kernel data
if (pos != global::planetMap.end() && isAck
&& handleKernel(pos->second, &kernelInfo))
{
// This is an incoming packet.
if (isAck)
{
// We only care about ack packets, not sent packets.
handleKernel(pos->second, &kernelInfo);
pos->second->captureAck(&packet);
}
pos->second->captureAck(&packet);
}
}
}
void handleKernel(Connection * conn, struct tcp_info * kernel)
bool handleKernel(Connection * conn, struct tcp_info * kernel)
{
bool result = true;
// This is a filthy filthy hack. Basically, I need the fd in order
// to introspect the kernel for it. But I don't want that part of
// the main interface because we don't even know that a random
......@@ -552,6 +569,7 @@ namespace
{
logWrite(ERROR, "Failed to get the kernel TCP info: %s",
strerror(errno));
result = false;
}
}
else
......@@ -560,6 +578,55 @@ namespace
"ConnectionModel on the actual connection wasn't of type "
"KernelTcp. This inconsistency will lead to "
"undefined/uninitialized behaviour");
result = false;
}
return result;
}
void parseOptions(unsigned char const * buffer, int size,
list<Option> * options)
{
unsigned char const * pos;
unsigned char const * limit = buffer + size;
Option current;
bool done = false;
while (pos < limit && !done)
{
current.type = *pos;
++pos;
if (current.type == 0)
{
// This is the ending marker. We're done here.
current.length = 0;
current.buffer = NULL;
options->push_back(current);
done = true;
}
else if (current.type == 1)
{
// This is the padding no-op marker. There is no length field.
current.length = 0;
current.buffer = NULL;
options->push_back(current);
}
else
{
// This is some other code. There is a length field and length-2
// other bytes.
current.length = *pos - 2;
++pos;
if (current.length > 0)
{
current.buffer = pos;
pos += current.length;
}
else
{
current.buffer = NULL;
}
options->push_back(current);
}
}
}
}
......@@ -170,7 +170,7 @@ struct Option
Option() : type(0), length(0), buffer(NULL) {}
unsigned char type;
unsigned char length;
char * buffer;
unsigned char const * buffer;
};
struct PacketInfo
......
......@@ -370,7 +370,7 @@ void replayWritePacket(int command, PacketInfo * packet)
{
Header head;
head.type = command;
head.size = PacketInfo::size;
head.size = packet->census();
head.key = packet->elab;
char headBuffer[Header::headerSize];
saveHeader(headBuffer, head);
......@@ -378,9 +378,10 @@ void replayWritePacket(int command, PacketInfo * packet)
bool success = replayWrite(headBuffer, Header::headerSize);
if (success)
{
char packetBuffer[PacketInfo::size];
savePacket(packetBuffer, *packet);
replayWrite(packetBuffer, PacketInfo::size);
vector<char> packetBuffer;
packetBuffer.resize(head.size);
savePacket(& packetBuffer[0], *packet);
replayWrite(& packetBuffer[0], head.size);
}
}
......@@ -409,11 +410,12 @@ void replayLoop(void)
bool done = false;
char headerBuffer[Header::headerSize];
Header head;
char packetBuffer[(PacketInfo::size > sizeof(SensorCommand))
? PacketInfo::size : sizeof(SensorCommand)];
vector<char> packetBuffer;
struct tcp_info kernel;
struct ip ip;
list<Option> ipOptions;
struct tcphdr tcp;
list<Option> tcpOptions;
PacketInfo packet;
map<Order, SensorList> streams;
......@@ -421,6 +423,7 @@ void replayLoop(void)
while (!done)
{
loadHeader(headerBuffer, &head);
packetBuffer.resize(head.size);
switch(head.type)
{
case NEW_CONNECTION_COMMAND:
......@@ -430,11 +433,11 @@ void replayLoop(void)
streams.erase(head.key);
break;
case SENSOR_COMMAND:
done = ! replayRead(packetBuffer, sizeof(int));
done = ! replayRead(& packetBuffer[0], sizeof(int));
if (!done)
{
unsigned int sensorType = 0;
loadInt(packetBuffer, & sensorType);
loadInt(& packetBuffer[0], & sensorType);
SensorCommand sensor;
sensor.type = sensorType;
streams[head.key].addSensor(sensor);
......@@ -442,10 +445,11 @@ void replayLoop(void)
break;
case PACKET_INFO_SEND_COMMAND:
case PACKET_INFO_ACK_COMMAND:
done = ! replayRead(packetBuffer, PacketInfo::size);
done = ! replayRead(& packetBuffer[0], head.size);
if (!done)
{
loadPacket(packetBuffer, &packet, kernel, ip, tcp);
loadPacket(& packetBuffer[0], &packet, kernel, ip, tcp, ipOptions,
tcpOptions);
Sensor * sensorHead = streams[head.key].getHead();
if (sensorHead != NULL)
{
......
......@@ -72,14 +72,40 @@ char * saveHeader(char * buffer, Header const & value)
}
}
char * saveOptions(char * buffer, std::list<Option> const & options)
{
char * pos = buffer;
pos = saveInt(pos, options.size());
list<Option>::const_iterator current = options.begin();
list<Option>::const_iterator limit = options.end();
for (; current != limit; ++current)
{
pos = saveChar(pos, current->type);
pos = saveChar(pos, current->length);
if (current->length > 0)
{
memcpy(pos, current->buffer, current->length);
pos += current->length;
}
}
return pos;
}
char * savePacket(char * buffer, PacketInfo const & value)
{
char * pos = buffer;
struct tcp_info const * kernel = value.kernel;
// Save packet time
pos = saveInt(pos, value.packetTime.getTimeval()->tv_sec);
pos = saveInt(pos, value.packetTime.getTimeval()->tv_usec);
// Save packet length
pos = saveInt(pos, value.packetLength);
// Save tcp_info
pos = saveChar(pos, kernel->tcpi_state);
pos = saveChar(pos, kernel->tcpi_ca_state);
pos = saveChar(pos, kernel->tcpi_retransmits);
......@@ -115,17 +141,27 @@ char * savePacket(char * buffer, PacketInfo const & value)
pos = saveInt(pos, kernel->tcpi_advmss);
pos = saveInt(pos, kernel->tcpi_reordering);
// Save IP header
memcpy(pos, value.ip, sizeof(struct ip));
pos += sizeof(struct ip);
// Save IP options
pos = saveOptions(pos, * value.ipOptions);
// Save TCP header
memcpy(pos, value.tcp, sizeof(struct tcphdr));
pos += sizeof(struct tcphdr);
// Save TCP options
pos = saveOptions(pos, * value.tcpOptions);
// Save elab stuff
pos = saveChar(pos, value.elab.transport);
pos = saveInt(pos, value.elab.ip);
pos = saveShort(pos, value.elab.localPort);
pos = saveShort(pos, value.elab.remotePort);
// Save bufferFull measurement
unsigned char bufferFull = value.bufferFull;
pos = saveChar(pos, bufferFull);
......@@ -218,6 +254,36 @@ char * loadHeader(char * buffer, Header * value)
}
}
// Assumption: The input buffer will not be destroyed or changed
// before the list of optins is destroyed.
char * loadOptions(char * buffer, std::list<Option> * options)
{
char * pos = buffer;
Option current;
size_t i = 0;
size_t limit = 0;
pos = loadInt(pos, &limit);
for (i = 0; i < limit; ++i)
{
pos = loadChar(pos, & current.type);
pos = loadChar(pos, & current.length);
if (current.length > 0)
{
current.buffer = reinterpret_cast<unsigned char *>(pos);
pos += current.length;
}
else
{
current.buffer = NULL;
}
options->push_back(current);
}
return pos;
}
auto_ptr<Command> loadCommand(Header * head, char * body)
{
auto_ptr<Command> result;
......@@ -278,22 +344,30 @@ auto_ptr<Command> loadCommand(Header * head, char * body)
}
char * loadPacket(char * buffer, PacketInfo * value, struct tcp_info & kernel,
struct ip & ip, struct tcphdr & tcp)
struct ip & ip, struct tcphdr & tcp,
list<Option> & ipOptions, list<Option> & tcpOptions)
{
char * pos = buffer;
value->kernel = &kernel;
value->ip = &ip;
value->ipOptions = &ipOptions;
value->tcp = &tcp;
value->tcpOptions = &tcpOptions;
// Load packet time
unsigned int timeSeconds = 0;
pos = loadInt(pos, & timeSeconds);
value->packetTime.getTimeval()->tv_sec = timeSeconds;
unsigned int timeMicroseconds = 0;
pos = loadInt(pos, & timeMicroseconds);
value->packetTime.getTimeval()->tv_usec = timeMicroseconds;
// Load packet length
unsigned int packetLength = 0;
pos = loadInt(pos, & packetLength);
value->packetLength = static_cast<int>(packetLength);
// Load tcp_info
pos = loadChar(pos, & kernel.tcpi_state);
pos = loadChar(pos, & kernel.tcpi_ca_state);
pos = loadChar(pos, & kernel.tcpi_retransmits);
......@@ -330,17 +404,27 @@ char * loadPacket(char * buffer, PacketInfo * value, struct tcp_info & kernel,
pos = loadInt(pos, & kernel.tcpi_advmss);
pos = loadInt(pos, & kernel.tcpi_reordering);
// Load IP header
memcpy(&ip, pos, sizeof(struct ip));
pos += sizeof(struct ip);
// Load IP options
pos = loadOptions(pos, &ipOptions);
// Load TCP header
memcpy(&tcp, pos, sizeof(struct tcphdr));
pos += sizeof(struct tcphdr);
// Load TCP options
pos = loadOptions(pos, &tcpOptions);
// Load elab
pos = loadChar(pos, & value->elab.transport);
pos = loadInt(pos, & value->elab.ip);
pos = loadShort(pos, & value->elab.localPort);
pos = loadShort(pos, & value->elab.remotePort);
// Load bufferFull
unsigned char bufferFull = 0;
pos = loadChar(pos, &bufferFull);
value->bufferFull = (bufferFull == 1);
......
......@@ -26,16 +26,20 @@ char * saveChar(char * buffer, unsigned char value);
char * saveShort(char * buffer, unsigned short value);
char * saveInt(char * buffer, unsigned int value);
char * saveHeader(char * buffer, Header const & value);
char * saveOptions(char * buffer, std::list<Option> const & options);
char * savePacket(char * buffer, PacketInfo const & value);
char * loadChar(char * buffer, unsigned char * value);
char * loadShort(char * buffer, unsigned short * value);
char * loadInt(char * buffer, unsigned int * value);
char * loadHeader(char * buffer, Header * value);
char * loadOptions(char * buffer, std::list<Option> * options);
std::auto_ptr<Command> loadCommand(Header * head, char * body);
// It is presumed that value contains pointers to the various
// substructures that need to be filled.
char * loadPacket(char * buffer, PacketInfo * value, struct tcp_info & kernel,
struct ip & ip, struct tcphdr & tcp);
struct ip & ip, struct tcphdr & tcp,
std::list<Option> & ipOptions,
std::list<Option> & tcpOptions);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment