Commit 8679bec6 authored by Jonathon Duerig's avatar Jonathon Duerig
Browse files

Added versioning to magent. Added UDP support to the sinews of the magent....

Added versioning to magent. Added UDP support to the sinews of the magent. Added a simple monitor mockup (not functional yet) which can drive the magent by giving it UDP write commands.
parent 16aaa101
......@@ -20,12 +20,13 @@ void NewConnectionCommand::run(std::multimap<Time, Connection *> &)
{
logWrite(COMMAND_INPUT, "Running NEW_CONNECTION_COMMAND: %s",
key.toString().c_str());
std::map<Order, Connection>::iterator pos
std::map<ElabOrder, Connection>::iterator pos
= global::connections.find(key);
if (pos == global::connections.end())
{
pos = global::connections.insert(make_pair(key, Connection())).first;
pos->second.reset(key, global::connectionModelExemplar->clone());
pos->second.reset(key, global::connectionModelExemplar->clone(),
transport);
}
}
......@@ -72,7 +73,7 @@ void ConnectCommand::runConnect(Connection * conn,
{
logWrite(COMMAND_INPUT, "Running CONNECT_COMMAND: %s",
key.toString().c_str());
conn->connect();
conn->connect(ip);
}
//-----------------------
......@@ -90,7 +91,7 @@ void DeleteConnectionCommand::run(std::multimap<Time, Connection *> & schedule)
{
logWrite(COMMAND_INPUT, "Running DELETE_CONNECTION_COMMAND: %s",
key.toString().c_str());
std::map<Order, Connection>::iterator pos
std::map<ElabOrder, Connection>::iterator pos
= global::connections.find(key);
if (pos != global::connections.end())
{
......
......@@ -16,7 +16,7 @@ class Command
public:
virtual void run(std::multimap<Time, Connection *> & schedule)
{
std::map<Order, Connection>::iterator pos
std::map<ElabOrder, Connection>::iterator pos
= global::connections.find(key);
if (pos != global::connections.end())
{
......@@ -32,16 +32,19 @@ public:
// We use a key here and look up the connection only on a run()
// because some commands delete a connection and we don't want later
// commands to keep the reference around.
Order key;
ElabOrder key;
};
class NewConnectionCommand : public Command
{
public:
NewConnectionCommand() : transport(TCP_CONNECTION) {}
virtual void run(std::multimap<Time, Connection *> &);
protected:
virtual void runConnect(Connection * conn,
std::multimap<Time, Connection *> &);
public:
unsigned char transport;
};
class TrafficModelCommand : public Command
......@@ -73,9 +76,13 @@ public:
class ConnectCommand : public Command
{
public:
ConnectCommand() : ip(0) {}
protected:
virtual void runConnect(Connection * conn,
std::multimap<Time, Connection *> &);
public:
unsigned int ip;
};
class TrafficWriteCommand : public Command
......
......@@ -32,7 +32,7 @@ public:
};
public:
virtual ~CommandOutput() {}
void eventMessage(std::string const & message, Order const & key,
void eventMessage(std::string const & message, ElabOrder const & key,
PathDirection dir=FORWARD_PATH)
{
if (dir == FORWARD_PATH)
......@@ -45,7 +45,8 @@ public:
}
}
void genericMessage(int type, std::string const & message, Order const & key)
void genericMessage(int type, std::string const & message,
ElabOrder const & key)
{
if (message.size() <= 0xffff && message.size() > 0)
{
......
......@@ -61,12 +61,29 @@ Connection & Connection::operator=(Connection const & right)
return *this;
}
void Connection::reset(Order const & newElab,
std::auto_ptr<ConnectionModel> newPeer)
void Connection::reset(ElabOrder const & newElab,
std::auto_ptr<ConnectionModel> newPeer,
unsigned char transport)
{
logWrite(CONNECTION, "Peer added to connection");
elab = newElab;
peer = newPeer;
planet.transport = transport;
switch (transport)
{
case TCP_CONNECTION:
planet.remotePort = global::peerServerPort;
break;
case UDP_CONNECTION:
planet.remotePort = global::peerUdpServerPort;
break;
default:
logWrite(ERROR, "Invalid transport protocol %d, "
"defaulting to TCP_CONNECTION", transport);
planet.transport = TCP_CONNECTION;
planet.remotePort = global::peerServerPort;
break;
}
}
void Connection::setTraffic(std::auto_ptr<TrafficModel> newTraffic)
......@@ -89,14 +106,12 @@ void Connection::addConnectionModelParam(ConnectionModelCommand const & param)
}
}
void Connection::connect(void)
void Connection::connect(unsigned int ip)
{
logWrite(CONNECTION, "Connection connected");
planet.ip = ip;
if (peer.get() != NULL)
{
planet.transport = TCP_CONNECTION;
planet.ip = elab.ip;
planet.remotePort = global::peerServerPort;
// planet is modified by ConnectionModel::connect()
peer->connect(planet);
isConnected = peer->isConnected();
......@@ -159,9 +174,9 @@ ConnectionModel const * Connection::getConnectionModel(void)
Time Connection::writeToConnection(Time const & previousTime)
{
WriteResult result;
result.planet.transport = TCP_CONNECTION;
result.planet.ip = elab.ip;
result.planet.remotePort = global::peerServerPort;
result.planet.transport = planet.transport;
result.planet.ip = planet.ip;
result.planet.remotePort = planet.remotePort;
if (isConnected)
{
result.planet.localPort = planet.localPort;
......
......@@ -14,7 +14,6 @@
class Time;
class ConnectionModel;
class TrafficModel;
class Sensor;
class ConnectionModelCommand;
class TrafficWriteCommand;
class SensorCommand;
......@@ -27,9 +26,12 @@ public:
Connection & operator=(Connection const & right);
// Called after the Connection is added to the map. Sets the
// elabOrder sorting element and sets up the Connection Model.
void reset(Order const & newElab,
std::auto_ptr<ConnectionModel> newPeer);
// elabOrder sorting element and sets up the Connection Model. Also
// sets the transport protocol of this connection (TCP_CONNECTION or
// UDP_CONNECTION)
void reset(ElabOrder const & newElab,
std::auto_ptr<ConnectionModel> newPeer,
unsigned char transport);
// Called when the monitor specifies which traffic model to use.
void setTraffic(std::auto_ptr<TrafficModel> newTraffic);
// Set a connection model parameter. Things like socket buffer
......@@ -37,7 +39,7 @@ public:
void addConnectionModelParam(ConnectionModelCommand const & param);
// This starts an attempt to connect through the connection
// model. Called when the monitor notifies of a connect.
void connect(void);
void connect(unsigned int ip);
// Notifies the traffic model of write information from the monitor.
void addTrafficWrite(TrafficWriteCommand const & newWrite,
std::multimap<Time, Connection *> & schedule);
......@@ -55,8 +57,8 @@ public:
private:
// There are two kinds of ordering. One is for commands from
// emulab. One is for the pcap analysis.
Order elab;
Order planet;
ElabOrder elab;
PlanetOrder planet;
std::auto_ptr<ConnectionModel> peer;
std::auto_ptr<TrafficModel> traffic;
......
......@@ -16,7 +16,7 @@ class ConnectionModel
public:
virtual ~ConnectionModel() {}
virtual std::auto_ptr<ConnectionModel> clone(void)=0;
virtual void connect(Order & planet)=0;
virtual void connect(PlanetOrder & planet)=0;
virtual void addParam(ConnectionModelCommand const & param)=0;
// Returns the number of bytes actually written or -1 if there was
// an error. Errno is not preserved.
......@@ -39,7 +39,7 @@ public:
{
return std::auto_ptr<ConnectionModel>(new ConnectionModelNull());
}
virtual void connect(Order &) {}
virtual void connect(PlanetOrder &) {}
virtual void addParam(ConnectionModelCommand const &) {}
static void init(void) {}
......@@ -52,6 +52,12 @@ public:
result.bufferFull = false;
return 0;
}
virtual int sendToMessage(int size, WriteResult & result)
{
result.isConnected = false;
result.bufferFull = false;
return 0;
}
virtual bool isConnected(void)
{
return false;
......
......@@ -17,6 +17,7 @@ DirectInput::DirectInput()
, monitorAccept(-1)
, monitorSocket(-1)
, index(0)
, versionSize(0)
{
logWrite(COMMAND_INPUT, "Creating the command accept socket on port %d",
global::monitorServerPort);
......@@ -53,8 +54,49 @@ void DirectInput::nextCommand(fd_set * readable)
"connection was not accepted)");
if (monitorSocket != -1)
{
state = HEADER_PREFIX;
}
}
if (state == HEADER_PREFIX && monitorSocket != -1
&& FD_ISSET(monitorSocket, readable))
{
int error = recv(monitorSocket, headerBuffer+index,
Header::PREFIX_SIZE - index, 0);
if (error == Header::PREFIX_SIZE - index)
{
char version = headerBuffer[Header::PREFIX_SIZE - 1];
switch (version)
{
case 0:
versionSize = Header::PREFIX_SIZE + Header::VERSION_0_SIZE;
break;
case 1:
versionSize = Header::PREFIX_SIZE + Header::VERSION_1_SIZE;
break;
default:
logWrite(ERROR, "Unknown version: %d"
", assuming that it really means version 1", version);
versionSize = Header::PREFIX_SIZE + Header::VERSION_1_SIZE;
break;
}
state = HEADER;
}
else if (error > 0)
{
index += error;
}
else if (error == 0)
{
logWrite(EXCEPTION, "Read count of 0 returned (state BODY): %s",
strerror(errno));
disconnect();
}
else if (error == -1)
{
logWrite(EXCEPTION, "Failed read on monitorSocket "
"(state HEADER_PREFIX) index=%d: %s", index,
strerror(errno));
}
}
// logWrite(COMMAND_INPUT, "Before HEADER check");
if (state == HEADER && monitorSocket != -1
......@@ -114,7 +156,7 @@ void DirectInput::nextCommand(fd_set * readable)
}
currentCommand = loadCommand(&commandHeader, bodyBuffer);
index = 0;
state = HEADER;
state = HEADER_PREFIX;
}
else if (error > 0)
{
......
......@@ -25,6 +25,7 @@ private:
enum MonitorState
{
ACCEPTING,
HEADER_PREFIX,
HEADER,
BODY
};
......@@ -34,6 +35,7 @@ private:
int monitorSocket;
int index;
char headerBuffer[Header::headerSize];
int versionSize;
Header commandHeader;
enum { bodyBufferSize = 0xffff };
char bodyBuffer[bodyBufferSize];
......
......@@ -24,11 +24,14 @@ namespace
unsigned char const * packet);
int getLinkLayer(struct pcap_pkthdr const * pcapInfo,
unsigned char const * packet);
void handleTcp(struct pcap_pkthdr const * pcapInfo,
struct ip const * ipPacket,
struct tcphdr const * tcpPacket,
unsigned char const * tcpPacketStart,
list<Option> & ipOptions);
void handleTransport(unsigned char transport,
struct pcap_pkthdr const * pcapInfo,
struct ip const * ipPacket,
struct tcphdr const * tcpPacket,
struct udphdr const * udpPacket,
unsigned char const * transportPacketStart,
list<Option> & ipOptions,
int bytesRemaining);
bool handleKernel(Connection * conn, struct tcp_info * kernel);
void parseOptions(unsigned char const * buffer, int size,
list<Option> * options);
......@@ -69,8 +72,13 @@ auto_ptr<ConnectionModel> KernelTcp::clone(void)
return modelResult;
}
void KernelTcp::connect(Order & planet)
void KernelTcp::connect(PlanetOrder & planet)
{
if (planet.transport == UDP_CONNECTION)
{
// PRAMOD: Insert any additional setup code for UDP connections here.
state = CONNECTED;
}
if (state == DISCONNECTED)
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
......@@ -182,11 +190,30 @@ void KernelTcp::addParam(ConnectionModelCommand const & param)
}
}
// MAX_WRITESIZE is in chars, and must be a multiple of 4
const int KernelTcp::MAX_WRITESIZE = 8192;
int KernelTcp::writeMessage(int size, WriteResult & result)
{
int retval = 0;
switch (result.planet.transport)
{
case TCP_CONNECTION:
retval = writeTcpMessage(size, result);
break;
case UDP_CONNECTION:
retval = writeUdpMessage(size, result);
break;
default:
logWrite(ERROR, "Failed to write message because of unknown transport "
"protocol %d", result.planet.transport);
break;
}
return retval;
}
int KernelTcp::writeTcpMessage(int size, WriteResult & result)
{
// MAX_WRITESIZE is in chars, and must be a multiple of 4
static const int MAX_WRITESIZE = 8192;
if (state == DISCONNECTED)
{
logWrite(CONNECTION_MODEL,
......@@ -283,6 +310,19 @@ int KernelTcp::writeMessage(int size, WriteResult & result)
return -1;
}
}
int KernelTcp::writeUdpMessage(int size, WriteResult & result)
{
// PRAMOD: Replace the following call with the UDP write code.
// result.planet.ip and result.planet.remotePort denote the destination.
// result.planet.ip is in host order
// result.planet.remotePort is in host order
// put the localport in host order in result.planet.localPort
// set result.bufferFull to false (UDP 'buffers' are never full)
// set result.isConnected to true (UDP 'connections' are always connected)
return writeTcpMessage(size, result);
}
bool KernelTcp::isConnected(void)
{
return state == CONNECTED;
......@@ -477,6 +517,7 @@ namespace
}
struct ip const * ipPacket;
struct tcphdr const * tcpPacket;
struct udphdr const * udpPacket;
size_t bytesRemaining = pcapInfo->caplen - sizeof(struct ether_header);
ipPacket = reinterpret_cast<struct ip const *>
......@@ -501,11 +542,6 @@ namespace
logWrite(ERROR, "Bad IP header length: %d", ipHeaderLength);
return;
}
if (ipPacket->ip_p != IPPROTO_TCP)
{
logWrite(ERROR, "A non TCP packet was captured");
return;
}
list<Option> ipOptions;
unsigned char const * optionsBegin = packet + sizeof(struct ether_header)
......@@ -515,17 +551,49 @@ namespace
// ipHeaderLength is multiplied by 4 because it is a
// length in 4-byte words.
unsigned char const * tcpPacketStart = packet + sizeof(struct ether_header)
+ ipHeaderLength*4;
tcpPacket = reinterpret_cast<struct tcphdr const *>(tcpPacketStart);
unsigned char const * transportPacketStart = packet
+ sizeof(struct ether_header)
+ ipHeaderLength*4;
bytesRemaining -= ipHeaderLength*4;
if (bytesRemaining < sizeof(struct tcphdr))
switch (ipPacket->ip_p)
{
logWrite(ERROR, "A captured packet was to short to contain "
"a TCP header");
return;
case IPPROTO_TCP:
tcpPacket
= reinterpret_cast<struct tcphdr const *>(transportPacketStart);
if (bytesRemaining < sizeof(struct tcphdr))
{
logWrite(ERROR, "A captured packet was to short to contain "
"a TCP header");
}
else
{
logWrite(PCAP, "Captured a TCP packet");
bytesRemaining -= tcpPacket->doff*4;
handleTransport(TCP_CONNECTION, pcapInfo, ipPacket, tcpPacket, NULL,
transportPacketStart, ipOptions, bytesRemaining);
}
break;
case IPPROTO_UDP:
udpPacket
= reinterpret_cast<struct udphdr const *>(transportPacketStart);
if (bytesRemaining < sizeof(struct udphdr))
{
logWrite(ERROR, "A captured packet was to short to contain "
"a UDP header");
}
else
{
logWrite(PCAP, "Captured a UDP packet");
bytesRemaining -= sizeof(struct udphdr);
handleTransport(UDP_CONNECTION, pcapInfo, ipPacket, NULL, udpPacket,
transportPacketStart, ipOptions, bytesRemaining);
}
break;
default:
logWrite(ERROR, "I captured a packet, "
"but don't know the transport protocol: %d", ipPacket->ip_p);
break;
}
handleTcp(pcapInfo, ipPacket, tcpPacket, tcpPacketStart, ipOptions);
}
int getLinkLayer(struct pcap_pkthdr const * pcapInfo,
......@@ -546,29 +614,75 @@ namespace
}
}
void handleTcp(struct pcap_pkthdr const * pcapInfo,
struct ip const * ipPacket,
struct tcphdr const * tcpPacket,
unsigned char const * tcpPacketStart,
list<Option> & ipOptions)
void handleTransport(unsigned char transport,
struct pcap_pkthdr const * pcapInfo,
struct ip const * ipPacket,
struct tcphdr const * tcpPacket,
struct udphdr const * udpPacket,
unsigned char const * transportPacketStart,
list<Option> & ipOptions,
int bytesRemaining)
{
logWrite(PCAP, "Captured a TCP packet");
struct tcp_info kernelInfo;
list<Option> tcpOptions;
unsigned char const * tcpOptionsBegin = tcpPacketStart
+ sizeof(struct tcphdr);
int tcpOptionsSize = tcpPacket->doff*4 - sizeof(struct tcphdr);
parseOptions(tcpOptionsBegin, tcpOptionsSize, &tcpOptions);
unsigned char const * payload = NULL;
if (transport == TCP_CONNECTION)
{
unsigned char const * tcpOptionsBegin = transportPacketStart
+ sizeof(struct tcphdr);
int tcpOptionsSize = tcpPacket->doff*4 - sizeof(struct tcphdr);
if (bytesRemaining >= 0)
{
parseOptions(tcpOptionsBegin, tcpOptionsSize, &tcpOptions);
}
else
{
logWrite(ERROR, "TCP Packet too short to parse TCP options");
}
payload = transportPacketStart + tcpPacket->doff*4;
}
else if (transport == UDP_CONNECTION)
{
payload = transportPacketStart + sizeof(struct udphdr);
}
PacketInfo packet;
packet.transport = transport;
packet.packetTime = Time(pcapInfo->ts);
packet.packetLength = pcapInfo->len;
packet.kernel = &kernelInfo;
if (transport == TCP_CONNECTION)
{
packet.kernel = &kernelInfo;
packet.tcp = tcpPacket;
packet.tcpOptions = &tcpOptions;
}
else
{
packet.kernel = NULL;
packet.tcp = NULL;
packet.tcpOptions = NULL;
}
packet.ip = ipPacket;
packet.ipOptions = &ipOptions;
packet.tcp = tcpPacket;
packet.tcpOptions = &tcpOptions;
if (transport == UDP_CONNECTION)
{
packet.udp = udpPacket;
}
else
{
packet.udp = NULL;
}
if (bytesRemaining <= 0)
{
packet.payloadSize = 0;
packet.payload = NULL;
}
else
{
packet.payloadSize = bytesRemaining;
packet.payload = payload;
}
/*
* Classify this packet as outgoing or incoming, by trying to look it up
......@@ -577,27 +691,36 @@ namespace
* NOTE: We put the destination IP address in the planetMap, so we
* reverse the sense of ip_dst and ip_src
*/
Order key;
key.transport = TCP_CONNECTION;
PlanetOrder key;
key.transport = transport;
key.ip = ntohl(ipPacket->ip_dst.s_addr);
key.localPort = ntohs(tcpPacket->source);
key.remotePort = ntohs(tcpPacket->dest);
if (transport == TCP_CONNECTION)
{
key.localPort = ntohs(tcpPacket->source);
key.remotePort = ntohs(tcpPacket->dest);
}
else if (transport == UDP_CONNECTION)
{
key.localPort = ntohs(udpPacket->source);
key.remotePort = ntohs(udpPacket->dest);
}
bool outgoing;
logWrite(PCAP,"Looking up key (outgoing): i=%s,lp=%i,rp=%i",
inet_ntoa(ipPacket->ip_dst),key.localPort,key.remotePort);
map<Order, Connection *>::iterator pos;
logWrite(PCAP,"Looking up key (outgoing): t=%d,i=%s,lp=%i,rp=%i",
transport, inet_ntoa(ipPacket->ip_dst),key.localPort,key.remotePort);
map<PlanetOrder, Connection *>::iterator pos;
pos = global::planetMap.find(key);
if (pos != global::planetMap.end()) {
outgoing = true;
} else {
key.ip = ntohl(ipPacket->ip_src.s_addr);
key.localPort = ntohs(tcpPacket->dest);
key.remotePort = ntohs(tcpPacket->source);
logWrite(PCAP,"Looking up key (incoming): i=%s,lp=%i,rp=%i",
inet_ntoa(ipPacket->ip_src),key.localPort,key.remotePort);
swap(key.localPort, key.remotePort);
// key.localPort = ntohs(tcpPacket->dest);
// key.remotePort = ntohs(tcpPacket->source);
logWrite(PCAP,"Looking up key (incoming): t=%d,i=%s,lp=%i,rp=%i",
transport,inet_ntoa(ipPacket->ip_src),key.localPort,key.remotePort);
pos = global::planetMap.find(key);
if (pos != global::planetMap.end()) {
outgoing = false;
......@@ -615,35 +738,41 @@ namespace
/*