Feature (contd.): Device Emulation - Mac protocol can now 'resolve' src/dst mac corresponding to the packet from the Device Emulation infra

This commit is contained in:
Srivats P 2015-11-10 19:40:32 +05:30
parent 0db170d393
commit 280d4bedaa
20 changed files with 323 additions and 106 deletions

View File

@ -49,3 +49,13 @@ void Stream::storeProtocolWidgets()
qWarning("%s: DOES NOTHING", __PRETTY_FUNCTION__);
return;
}
quint64 getDeviceMacAddress(int portId, int streamId, int frameIndex)
{
return 0;
}
quint64 getNeighborMacAddress(int portId, int streamId, int frameIndex)
{
return 0;
}

View File

@ -19,6 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "mac.h"
#include "../common/streambase.h"
#include <QRegExp>
#define uintToMacStr(num) \
@ -28,6 +30,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
MacProtocol::MacProtocol(StreamBase *stream, AbstractProtocol *parent)
: AbstractProtocol(stream, parent)
{
forResolve_ = false;
}
MacProtocol::~MacProtocol()
@ -124,6 +127,15 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib,
data.dst_mac_step();
dstMac = data.dst_mac() - u;
break;
case OstProto::Mac::e_mm_resolve:
if (forResolve_)
dstMac = 0;
else {
forResolve_ = true;
dstMac = mpStream->neighborMacAddress(streamIndex);
forResolve_ = false;
}
break;
default:
qWarning("Unhandled dstMac_mode %d", data.dst_mac_mode());
}
@ -169,6 +181,15 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib,
data.src_mac_step();
srcMac = data.src_mac() - u;
break;
case OstProto::Mac::e_mm_resolve:
if (forResolve_)
srcMac = 0;
else {
forResolve_ = true;
srcMac = mpStream->deviceMacAddress(streamIndex);
forResolve_ = false;
}
break;
default:
qWarning("Unhandled srcMac_mode %d", data.src_mac_mode());
}
@ -331,11 +352,23 @@ int MacProtocol::protocolFrameVariableCount() const
{
int count = AbstractProtocol::protocolFrameVariableCount();
if (data.dst_mac_mode() != OstProto::Mac::e_mm_fixed)
switch (data.dst_mac_mode()) {
case OstProto::Mac::e_mm_inc:
case OstProto::Mac::e_mm_dec:
count = AbstractProtocol::lcm(count, data.dst_mac_count());
break;
default:
break;
}
if (data.src_mac_mode() != OstProto::Mac::e_mm_fixed)
switch (data.src_mac_mode()) {
case OstProto::Mac::e_mm_inc:
case OstProto::Mac::e_mm_dec:
count = AbstractProtocol::lcm(count, data.src_mac_count());
break;
default:
break;
}
return count;
}

View File

@ -67,6 +67,7 @@ public:
private:
OstProto::Mac data;
mutable bool forResolve_;
};
#endif

View File

@ -28,6 +28,7 @@ message Mac {
e_mm_fixed = 0;
e_mm_inc = 1;
e_mm_dec = 2;
e_mm_resolve = 3; // dst: resolve neighbor; src: from device config
}
// Dst Mac

View File

@ -24,8 +24,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "protocolmanager.h"
extern ProtocolManager *OstProtocolManager;
extern quint64 getDeviceMacAddress(int portId, int streamId, int frameIndex);
extern quint64 getNeighborMacAddress(int portId, int streamId, int frameIndex);
StreamBase::StreamBase() :
StreamBase::StreamBase(int portId) :
portId_(portId),
mStreamId(new OstProto::StreamId),
mCore(new OstProto::StreamCore),
mControl(new OstProto::StreamControl)
@ -537,6 +540,16 @@ int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const
return pktLen;
}
quint64 StreamBase::deviceMacAddress(int frameIndex) const
{
return getDeviceMacAddress(portId_, int(mStreamId->id()), frameIndex);
}
quint64 StreamBase::neighborMacAddress(int frameIndex) const
{
return getNeighborMacAddress(portId_, int(mStreamId->id()), frameIndex);
}
bool StreamBase::preflightCheck(QString &result) const
{
bool pass = true;

View File

@ -33,15 +33,8 @@ class ProtocolListIterator;
class StreamBase
{
private:
OstProto::StreamId *mStreamId;
OstProto::StreamCore *mCore;
OstProto::StreamControl *mControl;
ProtocolList *currentFrameProtocols;
public:
StreamBase();
StreamBase(int portId = -1);
~StreamBase();
void protoDataCopyFrom(const OstProto::Stream &stream);
@ -142,9 +135,22 @@ public:
int frameProtocolLength(int frameIndex) const;
int frameCount() const;
int frameValue(uchar *buf, int bufMaxSize, int frameIndex) const;
quint64 deviceMacAddress(int frameIndex) const;
quint64 neighborMacAddress(int frameIndex) const;
bool preflightCheck(QString &result) const;
static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2);
private:
int portId_;
OstProto::StreamId *mStreamId;
OstProto::StreamCore *mCore;
OstProto::StreamControl *mControl;
ProtocolList *currentFrameProtocols;
};
#endif

