Device Emulation (contd.): Receive, parse IPv6 Neigh Advt and update NDP Table
This commit is contained in:
parent
d9be523827
commit
eff603304e
@ -104,7 +104,7 @@ message ArpEntry {
|
||||
optional uint64 mac = 2;
|
||||
}
|
||||
|
||||
message NdEntry {
|
||||
message NdpEntry {
|
||||
optional Ip6Address ip6 = 1;
|
||||
optional uint64 mac = 2;
|
||||
}
|
||||
@ -112,7 +112,7 @@ message NdEntry {
|
||||
message DeviceNeighborList {
|
||||
optional uint32 device_index = 1;
|
||||
repeated ArpEntry arp = 2;
|
||||
repeated NdEntry nd = 3;
|
||||
repeated NdpEntry ndp = 3;
|
||||
}
|
||||
|
||||
extend OstProto.PortNeighborList {
|
||||
|
@ -36,6 +36,7 @@ public:
|
||||
quint8* toArray() const;
|
||||
|
||||
bool operator==(const UInt128 &other) const;
|
||||
bool operator!=(const UInt128 &other) const;
|
||||
UInt128 operator+(const UInt128 &other) const;
|
||||
UInt128 operator*(const uint &other) const;
|
||||
UInt128 operator<<(const int &shift) const;
|
||||
@ -82,6 +83,11 @@ inline bool UInt128::operator==(const UInt128 &other) const
|
||||
return ((hi_ == other.hi_) && (lo_ == other.lo_));
|
||||
}
|
||||
|
||||
inline bool UInt128::operator!=(const UInt128 &other) const
|
||||
{
|
||||
return ((hi_ != other.hi_) || (lo_ != other.lo_));
|
||||
}
|
||||
|
||||
inline UInt128 UInt128::operator+(const UInt128 &other) const
|
||||
{
|
||||
UInt128 sum;
|
||||
|
@ -235,6 +235,10 @@ void Device::receivePacket(PacketBuffer *pktBuf)
|
||||
break;
|
||||
|
||||
case 0x86dd: // IPv6
|
||||
if (hasIp6_)
|
||||
receiveIp6(pktBuf);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -283,16 +287,28 @@ void Device::resolveNeighbor(PacketBuffer *pktBuf)
|
||||
// Append this device's neighbors to the list
|
||||
void Device::getNeighbors(OstEmul::DeviceNeighborList *neighbors)
|
||||
{
|
||||
QList<quint32> ipList = arpTable_.keys();
|
||||
QList<quint64> macList = arpTable_.values();
|
||||
QList<quint32> ip4List = arpTable_.keys();
|
||||
QList<UInt128> ip6List = ndpTable_.keys();
|
||||
QList<quint64> macList;
|
||||
|
||||
Q_ASSERT(ipList.size() == macList.size());
|
||||
macList = arpTable_.values();
|
||||
Q_ASSERT(ip4List.size() == macList.size());
|
||||
|
||||
for (int i = 0; i < ipList.size(); i++) {
|
||||
for (int i = 0; i < ip4List.size(); i++) {
|
||||
OstEmul::ArpEntry *arp = neighbors->add_arp();
|
||||
arp->set_ip4(ipList.at(i));
|
||||
arp->set_ip4(ip4List.at(i));
|
||||
arp->set_mac(macList.at(i));
|
||||
}
|
||||
|
||||
macList = ndpTable_.values();
|
||||
Q_ASSERT(ip6List.size() == macList.size());
|
||||
|
||||
for (int i = 0; i < ip6List.size(); i++) {
|
||||
OstEmul::NdpEntry *ndp = neighbors->add_ndp();
|
||||
ndp->mutable_ip6()->set_hi(ip6List.at(i).hi64());
|
||||
ndp->mutable_ip6()->set_lo(ip6List.at(i).lo64());
|
||||
ndp->set_mac(macList.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Are we the source of the given packet?
|
||||
@ -658,6 +674,46 @@ void Device::receiveIcmp4(PacketBuffer *pktBuf)
|
||||
* ---------------------------------------------------------
|
||||
*/
|
||||
|
||||
void Device::receiveIp6(PacketBuffer *pktBuf)
|
||||
{
|
||||
uchar *pktData = pktBuf->data();
|
||||
uchar ipProto;
|
||||
UInt128 dstIp;
|
||||
|
||||
if ((pktData[0] & 0xF0) != 0x60) {
|
||||
qDebug("%s: Unsupported IP version (%02x) ", __FUNCTION__,
|
||||
pktData[0]);
|
||||
goto _invalid_exit;
|
||||
}
|
||||
|
||||
if (pktBuf->length() < kIp6HdrLen) {
|
||||
qDebug("incomplete IPv6 header: expected %d, actual %d",
|
||||
kIp6HdrLen, pktBuf->length());
|
||||
goto _invalid_exit;
|
||||
}
|
||||
|
||||
dstIp = qFromBigEndian<UInt128>(pktData + 24);
|
||||
if (dstIp != ip6_) {
|
||||
qDebug("%s: dstIp %s is not me (%s)", __FUNCTION__,
|
||||
qPrintable(QHostAddress(dstIp.toArray()).toString()),
|
||||
qPrintable(QHostAddress(ip6_.toArray()).toString()));
|
||||
goto _invalid_exit;
|
||||
}
|
||||
|
||||
ipProto = pktData[6];
|
||||
switch (ipProto) {
|
||||
case kIpProtoIcmp6:
|
||||
pktBuf->pull(kIp6HdrLen);
|
||||
receiveIcmp6(pktBuf);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_invalid_exit:
|
||||
return;
|
||||
}
|
||||
|
||||
// pktBuf should point to start of IP payload
|
||||
bool Device::sendIp6(PacketBuffer *pktBuf, UInt128 dstIp, quint8 protocol)
|
||||
{
|
||||
@ -693,6 +749,81 @@ _error_exit:
|
||||
return false;
|
||||
}
|
||||
|
||||
void Device::receiveIcmp6(PacketBuffer *pktBuf)
|
||||
{
|
||||
uchar *pktData = pktBuf->data();
|
||||
quint8 type = pktData[0];
|
||||
|
||||
// XXX: We don't verify icmp checksum
|
||||
|
||||
switch (type) {
|
||||
case 135: // Neigh Solicit
|
||||
case 136: // Neigh Advt
|
||||
receiveNdp(pktBuf);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Device::receiveNdp(PacketBuffer *pktBuf)
|
||||
{
|
||||
uchar *pktData = pktBuf->data();
|
||||
quint8 type = pktData[0];
|
||||
|
||||
if (pktBuf->length() < 32) {
|
||||
qDebug("%s: incomplete NA header: expected 32, actual %d",
|
||||
__FUNCTION__, pktBuf->length());
|
||||
goto _invalid_exit;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 135: { // Neigh Solicit
|
||||
#if 0
|
||||
quint32 sum;
|
||||
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");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 136: { // Neigh Advt
|
||||
quint8 flags = pktData[4];
|
||||
const quint8 kSFlag = 0x40;
|
||||
const quint8 kOFlag = 0x20;
|
||||
UInt128 tgtIp = qFromBigEndian<UInt128>(pktData + 8);
|
||||
quint64 mac = ndpTable_.value(tgtIp);
|
||||
|
||||
// Update NDP table only for solicited responses
|
||||
if (!(flags & kSFlag))
|
||||
break;
|
||||
|
||||
if ((flags & kOFlag) || (mac == 0)) {
|
||||
// Check if we have a Target Link-Layer TLV
|
||||
if ((pktData[24] != 2) || (pktData[25] != 1))
|
||||
goto _invalid_exit;
|
||||
mac = qFromBigEndian<quint32>(pktData + 26);
|
||||
mac = (mac << 16) | qFromBigEndian<quint16>(pktData + 30);
|
||||
ndpTable_.insert(tgtIp, mac);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
_invalid_exit:
|
||||
return;
|
||||
}
|
||||
|
||||
// Send NS for the IPv6 packet in pktBuf
|
||||
// caller is responsible to check that pktBuf originates from this device
|
||||
// pktBuf should point to start of IP header
|
||||
|
@ -75,8 +75,12 @@ private: // methods
|
||||
|
||||
void receiveIcmp4(PacketBuffer *pktBuf);
|
||||
|
||||
void receiveIp6(PacketBuffer *pktBuf);
|
||||
bool sendIp6(PacketBuffer *pktBuf, UInt128 dstIp, quint8 protocol);
|
||||
|
||||
void receiveIcmp6(PacketBuffer *pktBuf);
|
||||
|
||||
void receiveNdp(PacketBuffer *pktBuf);
|
||||
void sendNeighborSolicit(PacketBuffer *pktBuf);
|
||||
|
||||
private: // data
|
||||
|
@ -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 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))";
|
||||
"arp or icmp or icmp6"
|
||||
"(vlan and (arp or icmp or icmp6)) or "
|
||||
"(vlan and vlan and (arp or icmp or icmp6)) or "
|
||||
"(vlan and vlan and vlan and (arp or icmp or icmp6)) or "
|
||||
"(vlan and vlan and vlan and vlan and (arp or icmp or icmp6))";
|
||||
#else
|
||||
const char *capture_filter =
|
||||
"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))";
|
||||
"arp or icmp or icmp6 "
|
||||
"(vlan and (arp or icmp or icmp6)) or "
|
||||
"(vlan and (arp or icmp or icmp6)) or "
|
||||
"(vlan and (arp or icmp or icmp6)) or "
|
||||
"(vlan and (arp or icmp or icmp6))";
|
||||
#endif
|
||||
|
||||
const int optimize = 1;
|
||||
|
@ -78,7 +78,10 @@ if not use_defaults:
|
||||
# the python ipaddress module
|
||||
class ip6_address(ipaddress.IPv6Interface):
|
||||
def __init__(self, addr):
|
||||
super(ip6_address, self).__init__(unicode(addr))
|
||||
if type(addr) is str:
|
||||
super(ip6_address, self).__init__(unicode(addr))
|
||||
else:
|
||||
super(ip6_address, self).__init__(addr.hi << 64 | addr.lo)
|
||||
self.ip6 = emul.Ip6Address()
|
||||
self.ip6.hi = int(self) >> 64
|
||||
self.ip6.lo = int(self) & 0xffffffffffffffff
|
||||
@ -544,6 +547,10 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
|
||||
assert re.search('10.10.[1-2].1\d\d.*lladdr', arp_cache) == None
|
||||
assert re.search('1234:[1-2]::\[\da-f]+.*lladdr', arp_cache) == None
|
||||
|
||||
# wait for interface to do DAD? Otherwise we don't get replies for NS
|
||||
# FIXME: find alternative to sleep
|
||||
time.sleep(5)
|
||||
|
||||
# resolve ARP on tx/rx ports
|
||||
log.info('resolving Neighbors on tx/rx ports ...')
|
||||
drone.startCapture(emul_ports)
|
||||
@ -610,39 +617,56 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
|
||||
print(cap_pkts)
|
||||
assert cap_pkts.count('\n') == 0
|
||||
|
||||
# retrieve and verify ARP Table on tx/rx ports
|
||||
log.info('retrieving ARP entries on tx port')
|
||||
# retrieve and verify ARP/NDP Table on tx/rx ports
|
||||
log.info('retrieving ARP/NDP entries on tx port')
|
||||
device_list = drone.getDeviceList(emul_ports.port_id[0])
|
||||
device_config = device_list.Extensions[emul.port_device]
|
||||
neigh_list = drone.getDeviceNeighbors(emul_ports.port_id[0])
|
||||
devices = neigh_list.Extensions[emul.devices]
|
||||
log.info('ARP Table on tx port')
|
||||
log.info('ARP/NDP Table on tx port')
|
||||
for dev_cfg, device in zip(device_config, devices):
|
||||
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))
|
||||
if (arp.ip4 == dev_cfg.ip4_default_gateway) and (arp.mac):
|
||||
resolved = True
|
||||
# TODO: ip6/ndp
|
||||
assert resolved
|
||||
if has_ip4:
|
||||
for arp in device.arp:
|
||||
print('%s: %s %012x' %
|
||||
(str(ipaddress.ip_address(dev_cfg.ip4)),
|
||||
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:
|
||||
for ndp in device.ndp:
|
||||
print('%s: %s %012x' %
|
||||
(str(ip6_address(dev_cfg.ip6)),
|
||||
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 entries on rx port')
|
||||
log.info('retrieving ARP/NDP entries on rx port')
|
||||
device_list = drone.getDeviceList(emul_ports.port_id[0])
|
||||
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 Table on rx port')
|
||||
log.info('ARP/NDP Table on rx port')
|
||||
for dev_cfg, device in zip(device_config, devices):
|
||||
# verify *no* ARPs learnt on rx port
|
||||
assert len(device.arp) == 0
|
||||
for arp in device.arp:
|
||||
# TODO: pretty print ip and mac
|
||||
print('%08x: %08x %012x' %
|
||||
(dev_cfg.ip4, arp.ip4, arp.mac))
|
||||
# TODO: ip6/ndp
|
||||
# verify *no* ARPs/NDPs learnt on rx port
|
||||
if has_ip4:
|
||||
assert len(device.arp) == 0
|
||||
for arp in device.arp:
|
||||
print('%s: %s %012x' %
|
||||
(str(ipaddress.ip_address(dev_cfg.ip4)),
|
||||
str(ipaddress.ip_address(arp.ip4)),
|
||||
arp.mac))
|
||||
if has_ip6:
|
||||
assert len(device.ndp) == 0
|
||||
for ndp in device.ndp:
|
||||
print('%s: %s %012x' %
|
||||
(str(ip6_address(dev_cfg.ip6)),
|
||||
str(ip6_address(ndp.ip6)),
|
||||
ndp.mac))
|
||||
|
||||
# ping the tx devices from the DUT
|
||||
for i in range(num_devs):
|
||||
|
Loading…
Reference in New Issue
Block a user