Feature (contd.): Device Emulation - Test case and code to support sending ping echo reply in response to received ping echo request
This commit is contained in:
parent
b135cb8df7
commit
ea44e55e78
@ -197,6 +197,9 @@ void Device::receivePacket(PacketBuffer *pktBuf)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0800: // IPv4
|
case 0x0800: // IPv4
|
||||||
|
receiveIp4(pktBuf);
|
||||||
|
break;
|
||||||
|
|
||||||
case 0x86dd: // IPv6
|
case 0x86dd: // IPv6
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -481,6 +484,113 @@ void Device::sendArpRequest(PacketBuffer *pktBuf)
|
|||||||
qPrintable(QHostAddress(tgtIp).toString()));
|
qPrintable(QHostAddress(tgtIp).toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Device::receiveIp4(PacketBuffer *pktBuf)
|
||||||
|
{
|
||||||
|
uchar *pktData = pktBuf->data();
|
||||||
|
uchar ipProto;
|
||||||
|
quint32 dstIp;
|
||||||
|
|
||||||
|
if (pktData[0] != 0x45) {
|
||||||
|
qDebug("%s: Unsupported IP version or options (%02x) ", __FUNCTION__,
|
||||||
|
pktData[0]);
|
||||||
|
goto _invalid_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pktBuf->length() < 20) {
|
||||||
|
qDebug("incomplete IPv4 header: expected 20, actual %d",
|
||||||
|
pktBuf->length());
|
||||||
|
goto _invalid_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: We don't verify IP Header checksum
|
||||||
|
|
||||||
|
dstIp = qFromBigEndian<quint32>(pktData + 16);
|
||||||
|
if (dstIp != ip4_) {
|
||||||
|
qDebug("%s: dstIp %x is not me (%x)", __FUNCTION__, dstIp, ip4_);
|
||||||
|
goto _invalid_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipProto = pktData[9];
|
||||||
|
switch (ipProto) {
|
||||||
|
case 1: // ICMP
|
||||||
|
pktBuf->pull(20);
|
||||||
|
receiveIcmp4(pktBuf);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_invalid_exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function assumes we are replying back to the same IP
|
||||||
|
// that originally sent us the packet and therefore we can reuse the
|
||||||
|
// ingress packet for egress; in other words, it assumes the
|
||||||
|
// original IP header is intact and will just reuse it after
|
||||||
|
// minimal modifications
|
||||||
|
void Device::sendIp4Reply(PacketBuffer *pktBuf)
|
||||||
|
{
|
||||||
|
uchar *pktData = pktBuf->push(20);
|
||||||
|
uchar origTtl = pktData[8];
|
||||||
|
uchar ipProto = pktData[9];
|
||||||
|
quint32 srcIp, dstIp;
|
||||||
|
quint32 sum;
|
||||||
|
|
||||||
|
// Swap src/dst IP addresses
|
||||||
|
dstIp = qFromBigEndian<quint32>(pktData + 12); // srcIp in original pkt
|
||||||
|
srcIp = qFromBigEndian<quint32>(pktData + 16); // dstIp in original pkt
|
||||||
|
|
||||||
|
if (!arpTable.contains(dstIp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
*(quint32*)(pktData + 12) = qToBigEndian(srcIp);
|
||||||
|
*(quint32*)(pktData + 16) = qToBigEndian(dstIp);
|
||||||
|
|
||||||
|
// Reset TTL
|
||||||
|
pktData[8] = 64;
|
||||||
|
|
||||||
|
// Incremental checksum update (RFC 1624 [Eqn.3])
|
||||||
|
// HC' = ~(~HC + ~m + m')
|
||||||
|
sum = quint16(~qFromBigEndian<quint16>(pktData + 10)); // old cksum
|
||||||
|
sum += quint16(~quint16(origTtl << 8 | ipProto)); // old value
|
||||||
|
sum += quint16(pktData[8] << 8 | ipProto); // new value
|
||||||
|
while(sum >> 16)
|
||||||
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
*(quint16*)(pktData + 10) = qToBigEndian(quint16(~sum));
|
||||||
|
|
||||||
|
encap(pktBuf, arpTable.value(dstIp), 0x0800);
|
||||||
|
transmitPacket(pktBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::receiveIcmp4(PacketBuffer *pktBuf)
|
||||||
|
{
|
||||||
|
uchar *pktData = pktBuf->data();
|
||||||
|
quint32 sum;
|
||||||
|
|
||||||
|
// XXX: We don't verify icmp checksum
|
||||||
|
|
||||||
|
// We handle only ping request
|
||||||
|
if (pktData[0] != 8) { // Echo Request
|
||||||
|
qDebug("%s: Ignoring non echo request (%d)", __FUNCTION__, pktData[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pktData[0] = 0; // Echo Reply
|
||||||
|
|
||||||
|
// Incremental checksum update (RFC 1624 [Eqn.3])
|
||||||
|
// HC' = ~(~HC + ~m + m')
|
||||||
|
sum = quint16(~qFromBigEndian<quint16>(pktData + 2)); // old cksum
|
||||||
|
sum += quint16(~quint16(8 << 8 | pktData[1])); // old value
|
||||||
|
sum += quint16(0 << 8 | pktData[1]); // new value
|
||||||
|
while(sum >> 16)
|
||||||
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
*(quint16*)(pktData + 2) = qToBigEndian(quint16(~sum));
|
||||||
|
|
||||||
|
sendIp4Reply(pktBuf);
|
||||||
|
qDebug("Sent ICMP Echo Reply");
|
||||||
|
}
|
||||||
|
|
||||||
bool operator<(const DeviceKey &a1, const DeviceKey &a2)
|
bool operator<(const DeviceKey &a1, const DeviceKey &a2)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -68,6 +68,11 @@ private: // methods
|
|||||||
void receiveArp(PacketBuffer *pktBuf);
|
void receiveArp(PacketBuffer *pktBuf);
|
||||||
void sendArpRequest(PacketBuffer *pktBuf);
|
void sendArpRequest(PacketBuffer *pktBuf);
|
||||||
|
|
||||||
|
void receiveIp4(PacketBuffer *pktBuf);
|
||||||
|
void sendIp4Reply(PacketBuffer *pktBuf);
|
||||||
|
|
||||||
|
void receiveIcmp4(PacketBuffer *pktBuf);
|
||||||
|
|
||||||
private: // data
|
private: // data
|
||||||
static const int kMaxVlan = 4;
|
static const int kMaxVlan = 4;
|
||||||
|
|
||||||
|
@ -966,18 +966,18 @@ void PcapPort::EmulationTransceiver::run()
|
|||||||
libpcap changes their implementation, this will need to change as well.
|
libpcap changes their implementation, this will need to change as well.
|
||||||
*/
|
*/
|
||||||
const char *capture_filter =
|
const char *capture_filter =
|
||||||
"arp or "
|
"arp or icmp or "
|
||||||
"(vlan and arp) or "
|
"(vlan and (arp or icmp)) or "
|
||||||
"(vlan and vlan and arp) or "
|
"(vlan and vlan and (arp or icmp)) or "
|
||||||
"(vlan and vlan and vlan and arp) or "
|
"(vlan and vlan and vlan and (arp or icmp)) or "
|
||||||
"(vlan and vlan and vlan and vlan and arp)";
|
"(vlan and vlan and vlan and vlan and (arp or icmp))";
|
||||||
#else
|
#else
|
||||||
const char *capture_filter =
|
const char *capture_filter =
|
||||||
"arp or "
|
"arp or icmp or "
|
||||||
"(vlan and arp) or "
|
"(vlan and (arp or icmp)) or "
|
||||||
"(vlan and arp) or "
|
"(vlan and (arp or icmp)) or "
|
||||||
"(vlan and arp) or "
|
"(vlan and (arp or icmp)) or "
|
||||||
"(vlan and arp)";
|
"(vlan and (arp or icmp))";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const int optimize = 1;
|
const int optimize = 1;
|
||||||
|
@ -219,6 +219,16 @@ def stream_id(request, drone, ports):
|
|||||||
|
|
||||||
return stream_id
|
return stream_id
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dut_ip(request, dut_ports):
|
||||||
|
sudo('ip address add 10.10.1.1/24 dev ' + dut_ports.rx)
|
||||||
|
sudo('ip address add 10.10.2.1/24 dev ' + dut_ports.tx)
|
||||||
|
|
||||||
|
def fin():
|
||||||
|
sudo('ip address delete 10.10.1.1/24 dev ' + dut_ports.rx)
|
||||||
|
sudo('ip address delete 10.10.2.1/24 dev ' + dut_ports.tx)
|
||||||
|
request.addfinalizer(fin)
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def dut_vlans(request, dut_ports):
|
def dut_vlans(request, dut_ports):
|
||||||
class Devices(object):
|
class Devices(object):
|
||||||
@ -326,8 +336,8 @@ def dut_vlans(request, dut_ports):
|
|||||||
{'mac_step': 1, 'ip_step': 1},
|
{'mac_step': 1, 'ip_step': 1},
|
||||||
{'mac_step': 2, 'ip_step': 5},
|
{'mac_step': 2, 'ip_step': 5},
|
||||||
])
|
])
|
||||||
def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, stream_id,
|
def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
|
||||||
emul_ports, dgid_list, dev_cfg):
|
stream_id, emul_ports, dgid_list, dev_cfg):
|
||||||
# ----------------------------------------------------------------- #
|
# ----------------------------------------------------------------- #
|
||||||
# TESTCASE: Emulate multiple IPv4 devices (no vlans)
|
# TESTCASE: Emulate multiple IPv4 devices (no vlans)
|
||||||
# DUT
|
# DUT
|
||||||
@ -343,10 +353,6 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, stream_id,
|
|||||||
mac_step = dev_cfg['mac_step']
|
mac_step = dev_cfg['mac_step']
|
||||||
ip_step = dev_cfg['ip_step']
|
ip_step = dev_cfg['ip_step']
|
||||||
|
|
||||||
# configure the DUT
|
|
||||||
sudo('ip address add 10.10.1.1/24 dev ' + dut_ports.rx)
|
|
||||||
sudo('ip address add 10.10.2.1/24 dev ' + dut_ports.tx)
|
|
||||||
|
|
||||||
# configure the tx device(s)
|
# configure the tx device(s)
|
||||||
devgrp_cfg = ost_pb.DeviceGroupConfigList()
|
devgrp_cfg = ost_pb.DeviceGroupConfigList()
|
||||||
devgrp_cfg.port_id.CopyFrom(ports.tx.port_id[0])
|
devgrp_cfg.port_id.CopyFrom(ports.tx.port_id[0])
|
||||||
@ -520,6 +526,16 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, stream_id,
|
|||||||
print('%08x: %08x %012x' %
|
print('%08x: %08x %012x' %
|
||||||
(dev_cfg.ip4, arp.ip4, arp.mac))
|
(dev_cfg.ip4, arp.ip4, arp.mac))
|
||||||
|
|
||||||
|
# ping the tx devices from the DUT
|
||||||
|
for i in range(num_devs):
|
||||||
|
out = run('ping -c3 10.10.1.'+str(101+i*ip_step), warn_only=True)
|
||||||
|
assert '100% packet loss' not in out
|
||||||
|
|
||||||
|
# ping the tx devices from the DUT
|
||||||
|
for i in range(num_devs):
|
||||||
|
out = run('ping -c3 10.10.2.'+str(101+i*ip_step), warn_only=True)
|
||||||
|
assert '100% packet loss' not in out
|
||||||
|
|
||||||
# We are all set now - so transmit the stream now
|
# We are all set now - so transmit the stream now
|
||||||
drone.startCapture(ports.rx)
|
drone.startCapture(ports.rx)
|
||||||
drone.startTransmit(ports.tx)
|
drone.startTransmit(ports.tx)
|
||||||
@ -547,10 +563,6 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, stream_id,
|
|||||||
drone.stopTransmit(ports.tx)
|
drone.stopTransmit(ports.tx)
|
||||||
run('ip neigh show')
|
run('ip neigh show')
|
||||||
|
|
||||||
# unconfigure DUT
|
|
||||||
sudo('ip address delete 10.10.1.1/24 dev ' + dut_ports.rx)
|
|
||||||
sudo('ip address delete 10.10.2.1/24 dev ' + dut_ports.tx)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('vlan_cfg', [
|
@pytest.mark.parametrize('vlan_cfg', [
|
||||||
[{'base': 11, 'count': 5}],
|
[{'base': 11, 'count': 5}],
|
||||||
|
|
||||||
@ -814,6 +826,23 @@ def test_multiEmulDevPerVlan(request, drone, ports, dut, dut_ports, stream_id,
|
|||||||
print('v%d|%08x: %08x %012x' %
|
print('v%d|%08x: %08x %012x' %
|
||||||
(dev_cfg.vlan[0] & 0xffff, dev_cfg.ip4, arp.ip4, arp.mac))
|
(dev_cfg.vlan[0] & 0xffff, dev_cfg.ip4, arp.ip4, arp.mac))
|
||||||
|
|
||||||
|
# ping the tx devices from the DUT
|
||||||
|
for i in range(num_vlans):
|
||||||
|
vrf = 'vrf' + str(i+1)
|
||||||
|
for j in range(num_devs_per_vlan):
|
||||||
|
out = sudo('ip netns exec ' + vrf
|
||||||
|
+ ' ping -c3 10.1.1.'+str(101+j), warn_only=True)
|
||||||
|
assert '100% packet loss' not in out
|
||||||
|
|
||||||
|
# ping the tx devices from the DUT
|
||||||
|
for i in range(num_vlans):
|
||||||
|
vrf = 'vrf' + str(i+1)
|
||||||
|
for j in range(num_devs_per_vlan):
|
||||||
|
out = sudo('ip netns exec ' + vrf
|
||||||
|
+ ' ping -c3 10.1.2.'+str(101+j), warn_only=True)
|
||||||
|
assert '100% packet loss' not in out
|
||||||
|
|
||||||
|
# we are all set - send data stream(s)
|
||||||
drone.startCapture(ports.rx)
|
drone.startCapture(ports.rx)
|
||||||
drone.startTransmit(ports.tx)
|
drone.startTransmit(ports.tx)
|
||||||
log.info('waiting for transmit to finish ...')
|
log.info('waiting for transmit to finish ...')
|
||||||
@ -857,4 +886,6 @@ def test_multiEmulDevPerVlan(request, drone, ports, dut, dut_ports, stream_id,
|
|||||||
# vlanMode: no-repeat; ip4Mode: no-repeat
|
# vlanMode: no-repeat; ip4Mode: no-repeat
|
||||||
# ----------------------------------------------------------------- #
|
# ----------------------------------------------------------------- #
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
pytest.main(__file__)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user