View File

@ -573,41 +573,6 @@ void AbstractPort::updatePacketListInterleaved()
isSendQueueDirty_ = false;
}
void AbstractPort::clearDeviceNeighbors()
{
deviceManager_->clearDeviceNeighbors();
}
void AbstractPort::resolveDeviceNeighbors()
{
// Resolve neighbor for each unique frame of each stream
// NOTE:
// 1. All the frames may have the same destination ip,but may have
// different source ip so may belong to a different emulated device;
// so we cannot optimize and send only one ARP
// 2. For a unidirectional stream, at egress, this will create ARP
// entries on the DUT for each of the source addresses
//
// TODO(optimization): Identify if stream does not vary in srcIp or dstIp
// - in which case resolve for only one frame of the stream
for (int i = 0; i < streamList_.size(); i++)
{
const StreamBase *stream = streamList_.at(i);
int frameCount = stream->frameVariableCount();
for (int j = 0; j < frameCount; j++) {
// TODO(optimization): we need the packet contents only uptil
// the L3 header; it would be best if protocols/streams could
// cache the frameValue()
int pktLen = stream->frameValue(pktBuf_, sizeof(pktBuf_), j);
if (pktLen) {
PacketBuffer pktBuf(pktBuf_, pktLen);
deviceManager_->resolveDeviceNeighbor(&pktBuf);
}
}
}
}
void AbstractPort::stats(PortStats *stats)
{
stats->rxPkts = (stats_.rxPkts >= epochStats_.rxPkts) ?
@ -641,3 +606,66 @@ void AbstractPort::stats(PortStats *stats)
stats_.rxFrameErrors - epochStats_.rxFrameErrors :
stats_.rxFrameErrors + (maxStatsValue_ - epochStats_.rxFrameErrors);
}
void AbstractPort::clearDeviceNeighbors()
{
deviceManager_->clearDeviceNeighbors();
isSendQueueDirty_ = true;
}
void AbstractPort::resolveDeviceNeighbors()
{
// Resolve neighbor for each unique frame of each stream
// NOTE:
// 1. All the frames may have the same destination ip,but may have
// different source ip so may belong to a different emulated device;
// so we cannot optimize and send only one ARP
// 2. For a unidirectional stream, at egress, this will create ARP
// entries on the DUT for each of the source addresses
//
// TODO(optimization): Identify if stream does not vary in srcIp or dstIp
// - in which case resolve for only one frame of the stream
for (int i = 0; i < streamList_.size(); i++)
{
const StreamBase *stream = streamList_.at(i);
int frameCount = stream->frameVariableCount();
for (int j = 0; j < frameCount; j++) {
// we need the packet contents only uptil the L3 header
int pktLen = stream->frameValue(pktBuf_, kMaxL3PktSize, j);
if (pktLen) {
PacketBuffer pktBuf(pktBuf_, pktLen);
deviceManager_->resolveDeviceNeighbor(&pktBuf);
}
}
}
isSendQueueDirty_ = true;
}
quint64 AbstractPort::deviceMacAddress(int streamId, int frameIndex)
{
// we need the packet contents only uptil the L3 header
StreamBase *s = stream(streamId);
int pktLen = s->frameValue(pktBuf_, kMaxL3PktSize, frameIndex);
if (pktLen) {
PacketBuffer pktBuf(pktBuf_, pktLen);
return deviceManager_->deviceMacAddress(&pktBuf);
}
return 0;
}
quint64 AbstractPort::neighborMacAddress(int streamId, int frameIndex)
{
// we need the packet contents only uptil the L3 header
StreamBase *s = stream(streamId);
int pktLen = s->frameValue(pktBuf_, kMaxL3PktSize, frameIndex);
if (pktLen) {
PacketBuffer pktBuf(pktBuf_, pktLen);
return deviceManager_->neighborMacAddress(&pktBuf);
}
return 0;
}

View File

