Feature (contd.): Device Emulation - user now configures a device group instead of a single device for easier creation of multiple devices; refactored and redistributed functionality between DeviceManager and Device; external functionality wise same as last commit; added initial code for vlans but not tested

This commit is contained in:
Srivats P 2015-09-20 17:49:15 +05:30
parent ab433dc22b
commit 246bc95c74
10 changed files with 561 additions and 360 deletions

View File

@ -21,46 +21,74 @@ import "protocol.proto";
package OstEmul; package OstEmul;
message MacEmulation { // ======
optional uint64 addr = 10; // VLAN
} // ======
extend OstProto.Device {
optional MacEmulation mac = 200;
}
message VlanEmulation { message VlanEmulation {
enum Mode {
kNoRepeat = 0;
kRepeat = 1;
}
message Vlan { message Vlan {
optional uint32 tpid = 1 [default = 0x8100]; optional uint32 tpid = 1 [default = 0x8100];
optional uint32 vlan_tag = 2; // includes prio, cfi and vlanid
// includes prio, cfi and vlanid
optional uint32 vlan_tag = 2 [default = 100];
optional uint32 count = 10 [default = 1];
optional uint32 step = 11 [default = 1];
optional Mode mode = 12 [default = kRepeat];
} }
// FIXME: rename as just stack?
repeated Vlan vlan_stack = 11; // outer to inner repeated Vlan stack = 1; // outer to inner
} }
extend OstProto.Device { extend OstProto.DeviceGroup {
optional VlanEmulation vlan = 201; optional VlanEmulation vlan = 200;
}
// ========
// Device
// ========
message MacEmulation {
optional uint64 address = 1; // FIXME: default value
optional uint64 step = 10 [default = 1];
} }
message Ip4Emulation { message Ip4Emulation {
optional uint32 addr = 2; optional uint32 address = 1;
optional uint32 prefix_length = 3; optional uint32 prefix_length = 2;
optional uint32 gateway = 4; // FIXME: rename to default_gateway? optional uint32 default_gateway = 3;
}
extend OstProto.Device { optional uint64 step = 10 [default = 1];
optional Ip4Emulation ip4 = 300; // FIXME: step for gateway?
} }
message Ip6Emulation { message Ip6Emulation {
optional uint64 addr_hi = 31; optional uint64 address_hi = 1;
optional uint64 addr_lo = 32; optional uint64 address_lo = 2;
optional uint32 prefix_length = 33; optional uint32 prefix_length = 3;
optional uint64 gateway_hi = 34; optional uint64 default_gateway_hi = 4;
optional uint64 gateway_lo = 35; optional uint64 default_gateway_lo = 5;
optional uint64 step_hi = 10 [default = 0];
optional uint64 step_lo = 11 [default = 1];
} }
extend OstProto.Device { message Device {
optional Ip6Emulation ip6 = 301; enum Mode {
kNoRepeat = 0;
kRepeat = 1;
}
optional MacEmulation mac = 1;
optional Ip4Emulation ip4 = 2;
optional Ip6Emulation ip6 = 3;
optional uint32 count = 10 [default = 1];
optional Mode mode = 11 [default = kRepeat]; // FIXME: per proto mode?
} }
extend OstProto.DeviceGroup {
optional Device device = 201;
}

View File

