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;
|
||||
|
||||
case 0x0800: // IPv4
|
||||
receiveIp4(pktBuf);
|
||||
break;
|
||||
|
||||
case 0x86dd: // IPv6
|
||||
default:
|
||||
break;
|
||||
@ -481,6 +484,113 @@ void Device::sendArpRequest(PacketBuffer *pktBuf)
|
||||
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)
|
||||
{
|
||||
int i = 0;
|
||||
|
@ -68,6 +68,11 @@ private: // methods
|
||||
void receiveArp(PacketBuffer *pktBuf);
|
||||
void sendArpRequest(PacketBuffer *pktBuf);
|
||||
|
||||
void receiveIp4(PacketBuffer *pktBuf);
|
||||
void sendIp4Reply(PacketBuffer *pktBuf);
|
||||
|
||||
void receiveIcmp4(PacketBuffer *pktBuf);
|
||||
|
||||
private: // data
|
||||
static const int kMaxVlan = 4;
|
||||
|
||||
|
@ -966,18 +966,18 @@ void PcapPort::EmulationTransceiver::run()
|
||||
libpcap changes their implementation, this will need to change as well.
|
||||
*/
|
||||
const char *capture_filter =
|
||||
"arp or "
|
||||
"(vlan and arp) or "
|
||||
"(vlan and vlan and arp) or "
|
||||
"(vlan and vlan and vlan and arp) or "
|
||||
"(vlan and vlan and vlan and vlan and arp)";
|
||||
"arp or icmp or "
|
||||
"(vlan and (arp or icmp)) or "
|
||||
"(vlan and vlan and (arp or icmp)) or "
|
||||
"(vlan and vlan and vlan and (arp or icmp)) or "
|
||||
"(vlan and vlan and vlan and vlan and (arp or icmp))";
|
||||
#else
|
||||
const char *capture_filter =
|
||||
"arp or "
|
||||
"(vlan and arp) or "
|
||||
"(vlan and arp) or "
|
||||
"(vlan and arp) or "
|
||||
"(vlan and arp)";
|
||||
"arp or icmp or "
|
||||
"(vlan and (arp or icmp)) or "
|
||||
"(vlan and (arp or icmp)) or "
|
||||
"(vlan and (arp or icmp)) or "
|
||||
"(vlan and (arp or icmp))";
|
||||
#endif
|
||||
|
||||
const int optimize = 1;
|
||||
|
@ -219,6 +219,16 @@ def stream_id(request, drone, ports):
|
||||
|
||||
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
|
||||
def dut_vlans(request, dut_ports):
|
||||
class Devices(object):
|
||||
@ -326,8 +336,8 @@ def dut_vlans(request, dut_ports):
|
||||
{'mac_step': 1, 'ip_step': 1},
|
||||
{'mac_step': 2, 'ip_step': 5},
|
||||
])
|
||||
def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, stream_id,
|
||||
emul_ports, dgid_list, dev_cfg):
|
||||
def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
|
||||
stream_id, emul_ports, dgid_list, dev_cfg):
|
||||
# ----------------------------------------------------------------- #
|
||||
# TESTCASE: Emulate multiple IPv4 devices (no vlans)
|
||||
# DUT
|
||||
@ -343,10 +353,6 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, stream_id,
|
||||
mac_step = dev_cfg['mac_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)
|
||||
devgrp_cfg = ost_pb.DeviceGroupConfigList()
|
||||
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' %
|
||||
(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
|
||||
drone.startCapture(ports.rx)
|
||||
drone.startTransmit(ports.tx)
|
||||
@ -547,10 +563,6 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, stream_id,
|
||||
drone.stopTransmit(ports.tx)
|
||||
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', [
|
||||
[{'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' %
|
||||
(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.startTransmit(ports.tx)
|
||||
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
|
||||
# ----------------------------------------------------------------- #
|
||||
|
||||
import pytest
|
||||
pytest.main(__file__)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user