@ -95,6 +95,9 @@ public:
virtual bool isCaptureOn() = 0;
virtual QIODevice* captureData() = 0;
void stats(PortStats *stats);
void resetStats() { epochStats_ = stats_; }
DeviceManager* deviceManager();
virtual void startDeviceEmulation() = 0;
virtual void stopDeviceEmulation() = 0;
@ -103,8 +106,8 @@ public:
void clearDeviceNeighbors();
void resolveDeviceNeighbors();
void stats(PortStats *stats);
void resetStats() { epochStats_ = stats_; }
quint64 deviceMacAddress(int streamId, int frameIndex);
quint64 neighborMacAddress(int streamId, int frameIndex);
protected:
void addNote(QString note);
@ -129,6 +132,12 @@ private:
static const int kMaxPktSize = 16384;
uchar pktBuf_[kMaxPktSize];
// When finding a corresponding device for a packet, we need to inspect
// only uptil the L3 header; in the worst case this would be -
// mac (12) + 4 x vlan (16) + ethType (2) + ipv6 (40) = 74 bytes
// let's round it up to 80 bytes
static const int kMaxL3PktSize = 80;
/*! \note StreamBase::id() and index into streamList[] are NOT same! */
QList<StreamBase*> streamList_;

View File

@ -72,6 +72,11 @@ void Device::setVlan(int index, quint16 vlan)
numVlanTags_ = index + 1;
}
quint64 Device::mac()
{
return mac_;
}
void Device::setMac(quint64 mac)
{
int ofs = kMaxVlan * sizeof(quint16);
@ -215,6 +220,65 @@ void Device::getNeighbors(OstEmul::DeviceNeighbors *neighbors)
}
}
// We expect pktBuf to point to EthType on entry
bool Device::isOrigin(const PacketBuffer *pktBuf)
{
const uchar *pktData = pktBuf->data();
quint16 ethType = qFromBigEndian<quint16>(pktData);
qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType);
pktData += 2;
// We know only about IP packets
if (ethType == 0x0800) { // IPv4
int ipHdrLen = (pktData[0] & 0x0F) << 2;
quint32 srcIp;
if (pktBuf->length() < ipHdrLen) {
qDebug("incomplete IPv4 header: expected %d, actual %d",
ipHdrLen, pktBuf->length());
return false;
}
srcIp = qFromBigEndian<quint32>(pktData + ipHdrLen - 8);
qDebug("%s: pktSrcIp/selfIp = 0x%x/0x%x", __FUNCTION__, srcIp, ip4_);
return (srcIp == ip4_);
}
return false;
}
// We expect pktBuf to point to EthType on entry
quint64 Device::neighborMac(const PacketBuffer *pktBuf)
{
const uchar *pktData = pktBuf->data();
quint16 ethType = qFromBigEndian<quint16>(pktData);
qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType);
pktData += 2;
// We know only about IP packets
if (ethType == 0x0800) { // IPv4
int ipHdrLen = (pktData[0] & 0x0F) << 2;
quint32 dstIp, tgtIp, mask;
if (pktBuf->length() < ipHdrLen) {
qDebug("incomplete IPv4 header: expected %d, actual %d",
ipHdrLen, pktBuf->length());
return false;
}
dstIp = qFromBigEndian<quint32>(pktData + ipHdrLen - 4);
mask = ~0 << (32 - ip4PrefixLength_);
qDebug("dst %x self %x mask %x", dstIp, ip4_, mask);
tgtIp = ((dstIp & mask) == (ip4_ & mask)) ? dstIp : ip4Gateway_;
return arpTable.value(tgtIp);
}
return false;
}
//
// Private Methods
//
@ -348,13 +412,15 @@ void Device::sendArpRequest(PacketBuffer *pktBuf)
dstIp = qFromBigEndian<quint32>(pktData + ipHdrLen - 4);
// TODO: if we have already sent a ARP request for the dst IP, do not
// resend - requires some sort of state per entry (timeout also?)
mask = ~0 << (32 - ip4PrefixLength_);
qDebug("dst %x src %x mask %x", dstIp, srcIp, mask);
tgtIp = ((dstIp & mask) == (srcIp & mask)) ? dstIp : ip4Gateway_;
// Do we already have a ARP entry (resolved or unresolved)?
// FIXME: do we need a timer to resend ARP for unresolved entries?
if (arpTable.contains(tgtIp))
return;
reqPkt = new PacketBuffer;
reqPkt->reserve(encapSize());
pktData = reqPkt->put(28);
@ -375,6 +441,7 @@ void Device::sendArpRequest(PacketBuffer *pktBuf)
encap(reqPkt, kBcastMac, 0x0806);
transmitPacket(reqPkt);
arpTable.insert(tgtIp, 0);
qDebug("Sent ARP Request for srcIp/tgtIp=%s/%s",
qPrintable(QHostAddress(srcIp).toString()),

View File

@ -37,6 +37,7 @@ public:
Device(DeviceManager *deviceManager);
void setVlan(int index, quint16 vlan);
quint64 mac();
void setMac(quint64 mac);
void setIp4(quint32 address, int prefixLength, quint32 gateway);
QString config();
@ -54,6 +55,9 @@ public:
void resolveNeighbor(PacketBuffer *pktBuf);
void getNeighbors(OstEmul::DeviceNeighbors *neighbors);
bool isOrigin(const PacketBuffer *pktBuf);
quint64 neighborMac(const PacketBuffer *pktBuf);
private: // methods
void receiveArp(PacketBuffer *pktBuf);
void sendArpRequest(PacketBuffer *pktBuf);

View File

@ -227,36 +227,47 @@ void DeviceManager::getDeviceNeighbors(
}
}
// FIXME: This function is mostly a duplicate of receivePacket;
// can we have a single combined one?
void DeviceManager::resolveDeviceNeighbor(PacketBuffer *pktBuf)
{
Device *device = originDevice(pktBuf);
if (device)
device->resolveNeighbor(pktBuf);
}
quint64 DeviceManager::deviceMacAddress(PacketBuffer *pktBuf)
{
Device *device = originDevice(pktBuf);
return device ? device->mac() : 0;
}
quint64 DeviceManager::neighborMacAddress(PacketBuffer *pktBuf)
{
Device *device = originDevice(pktBuf);
return device ? device->neighborMac(pktBuf) : 0;
}
// ------------------------------------ //
// Private Methods
// ------------------------------------ //
Device* DeviceManager::originDevice(PacketBuffer *pktBuf)
{
uchar *pktData = pktBuf->data();
int offset = 0;
int offset = 12; // start parsing after mac addresses
Device dk(this);
Device *device;
quint64 dstMac = kBcastMac;
quint16 ethType;
quint16 vlan;
int idx = 0;
// NOTE:
// 1. Since resolution hasn't happened yet, dstMac will not be valid;
// so we use the Bcast address instead
// 2. We assume pkt is ethernet; TODO: extend for other link layer types
// pktBuf will not have the correct dstMac populated, so use bcastMac
// and search for device by IP
// FIXME: validate before extracting if the offset is within pktLen
dk.setMac(dstMac);
offset += 6;
// Skip srcMac - don't care
offset += 6;
qDebug("dstMac %012" PRIx64, dstMac);
dk.setMac(kBcastMac);
_eth_type:
// Extract EthType
ethType = qFromBigEndian<quint16>(pktData + offset);
qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType);
@ -271,29 +282,13 @@ _eth_type:
pktBuf->pull(offset);
if (dstMac == kBcastMac) {
QList<Device*> list = bcastList_.values(dk.key());
// FIXME: We need to clone the pktBuf before passing to each
// device, otherwise only the first device gets the original
// packet - all subsequent ones get the modified packet!
// NOTE: modification may not be in the pkt data buffer but
// in the HDTE pointers - which is bad as well!
foreach(Device *device, list)
device->resolveNeighbor(pktBuf);
goto _exit;
foreach(Device *device, bcastList_.values(dk.key())) {
if (device->isOrigin(pktBuf))
return device;
}
// Is it destined for us?
device = deviceList_.value(dk.key());
if (!device) {
qDebug("%s: dstMac %012llx is not us", __FUNCTION__, dstMac);
goto _exit;
}
device->receivePacket(pktBuf);
_exit:
return;
qDebug("couldn't find origin device for packet");
return NULL;
}
void DeviceManager::enumerateDevices(

View File

@ -55,9 +55,12 @@ public:
void resolveDeviceNeighbor(PacketBuffer *pktBuf);
void getDeviceNeighbors(OstProto::DeviceNeighborList *neighborList);
quint64 deviceMacAddress(PacketBuffer *pktBuf);
quint64 neighborMacAddress(PacketBuffer *pktBuf);
private:
enum Operation { kAdd, kDelete };
Device* originDevice(PacketBuffer *pktBuf);
void enumerateDevices(
const OstProto::DeviceGroup *deviceGroup,
Operation oper);

View File

@ -58,3 +58,8 @@ bool Drone::init()
return true;
}
MyService* Drone::rpcService()
{
return service;
}