@ -278,32 +278,33 @@ message Notification {
* Protocol Emulation * Protocol Emulation
* FIXME: review/fix tag numbers * FIXME: review/fix tag numbers
* FIXME: move xxxEmulation to their own .proto files? * FIXME: move xxxEmulation to their own .proto files?
* FIXME: What will be the contents of a default device created by addDevice()? * FIXME: What will be the contents of a default device created by addDeviceGroup()?
* FIXME: decide default values for device and protoEmulations * FIXME: decide default values for device and protoEmulations
* FIXME: rename 'DeviceGroup'?
*/ */
message DeviceId { message DeviceGroupId {
required uint32 id = 1; required uint32 id = 1;
} }
message DeviceCore { message DeviceGroupCore {
optional string name = 1; optional string name = 1;
} }
message DeviceIdList { message DeviceGroupIdList {
required PortId port_id = 1; required PortId port_id = 1;
repeated DeviceId device_id = 2; repeated DeviceGroupId device_group_id = 2;
} }
message Device { message DeviceGroup {
required DeviceId device_id = 1; required DeviceGroupId device_group_id = 1;
optional DeviceCore core = 2; optional DeviceGroupCore core = 2;
extensions 200 to 500; // For use by Protocol Emulations extensions 200 to 500; // For use by Protocol Emulations
} }
message DeviceConfigList { message DeviceGroupConfigList {
required PortId port_id = 1; required PortId port_id = 1;
repeated Device device = 2; repeated DeviceGroup device_group = 2;
} }
service OstService { service OstService {
@ -329,11 +330,11 @@ service OstService {
rpc checkVersion(VersionInfo) returns (VersionCompatibility); rpc checkVersion(VersionInfo) returns (VersionCompatibility);
// Device Protocol Emulation // Device/Protocol Emulation
rpc getDeviceIdList(PortId) returns (DeviceIdList); rpc getDeviceGroupIdList(PortId) returns (DeviceGroupIdList);
rpc getDeviceConfig(DeviceIdList) returns (DeviceConfigList); rpc getDeviceGroupConfig(DeviceGroupIdList) returns (DeviceGroupConfigList);
rpc addDevice(DeviceIdList) returns (Ack); rpc addDeviceGroup(DeviceGroupIdList) returns (Ack);
rpc deleteDevice(DeviceIdList) returns (Ack); rpc deleteDeviceGroup(DeviceGroupIdList) returns (Ack);
rpc modifyDevice(DeviceConfigList) returns (Ack); rpc modifyDeviceGroup(DeviceGroupConfigList) returns (Ack);
} }

View File

@ -23,78 +23,99 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "devicemanager.h" #include "devicemanager.h"
#include "packetbuffer.h" #include "packetbuffer.h"
#include <QHostAddress>
#include <qendian.h> #include <qendian.h>
QHash<quint64, Device*> Device::macHash_; const int kBaseHex = 16;
QHash<quint32, Device*> Device::ip4Hash_; const int kMaxVlan = 4;
Device::Device(quint32 id, DeviceManager *deviceManager) /*
* NOTE:
* 1. Device Key is (VLANS + MAC) - is assumed to be unique for a device
* 2. Device clients/users (viz. DeviceManager) should take care when
* setting params that change the key, if the key is used elsewhere
* (e.g. in a hash)
*/
Device::Device(DeviceManager *deviceManager)
{ {
deviceManager_ = deviceManager; deviceManager_ = deviceManager;
data_.mutable_device_id()->set_id(id);
// FIXME: choose a better default mac address
data_.MutableExtension(OstEmul::mac)->set_addr(0x001122330000ULL + id);
Device::macHash_.insert(myMac(), this); for (int i = 0; i < kMaxVlan; i++)
vlan_[i] = 0;
numVlanTags_ = 0;
mac_ = 0;
ip4_ = 0;
ip4PrefixLength_ = 0;
clearKey();
} }
Device::~Device() void Device::setVlan(int index, quint16 vlan)
{ {
macHash_.remove(myMac()); int ofs;
ip4Hash_.remove(myIp4());
}
quint32 Device::id() if ((index < 0) || (index >= kMaxVlan)) {
{ qWarning("%s: vlan index %d out of range (0 - %d)", __FUNCTION__,
return data_.device_id().id(); index, kMaxVlan - 1);
} return;
void Device::protoDataCopyFrom(const OstProto::Device &device)
{
quint64 oldMac, newMac;
quint32 oldIp4, newIp4;
// Save old mac and ip before updating the device data
oldMac = myMac();
oldIp4 = myIp4();
data_.CopyFrom(device);
// Get new mac and ip for comparison
newMac = myMac();
newIp4 = myIp4();
// Update MacHash if mac has changed
if (newMac != oldMac) {
macHash_.remove(oldMac);
macHash_.insert(newMac, this);
} }
// Update Ip4Hash if ip4 has changed vlan_[index] = vlan;
if (newIp4 != oldIp4) {
ip4Hash_.remove(oldIp4); ofs = index * sizeof(quint16);
ip4Hash_.insert(newIp4, this); key_[ofs] = vlan >> 8;
} key_[ofs+1] = vlan & 0xff;
if (index >= numVlanTags_)
numVlanTags_ = index + 1;
} }
void Device::protoDataCopyInto(OstProto::Device &device) const void Device::setMac(quint64 mac)
{ {
device.CopyFrom(data_); int ofs = kMaxVlan * sizeof(quint16);
mac_ = mac;
memcpy(key_.data() + ofs, (char*)&mac, sizeof(mac));
}
void Device::setIp4(quint32 address, int prefixLength)
{
ip4_ = address;
ip4PrefixLength_ = prefixLength;
}
QString Device::config()
{
return QString("<vlans=%1/%2/%3/%4 mac=%5 ip4=%6/%7>")
.arg(vlan_[0]).arg(vlan_[1]).arg(vlan_[2]).arg(vlan_[3])
.arg(mac_, 12, kBaseHex, QChar('0'))
.arg(QHostAddress(ip4_).toString())
.arg(ip4PrefixLength_);
}
DeviceKey Device::key()
{
return key_;
}
void Device::clearKey()
{
key_.fill(0, kMaxVlan * sizeof(quint16) + sizeof(quint64));
} }
int Device::encapSize() int Device::encapSize()
{ {
int size = 14; // ethernet header size // ethernet header + vlans
int size = 14 + 4*numVlanTags_;
if (data_.HasExtension(OstEmul::vlan))
size += 4 * data_.GetExtension(OstEmul::vlan).vlan_stack_size();
return size; return size;
} }
void Device::encap(PacketBuffer *pktBuf, quint64 dstMac, quint16 type) void Device::encap(PacketBuffer *pktBuf, quint64 dstMac, quint16 type)
{ {
quint64 srcMac = myMac(); int ofs;
quint64 srcMac = mac_;
uchar *p = pktBuf->push(encapSize()); uchar *p = pktBuf->push(encapSize());
if (!p) { if (!p) {
@ -107,90 +128,52 @@ void Device::encap(PacketBuffer *pktBuf, quint64 dstMac, quint16 type)
*(quint16*)(p + 4) = qToBigEndian(quint16(dstMac & 0xffff)); *(quint16*)(p + 4) = qToBigEndian(quint16(dstMac & 0xffff));
*(quint32*)(p + 6) = qToBigEndian(quint32(srcMac >> 16)); *(quint32*)(p + 6) = qToBigEndian(quint32(srcMac >> 16));
*(quint16*)(p + 10) = qToBigEndian(quint16(srcMac & 0xffff)); *(quint16*)(p + 10) = qToBigEndian(quint16(srcMac & 0xffff));
// TODO: Vlan Encap ofs = 12;
*(quint16*)(p + 12) = qToBigEndian(type); for (int i = 0; i < numVlanTags_; i++) {
*(quint16*)(p + ofs) = qToBigEndian(quint32((0x8100 << 16)|vlan_[i]));
ofs += 4;
}
*(quint16*)(p + ofs) = qToBigEndian(type);
ofs += 2;
Q_ASSERT(ofs == encapSize());
_exit: _exit:
return; return;
} }
//
// Private Methods
//
quint64 Device::myMac()
{
if (data_.HasExtension(OstEmul::mac))
return data_.GetExtension(OstEmul::mac).addr();
return 0;
}
quint32 Device::myIp4()
{
if (data_.HasExtension(OstEmul::ip4))
return data_.GetExtension(OstEmul::ip4).addr();
return 0; // FIXME: how to indicate that we don't have a IP?
}
void Device::transmitPacket(PacketBuffer *pktBuf) void Device::transmitPacket(PacketBuffer *pktBuf)
{ {
deviceManager_->transmitPacket(pktBuf); deviceManager_->transmitPacket(pktBuf);
} }
// We expect pktBuf to point to EthType on entry
void Device::receivePacket(PacketBuffer *pktBuf) void Device::receivePacket(PacketBuffer *pktBuf)
{ {
uchar *pktData = pktBuf->data(); quint16 ethType = qFromBigEndian<quint16>(pktBuf->data());
int offset = 0; pktBuf->pull(2);
Device *device;
const quint64 bcastMac = 0xffffffffffffULL;
quint64 dstMac;
quint16 ethType;
// We assume pkt is ethernet qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType);
// TODO: extend for other link layer types
// Extract dstMac
dstMac = qFromBigEndian<quint32>(pktData + offset);
offset += 4;
dstMac = (dstMac << 16) | qFromBigEndian<quint16>(pktData + offset);
offset += 2;
// Skip srcMac - don't care
offset += 6;
qDebug("dstMac %llx", dstMac);
// Is it destined for us?
device = macHash_.value(dstMac);
if (!device && (dstMac != bcastMac)) {
qDebug("%s: dstMac %llx is not us", __FUNCTION__, dstMac);
goto _exit;
}
ethType = qFromBigEndian<quint16>(pktData + offset);
offset += 2;
qDebug("%s: ethType 0x%x", __FUNCTION__, ethType);
switch(ethType) switch(ethType)
{ {
case 0x0806: // ARP case 0x0806: // ARP
pktBuf->pull(offset); receiveArp(pktBuf);
receiveArp(device, pktBuf);
break; break;
case 0x8100: // VLAN
case 0x0800: // IPv4 case 0x0800: // IPv4
case 0x86dd: // IPv6 case 0x86dd: // IPv6
default: default:
break; break;
} }
_exit:
return;
} }
void Device::receiveArp(Device *device, PacketBuffer *pktBuf) //
// Private Methods
//
void Device::receiveArp(PacketBuffer *pktBuf)
{ {
PacketBuffer *rspPkt;
uchar *pktData = pktBuf->data(); uchar *pktData = pktBuf->data();
int offset = 0; int offset = 0;
quint16 hwType, protoType; quint16 hwType, protoType;
@ -199,6 +182,15 @@ void Device::receiveArp(Device *device, PacketBuffer *pktBuf)
quint64 srcMac, tgtMac; quint64 srcMac, tgtMac;
quint32 srcIp, tgtIp; quint32 srcIp, tgtIp;
// Extract tgtIp first to check quickly if this packet is for us or not
tgtIp = qFromBigEndian<quint32>(pktData + 24);
if (tgtIp != ip4_) {
qDebug("tgtIp %s is not me %s",
qPrintable(QHostAddress(tgtIp).toString()),
qPrintable(QHostAddress(ip4_).toString()));
return;
}
// Extract annd verify ARP packet contents // Extract annd verify ARP packet contents
hwType = qFromBigEndian<quint16>(pktData + offset); hwType = qFromBigEndian<quint16>(pktData + offset);
offset += 2; offset += 2;
@ -236,43 +228,33 @@ void Device::receiveArp(Device *device, PacketBuffer *pktBuf)
tgtMac = (tgtMac << 16) | qFromBigEndian<quint16>(pktData + offset); tgtMac = (tgtMac << 16) | qFromBigEndian<quint16>(pktData + offset);
offset += 2; offset += 2;
tgtIp = qFromBigEndian<quint32>(pktData + offset);
offset += 4;
switch (opCode) switch (opCode)
{ {
case 1: // ARP Request case 1: // ARP Request
if (!device) rspPkt = new PacketBuffer;
device = ip4Hash_.value(tgtIp); rspPkt->reserve(encapSize());
if (device->myIp4() == tgtIp) { pktData = rspPkt->put(28);
PacketBuffer *pktBuf = new PacketBuffer; if (pktData) {
uchar *p; // HTYP, PTYP
*(quint32*)(pktData ) = qToBigEndian(quint32(0x00010800));
pktBuf->reserve(device->encapSize()); // HLEN, PLEN, OPER
p = pktBuf->put(28); // FIXME: hardcoding *(quint32*)(pktData+ 4) = qToBigEndian(quint32(0x06040002));
if (p) { // Source H/W Addr, Proto Addr
// HTYP, PTYP *(quint32*)(pktData+ 8) = qToBigEndian(quint32(mac_ >> 16));
*(quint32*)(p ) = qToBigEndian(quint32(0x00010800)); *(quint16*)(pktData+12) = qToBigEndian(quint16(mac_ & 0xffff));
// HLEN, PLEN, OPER *(quint32*)(pktData+14) = qToBigEndian(ip4_);
*(quint32*)(p+ 4) = qToBigEndian(quint32(0x06040002)); // Target H/W Addr, Proto Addr
// Source H/W Addr, Proto Addr *(quint32*)(pktData+18) = qToBigEndian(quint32(srcMac >> 16));
*(quint32*)(p+ 8) = qToBigEndian( *(quint16*)(pktData+22) = qToBigEndian(quint16(srcMac & 0xffff));
quint32(device->myMac() >> 16)); *(quint32*)(pktData+24) = qToBigEndian(srcIp);
*(quint16*)(p+12) = qToBigEndian(
quint16(device->myMac() & 0xffff));
*(quint32*)(p+14) = qToBigEndian(tgtIp);
// Target H/W Addr, Proto Addr
*(quint32*)(p+18) = qToBigEndian(quint32(srcMac >> 16));
*(quint16*)(p+22) = qToBigEndian(quint16(srcMac & 0xffff));
*(quint32*)(p+24) = qToBigEndian(srcIp);
}
device->encap(pktBuf, srcMac, 0x0806);
device->transmitPacket(pktBuf);
qDebug("Sent ARP Reply for srcIp/tgtIp=0x%x/0x%x",
srcIp, tgtIp);
} }
encap(rspPkt, srcMac, 0x0806);
transmitPacket(rspPkt);
qDebug("Sent ARP Reply for srcIp/tgtIp=%s/%s",
qPrintable(QHostAddress(srcIp).toString()),
qPrintable(QHostAddress(tgtIp).toString()));
break; break;
case 2: // ARP Response case 2: // ARP Response
default: default:

View File

@ -22,45 +22,45 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "../common/protocol.pb.h" #include "../common/protocol.pb.h"
#include <QHash> #include <QByteArray>
class DeviceManager; class DeviceManager;
class PacketBuffer; class PacketBuffer;
typedef QByteArray DeviceKey;
class Device class Device
{ {
public: public:
Device(quint32 id, DeviceManager *deviceManager); Device(DeviceManager *deviceManager);
~Device();
quint32 id(); void setVlan(int index, quint16 vlan);
void setMac(quint64 mac);
void setIp4(quint32 address, int prefixLength);
QString config();
void protoDataCopyFrom(const OstProto::Device &device); DeviceKey key();
void protoDataCopyInto(OstProto::Device &device) const; void clearKey();
int encapSize(); int encapSize();
void encap(PacketBuffer *pktBuf, quint64 dstMac, quint16 type); void encap(PacketBuffer *pktBuf, quint64 dstMac, quint16 type);
// receivePacket() is a class method 'coz we don't have the target void receivePacket(PacketBuffer *pktBuf);
// device yet; transmitPacket() is always from a particular device
void transmitPacket(PacketBuffer *pktBuf); void transmitPacket(PacketBuffer *pktBuf);
static void receivePacket(PacketBuffer *pktBuf);
private: // methods private: // methods
// receiveArp() is a class method 'coz ARP request is broadcast, so void receiveArp(PacketBuffer *pktBuf);
// we can't identify the target device till we parse the ARP header
static void receiveArp(Device *device, PacketBuffer *pktBuf);
quint64 myMac();
quint32 myIp4();
private: // data private: // data
// Class data
static QHash<quint64, Device*> macHash_;
static QHash<quint32, Device*> ip4Hash_;
DeviceManager *deviceManager_; DeviceManager *deviceManager_;
OstProto::Device data_;
int numVlanTags_;
quint16 vlan_[4]; // FIXME: vlan tpid
quint64 mac_;
quint32 ip4_;
int ip4PrefixLength_;
DeviceKey key_;
}; };
#endif #endif

View File

@ -23,9 +23,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "device.h" #include "device.h"
#include "packetbuffer.h" #include "packetbuffer.h"
#include "../common/emulproto.pb.h"
#include <qendian.h> #include <qendian.h>
// FIXME: add lock to protect deviceList_ operations? const quint64 kBcastMac = 0xffffffffffffULL;
// FIXME: add lock to protect deviceGroupList_ operations?
DeviceManager::DeviceManager(AbstractPort *parent) DeviceManager::DeviceManager(AbstractPort *parent)
{ {
@ -36,6 +40,90 @@ DeviceManager::~DeviceManager()
{ {
foreach(Device *dev, deviceList_) foreach(Device *dev, deviceList_)
delete dev; delete dev;
foreach(OstProto::DeviceGroup *devGrp, deviceGroupList_)
delete devGrp;
}
int DeviceManager::deviceGroupCount()
{
return deviceGroupList_.size();
}
const OstProto::DeviceGroup* DeviceManager::deviceGroupAtIndex(int index)
{
if ((index < 0) || (index >= deviceGroupCount())) {
qWarning("%s: index %d out of range (0 - %d)", __FUNCTION__,
index, deviceGroupCount() - 1);
return NULL;
}
// Sort List by 'id', get the id at 'index' and then corresponding devGrp
return deviceGroupList_.value(deviceGroupList_.uniqueKeys().value(index));
}
const OstProto::DeviceGroup* DeviceManager::deviceGroup(uint deviceGroupId)
{
return deviceGroupList_.value(deviceGroupId);
}
bool DeviceManager::addDeviceGroup(uint deviceGroupId)
{
OstProto::DeviceGroup *deviceGroup;
if (deviceGroupList_.contains(deviceGroupId)) {
qWarning("%s: deviceGroup id %u already exists", __FUNCTION__,
deviceGroupId);
return false;
}
deviceGroup = new OstProto::DeviceGroup;
deviceGroup->mutable_device_group_id()->set_id(deviceGroupId);
deviceGroupList_.insert(deviceGroupId, deviceGroup);
enumerateDevices(deviceGroup, kAdd);
// Start emulation when first device is added
if ((deviceCount() == 1) && port_)
port_->startDeviceEmulation();
return true;
}
bool DeviceManager::deleteDeviceGroup(uint deviceGroupId)
{
OstProto::DeviceGroup *deviceGroup;
if (!deviceGroupList_.contains(deviceGroupId)) {
qWarning("%s: deviceGroup id %u does not exist", __FUNCTION__,
deviceGroupId);
return false;
}
deviceGroup = deviceGroupList_.take(deviceGroupId);
enumerateDevices(deviceGroup, kDelete);
delete deviceGroup;
// Stop emulation if no devices remain
if ((deviceCount() == 0) && port_)
port_->stopDeviceEmulation();
return true;
}
bool DeviceManager::modifyDeviceGroup(const OstProto::DeviceGroup *deviceGroup)
{
quint32 id = deviceGroup->device_group_id().id();
OstProto::DeviceGroup *myDeviceGroup = deviceGroupList_.value(id);
if (!myDeviceGroup) {
qWarning("%s: deviceGroup id %u does not exist", __FUNCTION__, id);
return false;
}
enumerateDevices(myDeviceGroup, kDelete);
myDeviceGroup->CopyFrom(*deviceGroup);
enumerateDevices(myDeviceGroup, kAdd);
return true;
} }
int DeviceManager::deviceCount() int DeviceManager::deviceCount()
@ -43,75 +131,155 @@ int DeviceManager::deviceCount()
return deviceList_.size(); return deviceList_.size();
} }
Device* DeviceManager::deviceAtIndex(int index)
{
if ((index < 0) || (index >= deviceCount())) {
qWarning("%s: index %d out of range (max %d)", __FUNCTION__,
index, deviceCount());
return NULL;
}
return deviceList_.value(deviceList_.uniqueKeys().value(index));
}
Device* DeviceManager::device(uint deviceId)
{
return deviceList_.value(deviceId);
}
bool DeviceManager::addDevice(uint deviceId)
{
Device *device;
if (deviceList_.contains(deviceId)) {
qWarning("%s: device id %u already exists", __FUNCTION__, deviceId);
return false;
}
device = new Device(deviceId, this);
deviceList_.insert(deviceId, device);
if ((deviceCount() == 1) && port_)
port_->startDeviceEmulation();
return true;
}
bool DeviceManager::deleteDevice(uint deviceId)
{
if (!deviceList_.contains(deviceId)) {
qWarning("%s: device id %u does not exist", __FUNCTION__, deviceId);
return false;
}
delete deviceList_.take(deviceId);
if ((deviceCount() == 0) && port_)
port_->stopDeviceEmulation();
return true;
}
bool DeviceManager::modifyDevice(const OstProto::Device *device)
{
quint32 id = device->device_id().id();
Device *myDevice = deviceList_.value(id);
if (!myDevice) {
qWarning("%s: device id %u does not exist", __FUNCTION__, id);
return false;
}
myDevice->protoDataCopyFrom(*device);
return true;
}
void DeviceManager::receivePacket(PacketBuffer *pktBuf) void DeviceManager::receivePacket(PacketBuffer *pktBuf)
{ {
Device::receivePacket(pktBuf); uchar *pktData = pktBuf->data();
int offset = 0;
Device dk(this);
Device *device;
quint64 dstMac;
quint16 ethType;
quint16 vlan;
int idx = 0;
// We assume pkt is ethernet
// TODO: extend for other link layer types
// FIXME: validate before extracting if the offset is within pktLen
// Extract dstMac
dstMac = qFromBigEndian<quint32>(pktData + offset);
offset += 4;
dstMac = (dstMac << 16) | qFromBigEndian<quint16>(pktData + offset);
dk.setMac(dstMac);
offset += 2;
// Skip srcMac - don't care
offset += 6;
qDebug("dstMac %llx", dstMac);
_eth_type:
// Extract EthType
ethType = qFromBigEndian<quint16>(pktData + offset);
qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType);
if (ethType == 0x8100) {
offset += 2;
vlan = qFromBigEndian<quint16>(pktData + offset);
dk.setVlan(idx++, vlan);
offset += 2;
qDebug("%s: idx: %d vlan 0x%x", __FUNCTION__, idx, vlan);
goto _eth_type;
}
pktBuf->pull(offset);
if (dstMac == kBcastMac) {
QList<Device*> list = bcastList_.values(dk.key());
foreach(Device *device, list)
device->receivePacket(pktBuf);
goto _exit;
}
// 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;
} }
void DeviceManager::transmitPacket(PacketBuffer *pktBuf) void DeviceManager::transmitPacket(PacketBuffer *pktBuf)
{ {
port_->sendEmulationPacket(pktBuf); port_->sendEmulationPacket(pktBuf);
} }
void DeviceManager::enumerateDevices(
const OstProto::DeviceGroup *deviceGroup,
Operation oper)
{
Device dk(this);
OstEmul::VlanEmulation pbVlan = deviceGroup->GetExtension(OstEmul::vlan);
OstEmul::Device pbDevice = deviceGroup->GetExtension(OstEmul::device);
int numTags = pbVlan.stack_size();
int vlanCount = 1;
for (int i = 0; i < numTags; i++)
vlanCount *= pbVlan.stack(i).count();
// If we have no vlans, we still have the non-vlan-segmented LAN
if (vlanCount == 0)
vlanCount = 1;
for (int i = 0; i < vlanCount; i++) {
for (int j = 0; j < numTags; j++) {
OstEmul::VlanEmulation::Vlan vlan = pbVlan.stack(j);
quint16 vlanAdd = i*vlan.step();
switch (vlan.mode()) {
case OstEmul::VlanEmulation::kNoRepeat:
/* Do nothing */
break;
case OstEmul::VlanEmulation::kRepeat:
default:
vlanAdd %= vlan.count();
break;
}
dk.setVlan(j, vlan.vlan_tag() + vlanAdd);
}
for (uint k = 0; k < pbDevice.count(); k++) {
Device *device;
quint64 macAdd = i*k*pbDevice.mac().step();
quint32 ip4Add = i*k*pbDevice.ip4().step();
switch (pbDevice.mode()) {
case OstEmul::Device::kNoRepeat:
/* Do Nothing*/
break;
case OstEmul::Device::kRepeat:
default:
macAdd %= pbDevice.count();
ip4Add %= pbDevice.count();
break;
}
dk.setMac(pbDevice.mac().address() + macAdd);
dk.setIp4(pbDevice.ip4().address() + ip4Add,
pbDevice.ip4().prefix_length());
// TODO: fill in other pbDevice data
switch (oper) {
case kAdd:
device = new Device(this);
*device = dk;
deviceList_.insert(dk.key(), device);
dk.setMac(kBcastMac);
bcastList_.insert(dk.key(), device);
qDebug("enumerate (add): %s", qPrintable(device->config()));
break;
case kDelete:
device = deviceList_.take(dk.key());
qDebug("enumerate (del): %s", qPrintable(device->config()));
delete device;
dk.setMac(kBcastMac);
bcastList_.take(dk.key()); // device already freed above
break;
default:
Q_ASSERT(0); // Unreachable
}
} // foreach device
} // foreach vlan
}

View File

@ -20,14 +20,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#ifndef _DEVICE_MANAGER_H #ifndef _DEVICE_MANAGER_H
#define _DEVICE_MANAGER_H #define _DEVICE_MANAGER_H
#include "../common/protocol.pb.h" #include "device.h"
#include <QHash> #include <QHash>
#include <QMultiHash>
#include <QtGlobal> #include <QtGlobal>
class AbstractPort; class AbstractPort;
class Device;
class PacketBuffer; class PacketBuffer;
namespace OstProto {
class DeviceGroup;
};
class DeviceManager class DeviceManager
{ {
@ -35,19 +38,30 @@ public:
DeviceManager(AbstractPort *parent = 0); DeviceManager(AbstractPort *parent = 0);
~DeviceManager(); ~DeviceManager();
int deviceCount(); int deviceGroupCount();
Device* deviceAtIndex(int index); const OstProto::DeviceGroup* deviceGroupAtIndex(int index);
Device* device(uint deviceId); const OstProto::DeviceGroup* deviceGroup(uint deviceGroupId);
bool addDevice(uint deviceId); bool addDeviceGroup(uint deviceGroupId);
bool deleteDevice(uint deviceId); bool deleteDeviceGroup(uint deviceGroupId);
bool modifyDevice(const OstProto::Device *device); bool modifyDeviceGroup(const OstProto::DeviceGroup *deviceGroup);
int deviceCount();
void receivePacket(PacketBuffer *pktBuf); void receivePacket(PacketBuffer *pktBuf);
void transmitPacket(PacketBuffer *pktBuf); void transmitPacket(PacketBuffer *pktBuf);
private: private:
enum Operation { kAdd, kDelete };
void enumerateDevices(
const OstProto::DeviceGroup *deviceGroup,
Operation oper);
AbstractPort *port_; AbstractPort *port_;
QHash<uint, Device*> deviceList_; QHash<uint, OstProto::DeviceGroup*> deviceGroupList_;
QHash<DeviceKey, Device*> deviceList_;
QMultiHash<DeviceKey, Device*> bcastList_;
}; };
#endif #endif

View File

@ -33,8 +33,8 @@ LIBS += -lprotobuf
HEADERS += drone.h \ HEADERS += drone.h \
myservice.h myservice.h
SOURCES += \ SOURCES += \
device.cpp \
devicemanager.cpp \ devicemanager.cpp \
device.cpp \
drone_main.cpp \ drone_main.cpp \
drone.cpp \ drone.cpp \
portmanager.cpp \ portmanager.cpp \

View File

@ -602,9 +602,10 @@ _invalid_version:
* streams/ports and devices independent? * streams/ports and devices independent?
* =================================================================== * ===================================================================
*/ */
void MyService::getDeviceIdList(::google::protobuf::RpcController* controller, void MyService::getDeviceGroupIdList(
::google::protobuf::RpcController* controller,
const ::OstProto::PortId* request, const ::OstProto::PortId* request,
::OstProto::DeviceIdList* response, ::OstProto::DeviceGroupIdList* response,
::google::protobuf::Closure* done) ::google::protobuf::Closure* done)
{ {
DeviceManager *devMgr; DeviceManager *devMgr;
@ -620,12 +621,12 @@ void MyService::getDeviceIdList(::google::protobuf::RpcController* controller,
response->mutable_port_id()->set_id(portId); response->mutable_port_id()->set_id(portId);
portLock[portId]->lockForRead(); portLock[portId]->lockForRead();
for (int i = 0; i < devMgr->deviceCount(); i++) for (int i = 0; i < devMgr->deviceGroupCount(); i++)
{ {
OstProto::DeviceId *d; OstProto::DeviceGroupId *dgid;
d = response->add_device_id(); dgid = response->add_device_group_id();
d->set_id(devMgr->deviceAtIndex(i)->id()); dgid->CopyFrom(devMgr->deviceGroupAtIndex(i)->device_group_id());
} }
portLock[portId]->unlock(); portLock[portId]->unlock();
@ -637,9 +638,10 @@ _invalid_port:
done->Run(); done->Run();
} }
void MyService::getDeviceConfig(::google::protobuf::RpcController* controller, void MyService::getDeviceGroupConfig(
const ::OstProto::DeviceIdList* request, ::google::protobuf::RpcController* controller,
::OstProto::DeviceConfigList* response, const ::OstProto::DeviceGroupIdList* request,
::OstProto::DeviceGroupConfigList* response,
::google::protobuf::Closure* done) ::google::protobuf::Closure* done)
{ {
DeviceManager *devMgr; DeviceManager *devMgr;
@ -655,17 +657,15 @@ void MyService::getDeviceConfig(::google::protobuf::RpcController* controller,
response->mutable_port_id()->set_id(portId); response->mutable_port_id()->set_id(portId);
portLock[portId]->lockForRead(); portLock[portId]->lockForRead();
for (int i = 0; i < request->device_id_size(); i++) for (int i = 0; i < request->device_group_id_size(); i++)
{ {
Device *device; const OstProto::DeviceGroup *dg;
OstProto::Device *d;
device = devMgr->device(request->device_id(i).id()); dg = devMgr->deviceGroup(request->device_group_id(i).id());
if (!device) if (!dg)
continue; //! \todo(LOW): Partial status of RPC continue; //! \todo(LOW): Partial status of RPC
d = response->add_device(); response->add_device_group()->CopyFrom(*dg);
device->protoDataCopyInto(*d);
} }
portLock[portId]->unlock(); portLock[portId]->unlock();
@ -677,8 +677,9 @@ _invalid_port:
done->Run(); done->Run();
} }
void MyService::addDevice(::google::protobuf::RpcController* controller, void MyService::addDeviceGroup(
const ::OstProto::DeviceIdList* request, ::google::protobuf::RpcController* controller,
const ::OstProto::DeviceGroupIdList* request,
::OstProto::Ack* /*response*/, ::OstProto::Ack* /*response*/,
::google::protobuf::Closure* done) ::google::protobuf::Closure* done)
{ {
@ -699,16 +700,16 @@ void MyService::addDevice(::google::protobuf::RpcController* controller,
#endif #endif
portLock[portId]->lockForWrite(); portLock[portId]->lockForWrite();
for (int i = 0; i < request->device_id_size(); i++) for (int i = 0; i < request->device_group_id_size(); i++)
{ {
quint32 id = request->device_id(i).id(); quint32 id = request->device_group_id(i).id();
Device *device = devMgr->device(id); const OstProto::DeviceGroup *dg = devMgr->deviceGroup(id);
// If device with same id as in request exists already ==> error!! // If device group with same id as in request exists already ==> error!
if (device) if (dg)
continue; //! \todo (LOW): Partial status of RPC continue; //! \todo (LOW): Partial status of RPC
devMgr->addDevice(id); devMgr->addDeviceGroup(id);
} }
portLock[portId]->unlock(); portLock[portId]->unlock();
@ -729,8 +730,9 @@ _exit:
done->Run(); done->Run();
} }
void MyService::deleteDevice(::google::protobuf::RpcController* controller, void MyService::deleteDeviceGroup(
const ::OstProto::DeviceIdList* request, ::google::protobuf::RpcController* controller,
const ::OstProto::DeviceGroupIdList* request,
::OstProto::Ack* /*response*/, ::OstProto::Ack* /*response*/,
::google::protobuf::Closure* done) ::google::protobuf::Closure* done)
{ {
@ -751,8 +753,8 @@ void MyService::deleteDevice(::google::protobuf::RpcController* controller,
#endif #endif
portLock[portId]->lockForWrite(); portLock[portId]->lockForWrite();
for (int i = 0; i < request->device_id_size(); i++) for (int i = 0; i < request->device_group_id_size(); i++)
devMgr->deleteDevice(request->device_id(i).id()); devMgr->deleteDeviceGroup(request->device_group_id(i).id());
portLock[portId]->unlock(); portLock[portId]->unlock();
//! \todo (LOW): fill-in response "Ack"???? //! \todo (LOW): fill-in response "Ack"????
@ -771,8 +773,9 @@ _exit:
done->Run(); done->Run();
} }
void MyService::modifyDevice(::google::protobuf::RpcController* controller, void MyService::modifyDeviceGroup(
const ::OstProto::DeviceConfigList* request, ::google::protobuf::RpcController* controller,
const ::OstProto::DeviceGroupConfigList* request,
::OstProto::Ack* /*response*/, ::OstProto::Ack* /*response*/,
::google::protobuf::Closure* done) ::google::protobuf::Closure* done)
{ {
@ -793,8 +796,8 @@ void MyService::modifyDevice(::google::protobuf::RpcController* controller,
#endif #endif
portLock[portId]->lockForWrite(); portLock[portId]->lockForWrite();
for (int i = 0; i < request->device_size(); i++) for (int i = 0; i < request->device_group_size(); i++)
devMgr->modifyDevice(&request->device(i)); devMgr->modifyDeviceGroup(&request->device_group(i));
portLock[portId]->unlock(); portLock[portId]->unlock();
// FIXME: check for overlaps between devices? // FIXME: check for overlaps between devices?

View File

@ -105,25 +105,30 @@ public:
::OstProto::VersionCompatibility* response, ::OstProto::VersionCompatibility* response,
::google::protobuf::Closure* done); ::google::protobuf::Closure* done);
// Device and Protocol Emulation // DeviceGroup and Protocol Emulation
virtual void getDeviceIdList(::google::protobuf::RpcController* controller, virtual void getDeviceGroupIdList(
::google::protobuf::RpcController* controller,
const ::OstProto::PortId* request, const ::OstProto::PortId* request,
::OstProto::DeviceIdList* response, ::OstProto::DeviceGroupIdList* response,
::google::protobuf::Closure* done); ::google::protobuf::Closure* done);
virtual void getDeviceConfig(::google::protobuf::RpcController* controller, virtual void getDeviceGroupConfig(
const ::OstProto::DeviceIdList* request, ::google::protobuf::RpcController* controller,
::OstProto::DeviceConfigList* response, const ::OstProto::DeviceGroupIdList* request,
::OstProto::DeviceGroupConfigList* response,
::google::protobuf::Closure* done); ::google::protobuf::Closure* done);
virtual void addDevice(::google::protobuf::RpcController* controller, virtual void addDeviceGroup(
const ::OstProto::DeviceIdList* request, ::google::protobuf::RpcController* controller,
const ::OstProto::DeviceGroupIdList* request,
::OstProto::Ack* response, ::OstProto::Ack* response,
::google::protobuf::Closure* done); ::google::protobuf::Closure* done);
virtual void deleteDevice(::google::protobuf::RpcController* controller, virtual void deleteDeviceGroup(
const ::OstProto::DeviceIdList* request, ::google::protobuf::RpcController* controller,
const ::OstProto::DeviceGroupIdList* request,
::OstProto::Ack* response, ::OstProto::Ack* response,
::google::protobuf::Closure* done); ::google::protobuf::Closure* done);
virtual void modifyDevice(::google::protobuf::RpcController* controller, virtual void modifyDeviceGroup(
const ::OstProto::DeviceConfigList* request, ::google::protobuf::RpcController* controller,
const ::OstProto::DeviceGroupConfigList* request,
::OstProto::Ack* response, ::OstProto::Ack* response,
::google::protobuf::Closure* done); ::google::protobuf::Closure* done);
signals: signals:

View File

@ -139,54 +139,54 @@ try:
# configure emulated device(s) on tx/rx ports # # configure emulated device(s) on tx/rx ports #
#---------------------------------------------# #---------------------------------------------#
# delete existing devices, if any, on tx port # delete existing devices, if any, on tx port
did_list = drone.getDeviceIdList(tx_port.port_id[0]) dgid_list = drone.getDeviceGroupIdList(tx_port.port_id[0])
drone.deleteDevice(did_list) drone.deleteDeviceGroup(dgid_list)
# add a emulated device on tx port # add a emulated device on tx port
device_id = ost_pb.DeviceIdList() dgid_list = ost_pb.DeviceGroupIdList()
device_id.port_id.CopyFrom(tx_port.port_id[0]) dgid_list.port_id.CopyFrom(tx_port.port_id[0])
device_id.device_id.add().id = 1 dgid_list.device_group_id.add().id = 1
log.info('adding tx_device %d' % device_id.device_id[0].id) log.info('adding tx device_group %d' % dgid_list.device_group_id[0].id)
drone.addDevice(device_id) drone.addDeviceGroup(dgid_list)
# configure the device # configure the device
device_cfg = ost_pb.DeviceConfigList() devgrp_cfg = ost_pb.DeviceGroupConfigList()
device_cfg.port_id.CopyFrom(tx_port.port_id[0]) devgrp_cfg.port_id.CopyFrom(tx_port.port_id[0])
d = device_cfg.device.add() dg = devgrp_cfg.device_group.add()
d.device_id.id = device_id.device_id[0].id dg.device_group_id.id = dgid_list.device_group_id[0].id
d.core.name = "Host1" dg.core.name = "Host1"
d.Extensions[emul.mac].addr = 0x000102030001 d = dg.Extensions[emul.device]
ip = d.Extensions[emul.ip4] d.mac.address = 0x000102030001
ip.addr = 0x0a0a0164 d.ip4.address = 0x0a0a0164
ip.prefix_length = 24 d.ip4.prefix_length = 24
ip.gateway = 0x0a0a0101 d.ip4.default_gateway = 0x0a0a0101
drone.modifyDevice(device_cfg) drone.modifyDeviceGroup(devgrp_cfg)
# delete existing devices, if any, on rx port # delete existing devices, if any, on rx port
did_list = drone.getDeviceIdList(rx_port.port_id[0]) dgid_list = drone.getDeviceGroupIdList(rx_port.port_id[0])
drone.deleteDevice(did_list) drone.deleteDeviceGroup(dgid_list)
# add a emulated device on rx port # add a emulated device on rx port
device_id = ost_pb.DeviceIdList() dgid_list = ost_pb.DeviceGroupIdList()
device_id.port_id.CopyFrom(rx_port.port_id[0]) dgid_list.port_id.CopyFrom(rx_port.port_id[0])
device_id.device_id.add().id = 1 dgid_list.device_group_id.add().id = 1
log.info('adding rx_device %d' % device_id.device_id[0].id) log.info('adding rx device_group %d' % dgid_list.device_group_id[0].id)
drone.addDevice(device_id) drone.addDeviceGroup(dgid_list)
# configure the device # configure the device
device_cfg = ost_pb.DeviceConfigList() devgrp_cfg = ost_pb.DeviceGroupConfigList()
device_cfg.port_id.CopyFrom(rx_port.port_id[0]) devgrp_cfg.port_id.CopyFrom(rx_port.port_id[0])
d = device_cfg.device.add() dg = devgrp_cfg.device_group.add()
d.device_id.id = device_id.device_id[0].id dg.device_group_id.id = dgid_list.device_group_id[0].id
d.core.name = "Host2" dg.core.name = "Host1"
d.Extensions[emul.mac].addr = 0x000102030002 d = dg.Extensions[emul.device]
ip = d.Extensions[emul.ip4] d.mac.address = 0x000102030002
ip.addr = 0x0a0a0264 d.ip4.address = 0x0a0a0264
ip.prefix_length = 24 d.ip4.prefix_length = 24
ip.gateway = 0x0a0a0201 d.ip4.default_gateway = 0x0a0a0201
drone.modifyDevice(device_cfg) drone.modifyDeviceGroup(devgrp_cfg)
#--------------------------------------# #--------------------------------------#
# configure traffic stream(s) # configure traffic stream(s)
@ -210,7 +210,7 @@ try:
s.core.is_enabled = True s.core.is_enabled = True
#s.core.frame_len = 128 #s.core.frame_len = 128
s.control.packets_per_sec = 20 s.control.packets_per_sec = 20
s.control.num_packets = 100 s.control.num_packets = 10
# setup stream protocols as mac:eth2:ip4:udp:payload # setup stream protocols as mac:eth2:ip4:udp:payload
p = s.protocol.add() p = s.protocol.add()
@ -280,10 +280,10 @@ try:
drone.deleteStream(stream_id) drone.deleteStream(stream_id)
# delete devices # delete devices
did_list = drone.getDeviceIdList(tx_port.port_id[0]) dgid_list = drone.getDeviceGroupIdList(tx_port.port_id[0])
drone.deleteDevice(did_list) drone.deleteDeviceGroup(dgid_list)
did_list = drone.getDeviceIdList(rx_port.port_id[0]) dgid_list = drone.getDeviceGroupIdList(rx_port.port_id[0])
drone.deleteDevice(did_list) drone.deleteDeviceGroup(dgid_list)
# bye for now # bye for now
drone.disconnect() drone.disconnect()