Bugfix: use default gateway for off subnet destinations in all cases - fixes #196

This commit is contained in:
Srivats P 2016-09-17 12:16:53 +05:30
parent 23ab64dc4e
commit f86ce2603d
2 changed files with 156 additions and 18 deletions

View File

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

View File

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