View File

@ -32,6 +32,7 @@ public:
Drone(QObject *parent = 0);
~Drone();
bool init();
MyService* rpcService();
private:
RpcServer *rpcServer;

View File

@ -35,6 +35,7 @@ extern ProtocolManager *OstProtocolManager;
extern char *version;
extern char *revision;
Drone *drone;
QSettings *appSettings;
int myport;
@ -47,7 +48,6 @@ int main(int argc, char *argv[])
{
int exitCode = 0;
QCoreApplication app(argc, argv);
Drone *drone;
// TODO: command line options
// -v (--version)

View File

@ -20,6 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "myservice.h"
#include "drone.h"
#if 0
#include <qglobal.h>
#include <qendian.h>
@ -38,6 +40,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include <QStringList>
extern Drone *drone;
extern char *version;
MyService::MyService()
@ -241,7 +244,7 @@ void MyService::addStream(::google::protobuf::RpcController* controller,
// Append a new "default" stream - actual contents of the new stream is
// expected in a subsequent "modifyStream" request - set the stream id
// now itself however!!!
stream = new StreamBase;
stream = new StreamBase(portId);
stream->setId(request->stream_id(i).id());
portInfo[portId]->addStream(stream);
}
@ -360,6 +363,8 @@ void MyService::startTransmit(::google::protobuf::RpcController* /*controller*/,
continue; //! \todo (LOW): partial RPC?
portLock[portId]->lockForWrite();
if (portInfo[portId]->isDirty())
portInfo[portId]->updatePacketList();
portInfo[portId]->startTransmit();
portLock[portId]->unlock();
}
@ -899,3 +904,33 @@ _invalid_port:
controller->SetFailed("Invalid Port Id");
done->Run();
}
quint64 getDeviceMacAddress(int portId, int streamId, int frameIndex)
{
MyService *service = drone->rpcService();
quint64 mac;
if (!service)
return 0;
service->portLock[portId]->lockForWrite();
mac = service->portInfo[portId]->deviceMacAddress(streamId, frameIndex);
service->portLock[portId]->unlock();
return mac;
}
quint64 getNeighborMacAddress(int portId, int streamId, int frameIndex)
{
MyService *service = drone->rpcService();
quint64 mac;
if (!service)
return 0;
service->portLock[portId]->lockForWrite();
mac = service->portInfo[portId]->neighborMacAddress(streamId, frameIndex);
service->portLock[portId]->unlock();
return mac;
}

