diff --git a/server/abstractport.cpp b/server/abstractport.cpp index f6568d6..42e636e 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -625,7 +625,10 @@ void AbstractPort::clearDeviceNeighbors() void AbstractPort::resolveDeviceNeighbors() { - // Resolve neighbor for each unique frame of each stream + // Resolve gateway for each device first ... + deviceManager_->resolveDeviceGateways(); + + // ... then resolve neighbor for each unique frame of each stream // NOTE: // 1. All the frames may have the same destination ip,but may have // different source ip so may belong to a different emulated device; diff --git a/server/device.cpp b/server/device.cpp index 97501ed..656531c 100644 --- a/server/device.cpp +++ b/server/device.cpp @@ -256,6 +256,15 @@ void Device::transmitPacket(PacketBuffer *pktBuf) deviceManager_->transmitPacket(pktBuf); } +void Device::resolveGateway() +{ + if (hasIp4_) + sendArpRequest(ip4Gateway_); + + if (hasIp6_) + sendNeighborSolicit(ip6Gateway_); +} + void Device::clearNeighbors() { arpTable_.clear(); @@ -524,11 +533,9 @@ _invalid_exit: // pktBuf points to start of IP header void Device::sendArpRequest(PacketBuffer *pktBuf) { - PacketBuffer *reqPkt; uchar *pktData = pktBuf->data(); - int offset = 0; - int ipHdrLen = (pktData[offset] & 0x0F) << 2; - quint32 srcIp, dstIp, mask, tgtIp; + int ipHdrLen = (pktData[0] & 0x0F) << 2; + quint32 srcIp = ip4_, dstIp, mask, tgtIp; if (pktBuf->length() < ipHdrLen) { qDebug("incomplete IPv4 header: expected %d, actual %d", @@ -536,25 +543,28 @@ void Device::sendArpRequest(PacketBuffer *pktBuf) return; } - // FIXME: not required - caller is checking for origin anyway - // Extract srcIp first to check quickly that this packet originates - // from this device - srcIp = qFromBigEndian(pktData + ipHdrLen - 8); - if (srcIp != ip4_) { - qDebug("%s: srcIp %s is not me %s", __FUNCTION__, - qPrintable(QHostAddress(srcIp).toString()), - qPrintable(QHostAddress(ip4_).toString())); - return; - } - dstIp = qFromBigEndian(pktData + ipHdrLen - 4); mask = ~0 << (32 - ip4PrefixLength_); qDebug("dst %x src %x mask %x", dstIp, srcIp, mask); tgtIp = ((dstIp & mask) == (srcIp & mask)) ? dstIp : ip4Gateway_; + sendArpRequest(tgtIp); + +} + +void Device::sendArpRequest(quint32 tgtIp) +{ + quint32 srcIp = ip4_; + PacketBuffer *reqPkt; + uchar *pktData; + + // Validate target IP + if (!tgtIp) + return; + // Do we already have a ARP entry (resolved or unresolved)? - // FIXME: do we need a timer to resend ARP for unresolved entries? + // XXX: No NDP state machine for now if (arpTable_.contains(tgtIp)) return; @@ -902,9 +912,8 @@ _invalid_exit: // pktBuf should point to start of IP header void Device::sendNeighborSolicit(PacketBuffer *pktBuf) { - PacketBuffer *reqPkt; uchar *pktData = pktBuf->data(); - UInt128 srcIp, dstIp, mask, tgtIp; + UInt128 srcIp = ip6_, dstIp, mask, tgtIp; if (pktBuf->length() < kIp6HdrLen) { qDebug("incomplete IPv6 header: expected %d, actual %d", @@ -912,7 +921,6 @@ void Device::sendNeighborSolicit(PacketBuffer *pktBuf) return; } - srcIp = qFromBigEndian(pktData + 8); dstIp = qFromBigEndian(pktData + 24); mask = ~UInt128(0, 0) << (128 - ip6PrefixLength_); @@ -922,8 +930,21 @@ void Device::sendNeighborSolicit(PacketBuffer *pktBuf) qPrintable(QHostAddress(mask.toArray()).toString())); tgtIp = ((dstIp & mask) == (srcIp & mask)) ? dstIp : ip6Gateway_; + sendNeighborSolicit(tgtIp); +} + +void Device::sendNeighborSolicit(UInt128 tgtIp) +{ + UInt128 dstIp, srcIp = ip6_; + PacketBuffer *reqPkt; + uchar *pktData; + + // Validate target IP + if (tgtIp == UInt128(0, 0)) + return; + // Do we already have a NDP entry (resolved or unresolved)? - // FIXME: do we need a timer to resend NS for unresolved entries? + // XXX: No ARP state machine for now if (ndpTable_.contains(tgtIp)) return; diff --git a/server/device.h b/server/device.h index f18a9f8..fda1a2e 100644 --- a/server/device.h +++ b/server/device.h @@ -59,6 +59,8 @@ public: void receivePacket(PacketBuffer *pktBuf); void transmitPacket(PacketBuffer *pktBuf); + void resolveGateway(); + void clearNeighbors(); void resolveNeighbor(PacketBuffer *pktBuf); void getNeighbors(OstEmul::DeviceNeighborList *neighbors); @@ -69,6 +71,7 @@ public: private: // methods void receiveArp(PacketBuffer *pktBuf); void sendArpRequest(PacketBuffer *pktBuf); + void sendArpRequest(quint32 tgtIp); void receiveIp4(PacketBuffer *pktBuf); void sendIp4Reply(PacketBuffer *pktBuf); @@ -83,6 +86,7 @@ private: // methods void receiveNdp(PacketBuffer *pktBuf); void sendNeighborSolicit(PacketBuffer *pktBuf); + void sendNeighborSolicit(UInt128 tgtIp); void sendNeighborAdvertisement(PacketBuffer *pktBuf); private: // data diff --git a/server/devicemanager.cpp b/server/devicemanager.cpp index d82714b..94a38da 100644 --- a/server/devicemanager.cpp +++ b/server/devicemanager.cpp @@ -260,6 +260,13 @@ void DeviceManager::transmitPacket(PacketBuffer *pktBuf) port_->sendEmulationPacket(pktBuf); } +void DeviceManager::resolveDeviceGateways() +{ + foreach(Device *device, deviceList_) { + device->resolveGateway(); + } +} + void DeviceManager::clearDeviceNeighbors() { foreach(Device *device, deviceList_) diff --git a/server/devicemanager.h b/server/devicemanager.h index e2658b9..2a9adf5 100644 --- a/server/devicemanager.h +++ b/server/devicemanager.h @@ -52,6 +52,8 @@ public: void receivePacket(PacketBuffer *pktBuf); void transmitPacket(PacketBuffer *pktBuf); + void resolveDeviceGateways(); + void clearDeviceNeighbors(); void resolveDeviceNeighbor(PacketBuffer *pktBuf); void getDeviceNeighbors(OstProto::PortNeighborList *neighborList); diff --git a/test/emultest.py b/test/emultest.py index ff9d85b..c54730f 100644 --- a/test/emultest.py +++ b/test/emultest.py @@ -594,7 +594,7 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip, print(cap_pkts) assert cap_pkts.count('\n') == 1 - # verify *no* ARP Requests sent out from rx port + # verify ARP Requests sent out from rx port buff = drone.getCaptureBuffer(emul_ports.port_id[1]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer (all)') @@ -621,7 +621,7 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip, cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap', '-Y', filter]) print(cap_pkts) - assert cap_pkts.count('\n') == 0 + assert cap_pkts.count('\n') == 1 # retrieve and verify ARP/NDP Table on tx/rx ports log.info('retrieving ARP/NDP entries on tx port') @@ -636,8 +636,7 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip, for arp in device.arp: print('%s: %s %012x' % (str(ipaddress.ip_address(dev_cfg.ip4)), - str(ipaddress.ip_address(arp.ip4)), - arp.mac)) + str(ipaddress.ip_address(arp.ip4)), arp.mac)) if (arp.ip4 == dev_cfg.ip4_default_gateway) and (arp.mac): resolved = True assert resolved @@ -646,32 +645,36 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip, for ndp in device.ndp: print('%s: %s %012x' % (str(ip6_address(dev_cfg.ip6)), - str(ip6_address(ndp.ip6)), - ndp.mac)) + str(ip6_address(ndp.ip6)), ndp.mac)) if (ndp.ip6 == dev_cfg.ip6_default_gateway) and (ndp.mac): resolved = True assert resolved log.info('retrieving ARP/NDP entries on rx port') - device_list = drone.getDeviceList(emul_ports.port_id[0]) + device_list = drone.getDeviceList(emul_ports.port_id[1]) device_config = device_list.Extensions[emul.port_device] neigh_list = drone.getDeviceNeighbors(emul_ports.port_id[1]) devices = neigh_list.Extensions[emul.devices] log.info('ARP/NDP Table on rx port') for dev_cfg, device in zip(device_config, devices): - # verify *no* ARPs/NDPs learnt on rx port if has_ip4: + resolved = False for arp in device.arp: print('%s: %s %012x' % (str(ipaddress.ip_address(dev_cfg.ip4)), str(ipaddress.ip_address(arp.ip4)), arp.mac)) - assert len(device.arp) == 0 + if (arp.ip4 == dev_cfg.ip4_default_gateway) and (arp.mac): + resolved = True + assert resolved if has_ip6: + resolved = False for ndp in device.ndp: print('%s: %s %012x' % (str(ip6_address(dev_cfg.ip6)), str(ip6_address(ndp.ip6)), ndp.mac)) - assert len(device.ndp) == 0 + if (ndp.ip6 == dev_cfg.ip6_default_gateway) and (ndp.mac): + resolved = True + assert resolved # ping the tx devices from the DUT for i in range(num_devs): @@ -998,8 +1001,8 @@ def test_multiEmulDevPerVlan(request, drone, ports, dut, dut_ports, print(cap_pkts) assert cap_pkts.count('\n') == 1 - # verify *no* ARP/NDP Requests sent out from rx port - buff = drone.getCaptureBuffer(emul_ports.port_id[0]) + # verify ARP/NDP Requests sent out from rx port + buff = drone.getCaptureBuffer(emul_ports.port_id[1]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer (all)') cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap']) @@ -1030,7 +1033,7 @@ def test_multiEmulDevPerVlan(request, drone, ports, dut, dut_ports, cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap', '-Y', filter]) print(cap_pkts) - assert cap_pkts.count('\n') == 0 + assert cap_pkts.count('\n') == 1 # retrieve and verify ARP/NDP Table on tx/rx ports log.info('retrieving ARP/NDP entries on tx port') @@ -1063,7 +1066,7 @@ def test_multiEmulDevPerVlan(request, drone, ports, dut, dut_ports, assert resolved log.info('retrieving ARP entries on rx port') - device_list = drone.getDeviceList(emul_ports.port_id[0]) + device_list = drone.getDeviceList(emul_ports.port_id[1]) device_config = device_list.Extensions[emul.port_device] neigh_list = drone.getDeviceNeighbors(emul_ports.port_id[1]) devices = neigh_list.Extensions[emul.devices] @@ -1072,19 +1075,24 @@ def test_multiEmulDevPerVlan(request, drone, ports, dut, dut_ports, vlans = '' for v in dev_cfg.vlan: vlans += str(v & 0xffff) + ' ' - # verify *no* ARPs/NDPs learnt on rx port if has_ip4: + resolved = False for arp in device.arp: print('%s%s: %s %012x' % (vlans, str(ipaddress.ip_address(dev_cfg.ip4)), - str(ipaddress.ip_address(arp.ip4)), arp.mac)) - assert len(device.arp) == 0 + str(ipaddress.ip_address(arp.ip4)), arp.mac)) + if (arp.ip4 == dev_cfg.ip4_default_gateway) and (arp.mac): + resolved = True + assert resolved if has_ip6: + resolved = False for ndp in device.ndp: print('%s%s: %s %012x' % (vlans, str(ip6_address(dev_cfg.ip6)), str(ip6_address(ndp.ip6)), ndp.mac)) - assert len(device.ndp) == 0 + if (ndp.ip6 == dev_cfg.ip6_default_gateway) and (ndp.mac): + resolved = True + assert resolved # ping the tx devices from the DUT for i in range(num_vlans):