Feature (contd.): Device Emulation - added resolveNeighbors() and related RPC code and implementation to send out ARP Requests and build the ARP Table on each device
This commit is contained in:
parent
8c41b536a4
commit
9302e5f17c
@ -38,6 +38,8 @@ message VlanEmulation {
|
|||||||
repeated Vlan stack = 1; // outer to inner
|
repeated Vlan stack = 1; // outer to inner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: encapsulate VlanEmulation inside a new encapEmulation message?
|
||||||
|
|
||||||
extend OstProto.DeviceGroup {
|
extend OstProto.DeviceGroup {
|
||||||
optional VlanEmulation vlan = 200;
|
optional VlanEmulation vlan = 200;
|
||||||
}
|
}
|
||||||
@ -60,6 +62,8 @@ message Ip4Emulation {
|
|||||||
// FIXME: step for gateway?
|
// FIXME: step for gateway?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: should we have a 'message Ip6Address' with 'hi', 'lo' fields
|
||||||
|
// and use that everywhere instead of having to define _hi and _lo?
|
||||||
message Ip6Emulation {
|
message Ip6Emulation {
|
||||||
optional uint64 address_hi = 1;
|
optional uint64 address_hi = 1;
|
||||||
optional uint64 address_lo = 2;
|
optional uint64 address_lo = 2;
|
||||||
@ -72,6 +76,7 @@ message Ip6Emulation {
|
|||||||
// FIXME: step for gateway?
|
// FIXME: step for gateway?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: move fields of Device directly inside 'extend OstProto.DeviceGroup'
|
||||||
message Device {
|
message Device {
|
||||||
optional MacEmulation mac = 1;
|
optional MacEmulation mac = 1;
|
||||||
optional Ip4Emulation ip4 = 2;
|
optional Ip4Emulation ip4 = 2;
|
||||||
@ -83,3 +88,28 @@ message Device {
|
|||||||
extend OstProto.DeviceGroup {
|
extend OstProto.DeviceGroup {
|
||||||
optional Device device = 201;
|
optional Device device = 201;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: rename xxxEntry to something better?
|
||||||
|
message ArpEntry {
|
||||||
|
optional uint32 ip4 = 1;
|
||||||
|
optional uint64 mac = 2;
|
||||||
|
// FIXME: add state/status field?
|
||||||
|
}
|
||||||
|
|
||||||
|
message NdEntry {
|
||||||
|
optional uint64 ip6_hi = 1;
|
||||||
|
optional uint64 ip6_lo = 2;
|
||||||
|
optional uint64 mac = 3;
|
||||||
|
// FIXME: add state/status field?
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: change message name
|
||||||
|
message DeviceNeighbors {
|
||||||
|
optional uint32 device_index = 1;
|
||||||
|
repeated ArpEntry arp = 2;
|
||||||
|
repeated NdEntry nd = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
extend OstProto.DeviceNeighborList {
|
||||||
|
repeated DeviceNeighbors devices = 100;
|
||||||
|
}
|
||||||
|
@ -281,6 +281,7 @@ message Notification {
|
|||||||
* FIXME: What will be the contents of a default device created by addDeviceGroup()?
|
* 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'?
|
* FIXME: rename 'DeviceGroup'?
|
||||||
|
* FIXME: review RPC abstractions - esp. Neighbor related
|
||||||
*/
|
*/
|
||||||
message DeviceGroupId {
|
message DeviceGroupId {
|
||||||
required uint32 id = 1;
|
required uint32 id = 1;
|
||||||
@ -307,6 +308,12 @@ message DeviceGroupConfigList {
|
|||||||
repeated DeviceGroup device_group = 2;
|
repeated DeviceGroup device_group = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message DeviceNeighborList {
|
||||||
|
required PortId port_id = 1;
|
||||||
|
|
||||||
|
extensions 100 to 199;
|
||||||
|
}
|
||||||
|
|
||||||
service OstService {
|
service OstService {
|
||||||
rpc getPortIdList(Void) returns (PortIdList);
|
rpc getPortIdList(Void) returns (PortIdList);
|
||||||
rpc getPortConfig(PortIdList) returns (PortConfigList);
|
rpc getPortConfig(PortIdList) returns (PortConfigList);
|
||||||
@ -336,5 +343,10 @@ service OstService {
|
|||||||
rpc addDeviceGroup(DeviceGroupIdList) returns (Ack);
|
rpc addDeviceGroup(DeviceGroupIdList) returns (Ack);
|
||||||
rpc deleteDeviceGroup(DeviceGroupIdList) returns (Ack);
|
rpc deleteDeviceGroup(DeviceGroupIdList) returns (Ack);
|
||||||
rpc modifyDeviceGroup(DeviceGroupConfigList) returns (Ack);
|
rpc modifyDeviceGroup(DeviceGroupConfigList) returns (Ack);
|
||||||
|
|
||||||
|
rpc resolveDeviceNeighbors(PortIdList) returns (Ack);
|
||||||
|
rpc clearDeviceNeighbors(PortIdList) returns (Ack);
|
||||||
|
// FIXME: take PortIdList instead of PortId?
|
||||||
|
rpc getDeviceNeighbors(PortId) returns (DeviceNeighborList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#include "../common/abstractprotocol.h"
|
#include "../common/abstractprotocol.h"
|
||||||
#include "../common/streambase.h"
|
#include "../common/streambase.h"
|
||||||
#include "devicemanager.h"
|
#include "devicemanager.h"
|
||||||
|
#include "packetbuffer.h"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
@ -572,6 +573,41 @@ void AbstractPort::updatePacketListInterleaved()
|
|||||||
isSendQueueDirty_ = false;
|
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)
|
void AbstractPort::stats(PortStats *stats)
|
||||||
{
|
{
|
||||||
stats->rxPkts = (stats_.rxPkts >= epochStats_.rxPkts) ?
|
stats->rxPkts = (stats_.rxPkts >= epochStats_.rxPkts) ?
|
||||||
|
@ -100,6 +100,9 @@ public:
|
|||||||
virtual void stopDeviceEmulation() = 0;
|
virtual void stopDeviceEmulation() = 0;
|
||||||
virtual int sendEmulationPacket(PacketBuffer *pktBuf) = 0;
|
virtual int sendEmulationPacket(PacketBuffer *pktBuf) = 0;
|
||||||
|
|
||||||
|
void clearDeviceNeighbors();
|
||||||
|
void resolveDeviceNeighbors();
|
||||||
|
|
||||||
void stats(PortStats *stats);
|
void stats(PortStats *stats);
|
||||||
void resetStats() { epochStats_ = stats_; }
|
void resetStats() { epochStats_ = stats_; }
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
const int kBaseHex = 16;
|
const int kBaseHex = 16;
|
||||||
const int kMaxVlan = 4;
|
const int kMaxVlan = 4;
|
||||||
|
const quint64 kBcastMac = 0xffffffffffffULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE:
|
* NOTE:
|
||||||
@ -79,10 +80,11 @@ void Device::setMac(quint64 mac)
|
|||||||
memcpy(key_.data() + ofs, (char*)&mac, sizeof(mac));
|
memcpy(key_.data() + ofs, (char*)&mac, sizeof(mac));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::setIp4(quint32 address, int prefixLength)
|
void Device::setIp4(quint32 address, int prefixLength, quint32 gateway)
|
||||||
{
|
{
|
||||||
ip4_ = address;
|
ip4_ = address;
|
||||||
ip4PrefixLength_ = prefixLength;
|
ip4PrefixLength_ = prefixLength;
|
||||||
|
ip4Gateway_ = gateway;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Device::config()
|
QString Device::config()
|
||||||
@ -142,11 +144,6 @@ _exit:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::transmitPacket(PacketBuffer *pktBuf)
|
|
||||||
{
|
|
||||||
deviceManager_->transmitPacket(pktBuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We expect pktBuf to point to EthType on entry
|
// We expect pktBuf to point to EthType on entry
|
||||||
void Device::receivePacket(PacketBuffer *pktBuf)
|
void Device::receivePacket(PacketBuffer *pktBuf)
|
||||||
{
|
{
|
||||||
@ -170,6 +167,54 @@ void Device::receivePacket(PacketBuffer *pktBuf)
|
|||||||
pktBuf->push(2);
|
pktBuf->push(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Device::transmitPacket(PacketBuffer *pktBuf)
|
||||||
|
{
|
||||||
|
deviceManager_->transmitPacket(pktBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::clearNeighbors()
|
||||||
|
{
|
||||||
|
arpTable.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the Neighbor IP address for this to-be-transmitted pktBuf
|
||||||
|
// We expect pktBuf to point to EthType on entry
|
||||||
|
void Device::resolveNeighbor(PacketBuffer *pktBuf)
|
||||||
|
{
|
||||||
|
quint16 ethType = qFromBigEndian<quint16>(pktBuf->data());
|
||||||
|
pktBuf->pull(2);
|
||||||
|
|
||||||
|
qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType);
|
||||||
|
|
||||||
|
switch(ethType)
|
||||||
|
{
|
||||||
|
case 0x0800: // IPv4
|
||||||
|
sendArpRequest(pktBuf);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x86dd: // IPv6
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// FIXME: temporary hack till DeviceManager clones pbufs
|
||||||
|
pktBuf->push(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append this device's neighbors to the list
|
||||||
|
void Device::getNeighbors(OstEmul::DeviceNeighbors *neighbors)
|
||||||
|
{
|
||||||
|
QList<quint32> ipList = arpTable.keys();
|
||||||
|
QList<quint64> macList = arpTable.values();
|
||||||
|
|
||||||
|
Q_ASSERT(ipList.size() == macList.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < ipList.size(); i++) {
|
||||||
|
OstEmul::ArpEntry *arp = neighbors->add_arp();
|
||||||
|
arp->set_ip4(ipList.at(i));
|
||||||
|
arp->set_mac(macList.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Private Methods
|
// Private Methods
|
||||||
//
|
//
|
||||||
@ -233,6 +278,8 @@ void Device::receiveArp(PacketBuffer *pktBuf)
|
|||||||
switch (opCode)
|
switch (opCode)
|
||||||
{
|
{
|
||||||
case 1: // ARP Request
|
case 1: // ARP Request
|
||||||
|
arpTable.insert(srcIp, srcMac);
|
||||||
|
|
||||||
rspPkt = new PacketBuffer;
|
rspPkt = new PacketBuffer;
|
||||||
rspPkt->reserve(encapSize());
|
rspPkt->reserve(encapSize());
|
||||||
pktData = rspPkt->put(28);
|
pktData = rspPkt->put(28);
|
||||||
@ -259,6 +306,9 @@ void Device::receiveArp(PacketBuffer *pktBuf)
|
|||||||
qPrintable(QHostAddress(tgtIp).toString()));
|
qPrintable(QHostAddress(tgtIp).toString()));
|
||||||
break;
|
break;
|
||||||
case 2: // ARP Response
|
case 2: // ARP Response
|
||||||
|
arpTable.insert(srcIp, srcMac);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -269,3 +319,64 @@ _invalid_exit:
|
|||||||
qWarning("Invalid ARP content");
|
qWarning("Invalid ARP content");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send ARP request for the IPv4 packet in pktBuf
|
||||||
|
// pktBuf points to start of IP header
|
||||||
|
void Device::sendArpRequest(PacketBuffer *pktBuf)
|
||||||
|
{
|
||||||
|
PacketBuffer *reqPkt;
|
||||||
|
uchar *pktData = pktBuf->data();
|
||||||
|
int offset = 0;
|
||||||
|
int ipHdrLen = (pktData[offset] & 0x0F) << 2;
|
||||||
|
quint32 srcIp, dstIp, mask, tgtIp;
|
||||||
|
|
||||||
|
if (pktBuf->length() < ipHdrLen) {
|
||||||
|
qDebug("incomplete IPv4 header: expected %d, actual %d",
|
||||||
|
ipHdrLen, pktBuf->length());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract srcIp first to check quickly that this packet originates
|
||||||
|
// from this device
|
||||||
|
srcIp = qFromBigEndian<quint32>(pktData + ipHdrLen - 8);
|
||||||
|
if (srcIp != ip4_) {
|
||||||
|
qDebug("%s: srcIp %s is not me %s", __FUNCTION__,
|
||||||
|
qPrintable(QHostAddress(srcIp).toString()),
|
||||||
|
qPrintable(QHostAddress(ip4_).toString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_;
|
||||||
|
|
||||||
|
reqPkt = new PacketBuffer;
|
||||||
|
reqPkt->reserve(encapSize());
|
||||||
|
pktData = reqPkt->put(28);
|
||||||
|
if (pktData) {
|
||||||
|
// HTYP, PTYP
|
||||||
|
*(quint32*)(pktData ) = qToBigEndian(quint32(0x00010800));
|
||||||
|
// HLEN, PLEN, OPER
|
||||||
|
*(quint32*)(pktData+ 4) = qToBigEndian(quint32(0x06040001));
|
||||||
|
// Source H/W Addr, Proto Addr
|
||||||
|
*(quint32*)(pktData+ 8) = qToBigEndian(quint32(mac_ >> 16));
|
||||||
|
*(quint16*)(pktData+12) = qToBigEndian(quint16(mac_ & 0xffff));
|
||||||
|
*(quint32*)(pktData+14) = qToBigEndian(srcIp);
|
||||||
|
// Target H/W Addr, Proto Addr
|
||||||
|
*(quint32*)(pktData+18) = qToBigEndian(quint32(0));
|
||||||
|
*(quint16*)(pktData+22) = qToBigEndian(quint16(0));
|
||||||
|
*(quint32*)(pktData+24) = qToBigEndian(tgtIp);
|
||||||
|
}
|
||||||
|
|
||||||
|
encap(reqPkt, kBcastMac, 0x0806);
|
||||||
|
transmitPacket(reqPkt);
|
||||||
|
|
||||||
|
qDebug("Sent ARP Request for srcIp/tgtIp=%s/%s",
|
||||||
|
qPrintable(QHostAddress(srcIp).toString()),
|
||||||
|
qPrintable(QHostAddress(tgtIp).toString()));
|
||||||
|
}
|
||||||
|
@ -20,9 +20,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#ifndef _DEVICE_H
|
#ifndef _DEVICE_H
|
||||||
#define _DEVICE_H
|
#define _DEVICE_H
|
||||||
|
|
||||||
|
#include "../common/emulproto.pb.h"
|
||||||
#include "../common/protocol.pb.h"
|
#include "../common/protocol.pb.h"
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
class DeviceManager;
|
class DeviceManager;
|
||||||
class PacketBuffer;
|
class PacketBuffer;
|
||||||
@ -36,7 +38,7 @@ public:
|
|||||||
|
|
||||||
void setVlan(int index, quint16 vlan);
|
void setVlan(int index, quint16 vlan);
|
||||||
void setMac(quint64 mac);
|
void setMac(quint64 mac);
|
||||||
void setIp4(quint32 address, int prefixLength);
|
void setIp4(quint32 address, int prefixLength, quint32 gateway);
|
||||||
QString config();
|
QString config();
|
||||||
|
|
||||||
DeviceKey key();
|
DeviceKey key();
|
||||||
@ -48,8 +50,13 @@ public:
|
|||||||
void receivePacket(PacketBuffer *pktBuf);
|
void receivePacket(PacketBuffer *pktBuf);
|
||||||
void transmitPacket(PacketBuffer *pktBuf);
|
void transmitPacket(PacketBuffer *pktBuf);
|
||||||
|
|
||||||
|
void clearNeighbors();
|
||||||
|
void resolveNeighbor(PacketBuffer *pktBuf);
|
||||||
|
void getNeighbors(OstEmul::DeviceNeighbors *neighbors);
|
||||||
|
|
||||||
private: // methods
|
private: // methods
|
||||||
void receiveArp(PacketBuffer *pktBuf);
|
void receiveArp(PacketBuffer *pktBuf);
|
||||||
|
void sendArpRequest(PacketBuffer *pktBuf);
|
||||||
|
|
||||||
private: // data
|
private: // data
|
||||||
DeviceManager *deviceManager_;
|
DeviceManager *deviceManager_;
|
||||||
@ -59,8 +66,11 @@ private: // data
|
|||||||
quint64 mac_;
|
quint64 mac_;
|
||||||
quint32 ip4_;
|
quint32 ip4_;
|
||||||
int ip4PrefixLength_;
|
int ip4PrefixLength_;
|
||||||
|
quint32 ip4Gateway_;
|
||||||
|
|
||||||
DeviceKey key_;
|
DeviceKey key_;
|
||||||
|
|
||||||
|
QHash<quint32, quint64> arpTable;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -208,6 +208,94 @@ void DeviceManager::transmitPacket(PacketBuffer *pktBuf)
|
|||||||
port_->sendEmulationPacket(pktBuf);
|
port_->sendEmulationPacket(pktBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeviceManager::clearDeviceNeighbors()
|
||||||
|
{
|
||||||
|
foreach(Device *device, deviceList_)
|
||||||
|
device->clearNeighbors();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::getDeviceNeighbors(
|
||||||
|
OstProto::DeviceNeighborList *neighborList)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
foreach(Device *device, deviceList_) {
|
||||||
|
OstEmul::DeviceNeighbors *neighList =
|
||||||
|
neighborList->AddExtension(OstEmul::devices);
|
||||||
|
neighList->set_device_index(count++);
|
||||||
|
device->getNeighbors(neighList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This function is mostly a duplicate of receivePacket;
|
||||||
|
// can we have a single combined one?
|
||||||
|
void DeviceManager::resolveDeviceNeighbor(PacketBuffer *pktBuf)
|
||||||
|
{
|
||||||
|
uchar *pktData = pktBuf->data();
|
||||||
|
int offset = 0;
|
||||||
|
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
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
_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 %d", __FUNCTION__, idx, vlan);
|
||||||
|
goto _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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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::enumerateDevices(
|
void DeviceManager::enumerateDevices(
|
||||||
const OstProto::DeviceGroup *deviceGroup,
|
const OstProto::DeviceGroup *deviceGroup,
|
||||||
Operation oper)
|
Operation oper)
|
||||||
@ -240,7 +328,8 @@ void DeviceManager::enumerateDevices(
|
|||||||
|
|
||||||
dk.setMac(pbDevice.mac().address() + macAdd);
|
dk.setMac(pbDevice.mac().address() + macAdd);
|
||||||
dk.setIp4(pbDevice.ip4().address() + ip4Add,
|
dk.setIp4(pbDevice.ip4().address() + ip4Add,
|
||||||
pbDevice.ip4().prefix_length());
|
pbDevice.ip4().prefix_length(),
|
||||||
|
pbDevice.ip4().default_gateway());
|
||||||
|
|
||||||
// TODO: fill in other pbDevice data
|
// TODO: fill in other pbDevice data
|
||||||
|
|
||||||
|
@ -51,6 +51,10 @@ public:
|
|||||||
void receivePacket(PacketBuffer *pktBuf);
|
void receivePacket(PacketBuffer *pktBuf);
|
||||||
void transmitPacket(PacketBuffer *pktBuf);
|
void transmitPacket(PacketBuffer *pktBuf);
|
||||||
|
|
||||||
|
void clearDeviceNeighbors();
|
||||||
|
void resolveDeviceNeighbor(PacketBuffer *pktBuf);
|
||||||
|
void getDeviceNeighbors(OstProto::DeviceNeighborList *neighborList);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum Operation { kAdd, kDelete };
|
enum Operation { kAdd, kDelete };
|
||||||
|
|
||||||
|
@ -817,3 +817,85 @@ _invalid_port:
|
|||||||
_exit:
|
_exit:
|
||||||
done->Run();
|
done->Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyService::resolveDeviceNeighbors(
|
||||||
|
::google::protobuf::RpcController* controller,
|
||||||
|
const ::OstProto::PortIdList* request,
|
||||||
|
::OstProto::Ack* response,
|
||||||
|
::google::protobuf::Closure* done)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __PRETTY_FUNCTION__);
|
||||||
|
|
||||||
|
for (int i = 0; i < request->port_id_size(); i++)
|
||||||
|
{
|
||||||
|
int portId;
|
||||||
|
|
||||||
|
portId = request->port_id(i).id();
|
||||||
|
if ((portId < 0) || (portId >= portInfo.size()))
|
||||||
|
continue; //! \todo (LOW): partial RPC?
|
||||||
|
|
||||||
|
portLock[portId]->lockForWrite();
|
||||||
|
portInfo[portId]->resolveDeviceNeighbors();
|
||||||
|
portLock[portId]->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \todo (LOW): fill-in response "Ack"????
|
||||||
|
|
||||||
|
done->Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyService::clearDeviceNeighbors(
|
||||||
|
::google::protobuf::RpcController* controller,
|
||||||
|
const ::OstProto::PortIdList* request,
|
||||||
|
::OstProto::Ack* response,
|
||||||
|
::google::protobuf::Closure* done)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __PRETTY_FUNCTION__);
|
||||||
|
|
||||||
|
for (int i = 0; i < request->port_id_size(); i++)
|
||||||
|
{
|
||||||
|
int portId;
|
||||||
|
|
||||||
|
portId = request->port_id(i).id();
|
||||||
|
if ((portId < 0) || (portId >= portInfo.size()))
|
||||||
|
continue; //! \todo (LOW): partial RPC?
|
||||||
|
|
||||||
|
portLock[portId]->lockForWrite();
|
||||||
|
portInfo[portId]->clearDeviceNeighbors();
|
||||||
|
portLock[portId]->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \todo (LOW): fill-in response "Ack"????
|
||||||
|
|
||||||
|
done->Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyService::getDeviceNeighbors(
|
||||||
|
::google::protobuf::RpcController* controller,
|
||||||
|
const ::OstProto::PortId* request,
|
||||||
|
::OstProto::DeviceNeighborList* response,
|
||||||
|
::google::protobuf::Closure* done)
|
||||||
|
{
|
||||||
|
DeviceManager *devMgr;
|
||||||
|
int portId;
|
||||||
|
|
||||||
|
qDebug("In %s", __PRETTY_FUNCTION__);
|
||||||
|
|
||||||
|
portId = request->id();
|
||||||
|
if ((portId < 0) || (portId >= portInfo.size()))
|
||||||
|
goto _invalid_port;
|
||||||
|
|
||||||
|
devMgr = portInfo[portId]->deviceManager();
|
||||||
|
|
||||||
|
response->mutable_port_id()->set_id(portId);
|
||||||
|
portLock[portId]->lockForRead();
|
||||||
|
devMgr->getDeviceNeighbors(response);
|
||||||
|
portLock[portId]->unlock();
|
||||||
|
|
||||||
|
done->Run();
|
||||||
|
return;
|
||||||
|
|
||||||
|
_invalid_port:
|
||||||
|
controller->SetFailed("Invalid Port Id");
|
||||||
|
done->Run();
|
||||||
|
}
|
||||||
|
@ -131,6 +131,22 @@ public:
|
|||||||
const ::OstProto::DeviceGroupConfigList* request,
|
const ::OstProto::DeviceGroupConfigList* request,
|
||||||
::OstProto::Ack* response,
|
::OstProto::Ack* response,
|
||||||
::google::protobuf::Closure* done);
|
::google::protobuf::Closure* done);
|
||||||
|
|
||||||
|
virtual void resolveDeviceNeighbors(
|
||||||
|
::google::protobuf::RpcController* controller,
|
||||||
|
const ::OstProto::PortIdList* request,
|
||||||
|
::OstProto::Ack* response,
|
||||||
|
::google::protobuf::Closure* done);
|
||||||
|
virtual void clearDeviceNeighbors(
|
||||||
|
::google::protobuf::RpcController* controller,
|
||||||
|
const ::OstProto::PortIdList* request,
|
||||||
|
::OstProto::Ack* response,
|
||||||
|
::google::protobuf::Closure* done);
|
||||||
|
virtual void getDeviceNeighbors(
|
||||||
|
::google::protobuf::RpcController* controller,
|
||||||
|
const ::OstProto::PortId* request,
|
||||||
|
::OstProto::DeviceNeighborList* response,
|
||||||
|
::google::protobuf::Closure* done);
|
||||||
signals:
|
signals:
|
||||||
void notification(int notifType, SharedProtobufMessage notifData);
|
void notification(int notifType, SharedProtobufMessage notifData);
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ PacketBuffer::~PacketBuffer()
|
|||||||
|
|
||||||
int PacketBuffer::length()
|
int PacketBuffer::length()
|
||||||
{
|
{
|
||||||
return tail_ - head_;
|
return tail_ - data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
uchar* PacketBuffer::head()
|
uchar* PacketBuffer::head()
|
||||||
|
@ -38,6 +38,8 @@ env.host_string = 'localhost:50022'
|
|||||||
dut_rx_port = 'eth1'
|
dut_rx_port = 'eth1'
|
||||||
dut_tx_port = 'eth2'
|
dut_tx_port = 'eth2'
|
||||||
|
|
||||||
|
dut_dst_mac = 0x0800278df2b4 #FIXME: hardcoding
|
||||||
|
|
||||||
# setup logging
|
# setup logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
@ -71,6 +73,8 @@ suite = TestSuite()
|
|||||||
if not use_defaults:
|
if not use_defaults:
|
||||||
s = raw_input('Drone\'s Hostname/IP [%s]: ' % (host_name))
|
s = raw_input('Drone\'s Hostname/IP [%s]: ' % (host_name))
|
||||||
host_name = s or host_name
|
host_name = s or host_name
|
||||||
|
s = raw_input('DUT\'s Hostname/IP [%s]: ' % (env.host_string))
|
||||||
|
env.host_string = s or env.host_string
|
||||||
|
|
||||||
drone = DroneProxy(host_name)
|
drone = DroneProxy(host_name)
|
||||||
|
|
||||||
@ -117,6 +121,10 @@ try:
|
|||||||
tx_port_number = port.port_id.id
|
tx_port_number = port.port_id.id
|
||||||
elif rx_port_number < 0:
|
elif rx_port_number < 0:
|
||||||
rx_port_number = port.port_id.id
|
rx_port_number = port.port_id.id
|
||||||
|
if ('eth1' in port.name):
|
||||||
|
tx_port_number = port.port_id.id
|
||||||
|
if ('eth2' in port.name):
|
||||||
|
rx_port_number = port.port_id.id
|
||||||
|
|
||||||
if not use_defaults:
|
if not use_defaults:
|
||||||
p = raw_input('Tx Port Id [%d]: ' % (tx_port_number))
|
p = raw_input('Tx Port Id [%d]: ' % (tx_port_number))
|
||||||
@ -145,6 +153,10 @@ try:
|
|||||||
# modify and reuse these devices as per its needs
|
# modify and reuse these devices as per its needs
|
||||||
# ----------------------------------------------------------------- #
|
# ----------------------------------------------------------------- #
|
||||||
|
|
||||||
|
emul_ports = ost_pb.PortIdList()
|
||||||
|
emul_ports.port_id.add().id = tx_port_number;
|
||||||
|
emul_ports.port_id.add().id = rx_port_number;
|
||||||
|
|
||||||
# delete existing devices, if any, on tx port
|
# delete existing devices, if any, on tx port
|
||||||
tx_dgid_list = drone.getDeviceGroupIdList(tx_port.port_id[0])
|
tx_dgid_list = drone.getDeviceGroupIdList(tx_port.port_id[0])
|
||||||
drone.deleteDeviceGroup(tx_dgid_list)
|
drone.deleteDeviceGroup(tx_dgid_list)
|
||||||
@ -251,7 +263,7 @@ try:
|
|||||||
# 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()
|
||||||
p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber
|
p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber
|
||||||
p.Extensions[mac].dst_mac = 0x0800278df2b4 #FIXME: hardcoding
|
p.Extensions[mac].dst_mac = dut_dst_mac
|
||||||
p.Extensions[mac].src_mac = 0x00aabbccddee
|
p.Extensions[mac].src_mac = 0x00aabbccddee
|
||||||
|
|
||||||
p = s.protocol.add()
|
p = s.protocol.add()
|
||||||
@ -284,6 +296,74 @@ try:
|
|||||||
if re.search('10.10.2.10[1-5].*lladdr', arp_cache):
|
if re.search('10.10.2.10[1-5].*lladdr', arp_cache):
|
||||||
raise TestPreRequisiteError('ARP cache not cleared')
|
raise TestPreRequisiteError('ARP cache not cleared')
|
||||||
|
|
||||||
|
# resolve ARP on tx/rx ports
|
||||||
|
log.info('resolving Neighbors on tx/rx ports ...')
|
||||||
|
drone.startCapture(emul_ports)
|
||||||
|
drone.clearDeviceNeighbors(emul_ports)
|
||||||
|
drone.resolveDeviceNeighbors(emul_ports)
|
||||||
|
time.sleep(10)
|
||||||
|
drone.stopCapture(emul_ports)
|
||||||
|
|
||||||
|
fail = 0
|
||||||
|
|
||||||
|
# verify ARP Requests sent out from tx port
|
||||||
|
buff = drone.getCaptureBuffer(emul_ports.port_id[0])
|
||||||
|
drone.saveCaptureBuffer(buff, 'capture.pcap')
|
||||||
|
log.info('dumping Tx capture buffer (all)')
|
||||||
|
cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap'])
|
||||||
|
print(cap_pkts)
|
||||||
|
log.info('dumping Tx capture buffer (filtered)')
|
||||||
|
for i in range(num_devs):
|
||||||
|
cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap',
|
||||||
|
'-R', '(arp.opcode == 1)'
|
||||||
|
' && (arp.src.proto_ipv4 == 10.10.1.'+str(101+i)+')'
|
||||||
|
' && (arp.dst.proto_ipv4 == 10.10.1.1)'])
|
||||||
|
print(cap_pkts)
|
||||||
|
if cap_pkts.count('\n') != 1:
|
||||||
|
fail = fail + 1
|
||||||
|
|
||||||
|
# verify *no* ARP Requests sent out from rx port
|
||||||
|
buff = drone.getCaptureBuffer(emul_ports.port_id[1])
|
||||||
|
drone.saveCaptureBuffer(buff, 'capture.pcap')
|
||||||
|
log.info('dumping Rx capture buffer (all)')
|
||||||
|
cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap'])
|
||||||
|
print(cap_pkts)
|
||||||
|
log.info('dumping Rx capture buffer (filtered)')
|
||||||
|
for i in range(num_devs):
|
||||||
|
cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap',
|
||||||
|
'-R', '(arp.opcode == 1)'
|
||||||
|
' && (arp.src.proto_ipv4 == 10.10.2.'+str(101+i)+')'
|
||||||
|
' && (arp.dst.proto_ipv4 == 10.10.2.1)'])
|
||||||
|
print(cap_pkts)
|
||||||
|
if cap_pkts.count('\n') != 0:
|
||||||
|
fail = fail + 1
|
||||||
|
|
||||||
|
# retrieve and verify ARP Table on tx/rx ports
|
||||||
|
log.info('retrieving ARP entries on tx port')
|
||||||
|
neigh_list = drone.getDeviceNeighbors(emul_ports.port_id[0])
|
||||||
|
devices = neigh_list.Extensions[emul.devices]
|
||||||
|
# TODO: verify gateway IP is resolved for each device
|
||||||
|
# FIXME: needs device ip as part of neigh_list
|
||||||
|
log.info('ARP Table on tx port')
|
||||||
|
for device in devices:
|
||||||
|
for arp in device.arp:
|
||||||
|
# TODO: pretty print ip and mac
|
||||||
|
print('%d: %08x %012x' %
|
||||||
|
(device.device_index, arp.ip4, arp.mac))
|
||||||
|
|
||||||
|
log.info('retrieving ARP entries on rx port')
|
||||||
|
neigh_list = drone.getDeviceNeighbors(emul_ports.port_id[1])
|
||||||
|
devices = neigh_list.Extensions[emul.devices]
|
||||||
|
log.info('ARP Table on rx port')
|
||||||
|
for device in devices:
|
||||||
|
# verify *no* ARPs learnt on rx port
|
||||||
|
if len(device.arp):
|
||||||
|
fail = fail + 1
|
||||||
|
for arp in device.arp:
|
||||||
|
# TODO: pretty print ip and mac
|
||||||
|
print('%d: %08x %012x' %
|
||||||
|
(device.device_index, arp.ip4, arp.mac))
|
||||||
|
|
||||||
drone.startCapture(rx_port)
|
drone.startCapture(rx_port)
|
||||||
drone.startTransmit(tx_port)
|
drone.startTransmit(tx_port)
|
||||||
log.info('waiting for transmit to finish ...')
|
log.info('waiting for transmit to finish ...')
|
||||||
@ -297,7 +377,6 @@ try:
|
|||||||
cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap'])
|
cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap'])
|
||||||
print(cap_pkts)
|
print(cap_pkts)
|
||||||
log.info('dumping Rx capture buffer (filtered)')
|
log.info('dumping Rx capture buffer (filtered)')
|
||||||
fail = 0
|
|
||||||
for i in range(num_devs):
|
for i in range(num_devs):
|
||||||
cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap',
|
cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap',
|
||||||
'-R', '(ip.src == 10.10.1.' + str(101+i) + ') '
|
'-R', '(ip.src == 10.10.1.' + str(101+i) + ') '
|
||||||
@ -309,6 +388,8 @@ try:
|
|||||||
fail = fail + 1
|
fail = fail + 1
|
||||||
if fail == 0:
|
if fail == 0:
|
||||||
passed = True
|
passed = True
|
||||||
|
else:
|
||||||
|
log.info('failed checks: %d' % fail)
|
||||||
os.remove('capture.pcap')
|
os.remove('capture.pcap')
|
||||||
except RpcError as e:
|
except RpcError as e:
|
||||||
raise
|
raise
|
||||||
@ -320,6 +401,9 @@ try:
|
|||||||
sudo('ip address delete 10.10.2.1/24 dev ' + dut_tx_port)
|
sudo('ip address delete 10.10.2.1/24 dev ' + dut_tx_port)
|
||||||
suite.test_end(passed)
|
suite.test_end(passed)
|
||||||
|
|
||||||
|
# FIXME: update the below test cases to resolve Neighbors and streams
|
||||||
|
# to derive src/dst mac from device
|
||||||
|
|
||||||
# ----------------------------------------------------------------- #
|
# ----------------------------------------------------------------- #
|
||||||
# TESTCASE: Emulate multiple IPv4 device per multiple single-tag VLANs
|
# TESTCASE: Emulate multiple IPv4 device per multiple single-tag VLANs
|
||||||
#
|
#
|
||||||
@ -438,7 +522,7 @@ try:
|
|||||||
# setup stream protocols as mac:vlan:eth2:ip4:udp:payload
|
# setup stream protocols as mac:vlan:eth2:ip4:udp:payload
|
||||||
p = s.protocol.add()
|
p = s.protocol.add()
|
||||||
p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber
|
p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber
|
||||||
p.Extensions[mac].dst_mac = 0x0800278df2b4 #FIXME: hardcoding
|
p.Extensions[mac].dst_mac = dut_dst_mac
|
||||||
p.Extensions[mac].src_mac = 0x00aabbccddee
|
p.Extensions[mac].src_mac = 0x00aabbccddee
|
||||||
|
|
||||||
p = s.protocol.add()
|
p = s.protocol.add()
|
||||||
|
Loading…
Reference in New Issue
Block a user