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:
Srivats P 2015-12-23 21:18:25 +05:30
parent b135cb8df7
commit ea44e55e78
4 changed files with 166 additions and 20 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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__)