View File

@ -147,6 +147,11 @@ public:
const ::OstProto::PortId* request,
::OstProto::DeviceNeighborList* response,
::google::protobuf::Closure* done);
friend quint64 getDeviceMacAddress(
int portId, int streamId, int frameIndex);
friend quint64 getNeighborMacAddress(
int portId, int streamId, int frameIndex);
signals:
void notification(int notifType, SharedProtobufMessage notifData);

View File

@ -49,27 +49,27 @@ PacketBuffer::~PacketBuffer()
delete[] buffer_;
}
int PacketBuffer::length()
int PacketBuffer::length() const
{
return tail_ - data_;
}
uchar* PacketBuffer::head()
uchar* PacketBuffer::head() const
{
return head_;
}
uchar* PacketBuffer::data()
uchar* PacketBuffer::data() const
{
return data_;
}
uchar* PacketBuffer::tail()
uchar* PacketBuffer::tail() const
{
return tail_;
}
uchar* PacketBuffer::end()
uchar* PacketBuffer::end() const
{
return end_;
}

View File

@ -29,12 +29,12 @@ public:
PacketBuffer(const uchar *buffer, int size);
~PacketBuffer();
int length();
int length() const;
uchar* head();
uchar* data();
uchar* tail();
uchar* end();
uchar* head() const;
uchar* data() const;
uchar* tail() const;
uchar* end() const;
void reserve(int len);
uchar* pull(int len);

View File

@ -14,7 +14,7 @@ from harness import Test, TestSuite, TestPreRequisiteError
sys.path.insert(1, '../binding')
from core import ost_pb, emul, DroneProxy
from rpc import RpcError
from protocols.mac_pb2 import mac
from protocols.mac_pb2 import mac, Mac
from protocols.ip4_pb2 import ip4, Ip4
from protocols.vlan_pb2 import vlan
@ -263,8 +263,8 @@ try:
# setup stream protocols as mac:eth2:ip4:udp:payload
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber
p.Extensions[mac].dst_mac = dut_dst_mac
p.Extensions[mac].src_mac = 0x00aabbccddee
p.Extensions[mac].dst_mac_mode = Mac.e_mm_resolve
p.Extensions[mac].src_mac_mode = Mac.e_mm_resolve
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kEth2FieldNumber
@ -401,6 +401,7 @@ try:
sudo('ip address delete 10.10.2.1/24 dev ' + dut_tx_port)
suite.test_end(passed)
sys.exit(1)
# FIXME: update the below test cases to resolve Neighbors and streams
# to derive src/dst mac from device