Bugfix: use default gateway for off subnet destinations in all cases - fixes #196
This commit is contained in:
parent
23ab64dc4e
commit
f86ce2603d
@ -684,16 +684,20 @@ void Device::sendIp4Reply(PacketBuffer *pktBuf)
|
||||
uchar *pktData = pktBuf->push(20);
|
||||
uchar origTtl = pktData[8];
|
||||
uchar ipProto = pktData[9];
|
||||
quint32 srcIp, dstIp;
|
||||
quint32 srcIp, dstIp, tgtIp, mask;
|
||||
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)) {
|
||||
mask = ~0 << (32 - ip4PrefixLength_);
|
||||
qDebug("dst %x mask %x self %x", dstIp, mask, ip4_);
|
||||
tgtIp = ((dstIp & mask) == (ip4_ & mask)) ? dstIp : ip4Gateway_;
|
||||
|
||||
if (!arpTable_.contains(tgtIp)) {
|
||||
qWarning("%s: mac not found for %s; unable to send IPv4 packet",
|
||||
__FUNCTION__, qPrintable(QHostAddress(dstIp).toString()));
|
||||
__FUNCTION__, qPrintable(QHostAddress(tgtIp).toString()));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -712,7 +716,7 @@ void Device::sendIp4Reply(PacketBuffer *pktBuf)
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
*(quint16*)(pktData + 10) = qToBigEndian(quint16(~sum));
|
||||
|
||||
encap(pktBuf, arpTable_.value(dstIp), 0x0800);
|
||||
encap(pktBuf, arpTable_.value(tgtIp), 0x0800);
|
||||
transmitPacket(pktBuf);
|
||||
}
|
||||
|
||||
@ -796,7 +800,7 @@ bool Device::sendIp6(PacketBuffer *pktBuf, UInt128 dstIp, quint8 protocol)
|
||||
{
|
||||
int payloadLen = pktBuf->length();
|
||||
uchar *p = pktBuf->push(kIp6HdrLen);
|
||||
quint64 dstMac = ndpTable_.value(dstIp);
|
||||
quint64 dstMac;
|
||||
|
||||
if (!p) {
|
||||
qWarning("%s: failed to push %d bytes [0x%p, 0x%p]", __FUNCTION__,
|
||||
@ -807,6 +811,15 @@ bool Device::sendIp6(PacketBuffer *pktBuf, UInt128 dstIp, quint8 protocol)
|
||||
// In case of mcast, derive dstMac
|
||||
if ((dstIp.hi64() >> 56) == 0xff)
|
||||
dstMac = (quint64(0x3333) << 32) | (dstIp.lo64() & 0xffffffff);
|
||||
else {
|
||||
UInt128 mask = ~UInt128(0, 0) << (128 - ip6PrefixLength_);
|
||||
UInt128 tgtIp = ((dstIp & mask) == (ip6_ & mask)) ? dstIp : ip6Gateway_;
|
||||
qDebug("dst %s mask %s self %s",
|
||||
qPrintable(QHostAddress(dstIp.toArray()).toString()),
|
||||
qPrintable(QHostAddress(mask.toArray()).toString()),
|
||||
qPrintable(QHostAddress(ip6_.toArray()).toString()));
|
||||
dstMac = ndpTable_.value(tgtIp);
|
||||
}
|
||||
|
||||
if (!dstMac) {
|
||||
qWarning("%s: mac not found for %s; unable to send IPv6 packet",
|
||||
@ -841,16 +854,22 @@ _error_exit:
|
||||
void Device::sendIp6Reply(PacketBuffer *pktBuf)
|
||||
{
|
||||
uchar *pktData = pktBuf->push(kIp6HdrLen);
|
||||
UInt128 srcIp, dstIp;
|
||||
UInt128 srcIp, dstIp, tgtIp, mask;
|
||||
|
||||
// Swap src/dst IP addresses
|
||||
dstIp = qFromBigEndian<UInt128>(pktData + 8); // srcIp in original pkt
|
||||
srcIp = qFromBigEndian<UInt128>(pktData + 24); // dstIp in original pkt
|
||||
|
||||
if (!ndpTable_.contains(dstIp)) {
|
||||
mask = ~UInt128(0, 0) << (128 - ip6PrefixLength_);
|
||||
qDebug("dst %s mask %s self %s",
|
||||
qPrintable(QHostAddress(dstIp.toArray()).toString()),
|
||||
qPrintable(QHostAddress(mask.toArray()).toString()),
|
||||
qPrintable(QHostAddress(ip6_.toArray()).toString()));
|
||||
tgtIp = ((dstIp & mask) == (ip6_ & mask)) ? dstIp : ip6Gateway_;
|
||||
if (!ndpTable_.contains(tgtIp)) {
|
||||
qWarning("%s: mac not found for %s; unable to send IPv6 packet",
|
||||
__FUNCTION__,
|
||||
qPrintable(QHostAddress(dstIp.toArray()).toString()));
|
||||
qPrintable(QHostAddress(tgtIp.toArray()).toString()));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -860,7 +879,7 @@ void Device::sendIp6Reply(PacketBuffer *pktBuf)
|
||||
// Reset TTL
|
||||
pktData[7] = 64;
|
||||
|
||||
encap(pktBuf, ndpTable_.value(dstIp), 0x86dd);
|
||||
encap(pktBuf, ndpTable_.value(tgtIp), 0x86dd);
|
||||
transmitPacket(pktBuf);
|
||||
}
|
||||
|
||||
|
137
test/emultest.py
137
test/emultest.py
@ -21,6 +21,7 @@ from rpc import RpcError
|
||||
from protocols.mac_pb2 import mac, Mac
|
||||
from protocols.ip4_pb2 import ip4, Ip4
|
||||
from protocols.ip6_pb2 import ip6, Ip6
|
||||
from protocols.icmp_pb2 import icmp, Icmp
|
||||
from protocols.vlan_pb2 import vlan
|
||||
|
||||
use_defaults = True
|
||||
@ -348,6 +349,106 @@ def dut_vlans(request, dut_ports):
|
||||
|
||||
request.addfinalizer(delete_vdev)
|
||||
|
||||
@pytest.fixture
|
||||
def ping(request, drone, ip_ver, port_id, src_ip, dst_ip):
|
||||
# create ICMP stream
|
||||
stream_id = ost_pb.StreamIdList()
|
||||
stream_id.port_id.CopyFrom(port_id)
|
||||
stream_id.stream_id.add().id = 0
|
||||
log.info('adding ping tx_stream %d' % stream_id.stream_id[0].id)
|
||||
|
||||
drone.addStream(stream_id)
|
||||
|
||||
# configure the ICMP echo tx stream(s)
|
||||
stream_cfg = ost_pb.StreamConfigList()
|
||||
stream_cfg.port_id.CopyFrom(port_id)
|
||||
s = stream_cfg.stream.add()
|
||||
s.stream_id.id = stream_id.stream_id[0].id
|
||||
s.core.is_enabled = True
|
||||
s.core.frame_len = 128
|
||||
s.control.packets_per_sec = 1
|
||||
s.control.num_packets = 3
|
||||
|
||||
# setup stream protocols as mac:eth2:ip:icmp:payload
|
||||
p = s.protocol.add()
|
||||
p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber
|
||||
p.Extensions[mac].dst_mac_mode = Mac.e_mm_resolve
|
||||
p.Extensions[mac].src_mac_mode = Mac.e_mm_resolve
|
||||
|
||||
p = s.protocol.add()
|
||||
p.protocol_id.id = ost_pb.Protocol.kEth2FieldNumber
|
||||
|
||||
if ip_ver == 4:
|
||||
p = s.protocol.add()
|
||||
p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber
|
||||
ip = None
|
||||
ip = p.Extensions[ip4]
|
||||
ip.src_ip = src_ip
|
||||
ip.dst_ip = dst_ip
|
||||
elif ip_ver == 6:
|
||||
p = s.protocol.add()
|
||||
p.protocol_id.id = ost_pb.Protocol.kIp6FieldNumber
|
||||
ip = p.Extensions[ip6]
|
||||
ip.src_addr_hi = src_ip.hi
|
||||
ip.src_addr_lo = src_ip.lo
|
||||
ip.dst_addr_hi = dst_ip.hi
|
||||
ip.dst_addr_lo = dst_ip.lo
|
||||
else:
|
||||
assert False # unreachable
|
||||
|
||||
p = s.protocol.add()
|
||||
p.protocol_id.id = ost_pb.Protocol.kIcmpFieldNumber
|
||||
if ip_ver == 6:
|
||||
p.Extensions[icmp].icmp_version = Icmp.kIcmp6
|
||||
p.Extensions[icmp].type = 128 # icmpv6 echo request
|
||||
|
||||
s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber
|
||||
|
||||
log.info('configuring ping tx_stream %d' % stream_id.stream_id[0].id)
|
||||
|
||||
drone.modifyStream(stream_cfg)
|
||||
|
||||
# send ping packets
|
||||
ports = ost_pb.PortIdList()
|
||||
ports.port_id.add().id = port_id.id
|
||||
drone.startCapture(ports)
|
||||
drone.startTransmit(ports)
|
||||
time.sleep(5)
|
||||
drone.stopCapture(ports)
|
||||
|
||||
# delete ping stream
|
||||
drone.deleteStream(stream_id)
|
||||
|
||||
# FIXME: workaround for bug#179
|
||||
stream_cfg.ClearField("stream")
|
||||
drone.modifyStream(stream_cfg)
|
||||
|
||||
# verify ICMP Replies are received
|
||||
buff = drone.getCaptureBuffer(port_id)
|
||||
drone.saveCaptureBuffer(buff, 'capture.pcap')
|
||||
log.info('dumping Rx capture buffer (all)')
|
||||
cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap'])
|
||||
print(cap_pkts)
|
||||
if ip_ver == 4:
|
||||
filter = '(icmp.type == 0)' \
|
||||
' && (icmp.code == 0)' \
|
||||
' && (ip.src == ' + str(ipaddress.ip_address(dst_ip)) + ')' \
|
||||
' && (ip.dst == ' + str(ipaddress.ip_address(src_ip)) + ')' \
|
||||
' && !expert.severity'
|
||||
elif ip_ver == 6:
|
||||
filter = '(icmpv6.type == 129)' \
|
||||
' && (icmpv6.code == 0)' \
|
||||
' && (ipv6.src == ' \
|
||||
+ str(ip6_address(dst_ip)) + ')' \
|
||||
' && (ipv6.dst == ' \
|
||||
+ str(ip6_address(src_ip)) + ')' \
|
||||
' && !expert.severity'
|
||||
log.info('dumping Rx capture buffer (filtered)')
|
||||
print filter
|
||||
cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap',
|
||||
'-Y', filter])
|
||||
print(cap_pkts)
|
||||
return cap_pkts.count('\n') > 1
|
||||
|
||||
# ================================================================= #
|
||||
# ----------------------------------------------------------------- #
|
||||
@ -360,7 +461,7 @@ def dut_vlans(request, dut_ports):
|
||||
{'ip_ver': [6], 'mac_step': 1, 'ip_step': 1},
|
||||
{'ip_ver': [4, 6], 'mac_step': 2, 'ip_step': 5},
|
||||
])
|
||||
def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
|
||||
def test_multiEmulDevNoVlan(request, drone, ports, dut, dut_ports, dut_ip,
|
||||
stream_clear, emul_ports, dgid_list, dev_cfg):
|
||||
# ----------------------------------------------------------------- #
|
||||
# TESTCASE: Emulate multiple IPv4 devices (no vlans)
|
||||
@ -381,9 +482,9 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
|
||||
ip_step = dev_cfg['ip_step']
|
||||
|
||||
# configure the tx device(s)
|
||||
devgrp_cfg = ost_pb.DeviceGroupConfigList()
|
||||
devgrp_cfg.port_id.CopyFrom(ports.tx.port_id[0])
|
||||
dg = devgrp_cfg.device_group.add()
|
||||
tx_devgrp_cfg = ost_pb.DeviceGroupConfigList()
|
||||
tx_devgrp_cfg.port_id.CopyFrom(ports.tx.port_id[0])
|
||||
dg = tx_devgrp_cfg.device_group.add()
|
||||
dg.device_group_id.id = dgid_list.tx.device_group_id[0].id
|
||||
dg.core.name = "Host1"
|
||||
dg.device_count = num_devs
|
||||
@ -406,12 +507,12 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
|
||||
ip.step.CopyFrom(ip6_address(ip_step).ip6)
|
||||
ip.default_gateway.CopyFrom(ip6addr.gateway)
|
||||
|
||||
drone.modifyDeviceGroup(devgrp_cfg)
|
||||
drone.modifyDeviceGroup(tx_devgrp_cfg)
|
||||
|
||||
# configure the rx device(s)
|
||||
devgrp_cfg = ost_pb.DeviceGroupConfigList()
|
||||
devgrp_cfg.port_id.CopyFrom(ports.rx.port_id[0])
|
||||
dg = devgrp_cfg.device_group.add()
|
||||
rx_devgrp_cfg = ost_pb.DeviceGroupConfigList()
|
||||
rx_devgrp_cfg.port_id.CopyFrom(ports.rx.port_id[0])
|
||||
dg = rx_devgrp_cfg.device_group.add()
|
||||
dg.device_group_id.id = dgid_list.rx.device_group_id[0].id
|
||||
dg.core.name = "Host1"
|
||||
dg.device_count = num_devs
|
||||
@ -434,7 +535,25 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
|
||||
ip.step.CopyFrom(ip6_address(ip_step).ip6)
|
||||
ip.default_gateway.CopyFrom(ip6addr.gateway)
|
||||
|
||||
drone.modifyDeviceGroup(devgrp_cfg)
|
||||
drone.modifyDeviceGroup(rx_devgrp_cfg)
|
||||
|
||||
# test end-to-end reachability - after resolving ARP/NDP
|
||||
# FIXME: if and when ping RPC is added, move to where we ping DUT below
|
||||
# FIXME: also add ping test to vlan case
|
||||
time.sleep(10) # wait for DAD? otherwise we don't get replies for NS
|
||||
drone.clearDeviceNeighbors(emul_ports)
|
||||
drone.resolveDeviceNeighbors(emul_ports)
|
||||
time.sleep(3) # wait for ARP resolution
|
||||
# FIXME: if ping6/ping4 order is swapped, DUT does not send NS - have
|
||||
# spent several hours trying to figure out why - to no avail :(
|
||||
if has_ip6:
|
||||
assert ping(request, drone, 6, emul_ports.port_id[0],
|
||||
tx_devgrp_cfg.device_group[0].Extensions[emul.ip6].address,
|
||||
rx_devgrp_cfg.device_group[0].Extensions[emul.ip6].address)
|
||||
if has_ip4:
|
||||
assert ping(request, drone, 4, emul_ports.port_id[0],
|
||||
tx_devgrp_cfg.device_group[0].Extensions[emul.ip4].address,
|
||||
rx_devgrp_cfg.device_group[0].Extensions[emul.ip4].address)
|
||||
|
||||
# add the tx stream(s) - we may need more than one
|
||||
stream_id = ost_pb.StreamIdList()
|
||||
|
Loading…
Reference in New Issue
Block a user