Device Emulation (contd.): Implemented responding IPv6 NS with NA
This commit is contained in:
parent
eff603304e
commit
04147076c4
@ -52,6 +52,11 @@ inline quint32 sumUInt128(UInt128 value)
|
|||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isIp6Mcast(UInt128 ip)
|
||||||
|
{
|
||||||
|
return (ip.hi64() >> 56) == 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
Device::Device(DeviceManager *deviceManager)
|
Device::Device(DeviceManager *deviceManager)
|
||||||
{
|
{
|
||||||
deviceManager_ = deviceManager;
|
deviceManager_ = deviceManager;
|
||||||
@ -588,12 +593,14 @@ void Device::receiveIp4(PacketBuffer *pktBuf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ipProto = pktData[9];
|
ipProto = pktData[9];
|
||||||
|
qDebug("%s: ipProto = %d", __FUNCTION__, ipProto);
|
||||||
switch (ipProto) {
|
switch (ipProto) {
|
||||||
case 1: // ICMP
|
case 1: // ICMP
|
||||||
pktBuf->pull(20);
|
pktBuf->pull(20);
|
||||||
receiveIcmp4(pktBuf);
|
receiveIcmp4(pktBuf);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
qWarning("%s: Unsupported ipProto %d", __FUNCTION__, ipProto);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -692,8 +699,9 @@ void Device::receiveIp6(PacketBuffer *pktBuf)
|
|||||||
goto _invalid_exit;
|
goto _invalid_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: check for specific mcast address(es) instead of any mcast?
|
||||||
dstIp = qFromBigEndian<UInt128>(pktData + 24);
|
dstIp = qFromBigEndian<UInt128>(pktData + 24);
|
||||||
if (dstIp != ip6_) {
|
if (!isIp6Mcast(dstIp) && (dstIp != ip6_)) {
|
||||||
qDebug("%s: dstIp %s is not me (%s)", __FUNCTION__,
|
qDebug("%s: dstIp %s is not me (%s)", __FUNCTION__,
|
||||||
qPrintable(QHostAddress(dstIp.toArray()).toString()),
|
qPrintable(QHostAddress(dstIp.toArray()).toString()),
|
||||||
qPrintable(QHostAddress(ip6_.toArray()).toString()));
|
qPrintable(QHostAddress(ip6_.toArray()).toString()));
|
||||||
@ -770,32 +778,20 @@ void Device::receiveNdp(PacketBuffer *pktBuf)
|
|||||||
{
|
{
|
||||||
uchar *pktData = pktBuf->data();
|
uchar *pktData = pktBuf->data();
|
||||||
quint8 type = pktData[0];
|
quint8 type = pktData[0];
|
||||||
|
int len = pktBuf->length();
|
||||||
|
int minLen = 24 + (type == 136 ? 8 : 0); // NA should have the Target TLV
|
||||||
|
|
||||||
if (pktBuf->length() < 32) {
|
if (len < minLen) {
|
||||||
qDebug("%s: incomplete NA header: expected 32, actual %d",
|
qDebug("%s: incomplete NS/NA header: expected %d, actual %d",
|
||||||
__FUNCTION__, pktBuf->length());
|
__FUNCTION__, minLen, pktBuf->length());
|
||||||
goto _invalid_exit;
|
goto _invalid_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case 135: { // Neigh Solicit
|
case 135: { // Neigh Solicit
|
||||||
#if 0
|
// TODO: Validation as per RFC 4861
|
||||||
quint32 sum;
|
sendNeighborAdvertisement(pktBuf);
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
case 136: { // Neigh Advt
|
case 136: { // Neigh Advt
|
||||||
@ -902,6 +898,76 @@ void Device::sendNeighborSolicit(PacketBuffer *pktBuf)
|
|||||||
qPrintable(QHostAddress(tgtIp.toArray()).toString()));
|
qPrintable(QHostAddress(tgtIp.toArray()).toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send NA for the NS packet in pktBuf
|
||||||
|
// pktBuf should point to start of ICMPv6 header
|
||||||
|
void Device::sendNeighborAdvertisement(PacketBuffer *pktBuf)
|
||||||
|
{
|
||||||
|
PacketBuffer *naPkt;
|
||||||
|
uchar *pktData = pktBuf->data();
|
||||||
|
quint16 flags = 0x6000; // solicit = 1; overide = 1
|
||||||
|
uchar *ip6Hdr;
|
||||||
|
UInt128 tgtIp, srcIp;
|
||||||
|
|
||||||
|
tgtIp = qFromBigEndian<UInt128>(pktData + 8);
|
||||||
|
if (tgtIp != ip6_) {
|
||||||
|
qDebug("%s: NS tgtIp %s is not us %s", __FUNCTION__,
|
||||||
|
qPrintable(QHostAddress(tgtIp.toArray()).toString()),
|
||||||
|
qPrintable(QHostAddress(ip6_.toArray()).toString()));
|
||||||
|
ip6Hdr = pktBuf->push(kIp6HdrLen);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ip6Hdr = pktBuf->push(kIp6HdrLen);
|
||||||
|
srcIp = qFromBigEndian<UInt128>(ip6Hdr + 8);
|
||||||
|
|
||||||
|
if (srcIp == UInt128(0, 0)) {
|
||||||
|
// reset solicit flag
|
||||||
|
flags &= ~0x4000;
|
||||||
|
// NA should be sent to All nodes address
|
||||||
|
srcIp = UInt128(quint64(0xff02) << 48, quint64(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
naPkt = new PacketBuffer;
|
||||||
|
naPkt->reserve(encapSize() + kIp6HdrLen);
|
||||||
|
pktData = naPkt->put(32);
|
||||||
|
if (pktData) {
|
||||||
|
// Calculate checksum first -
|
||||||
|
// start with fixed fields in ICMP Header and IPv6 Pseudo Header ...
|
||||||
|
quint32 sum = (0x8800 + flags + 0x0201) + (32 + kIpProtoIcmp6);
|
||||||
|
|
||||||
|
// then variable fields from ICMP header ...
|
||||||
|
sum += sumUInt128(tgtIp);
|
||||||
|
sum += (mac_ >> 32) + ((mac_ >> 16) & 0xffff) + (mac_ & 0xffff);
|
||||||
|
|
||||||
|
// and variable fields from IPv6 pseudo header
|
||||||
|
sum += sumUInt128(ip6_);
|
||||||
|
sum += sumUInt128(srcIp);
|
||||||
|
|
||||||
|
while(sum >> 16)
|
||||||
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
|
||||||
|
// Type, Code
|
||||||
|
*(quint16*)(pktData ) = qToBigEndian(quint16(0x8800));
|
||||||
|
// Checksum
|
||||||
|
*(quint16*)(pktData+ 2) = qToBigEndian(quint16(~sum));
|
||||||
|
// Flags-Reserved
|
||||||
|
*(quint32*)(pktData+ 4) = qToBigEndian(quint32(flags << 16));
|
||||||
|
// Target IP
|
||||||
|
memcpy(pktData+ 8, tgtIp.toArray(), 16);
|
||||||
|
// Target Addr TLV + MacAddr
|
||||||
|
*(quint16*)(pktData+24) = qToBigEndian(quint16(0x0201));
|
||||||
|
*(quint32*)(pktData+26) = qToBigEndian(quint32(mac_ >> 16));
|
||||||
|
*(quint16*)(pktData+30) = qToBigEndian(quint16(mac_ & 0xffff));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendIp6(naPkt, srcIp , kIpProtoIcmp6))
|
||||||
|
return;
|
||||||
|
|
||||||
|
qDebug("Sent Neigh Advt to dstIp for tgtIp=%s/%s",
|
||||||
|
qPrintable(QHostAddress(srcIp.toArray()).toString()),
|
||||||
|
qPrintable(QHostAddress(tgtIp.toArray()).toString()));
|
||||||
|
}
|
||||||
|
|
||||||
bool operator<(const DeviceKey &a1, const DeviceKey &a2)
|
bool operator<(const DeviceKey &a1, const DeviceKey &a2)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -82,6 +82,7 @@ private: // methods
|
|||||||
|
|
||||||
void receiveNdp(PacketBuffer *pktBuf);
|
void receiveNdp(PacketBuffer *pktBuf);
|
||||||
void sendNeighborSolicit(PacketBuffer *pktBuf);
|
void sendNeighborSolicit(PacketBuffer *pktBuf);
|
||||||
|
void sendNeighborAdvertisement(PacketBuffer *pktBuf);
|
||||||
|
|
||||||
private: // data
|
private: // data
|
||||||
static const int kMaxVlan = 4;
|
static const int kMaxVlan = 4;
|
||||||
|
@ -31,12 +31,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#define __STDC_FORMAT_MACROS
|
#define __STDC_FORMAT_MACROS
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
const quint64 kBcastMac = 0xffffffffffffULL;
|
||||||
|
|
||||||
inline UInt128 UINT128(OstEmul::Ip6Address x)
|
inline UInt128 UINT128(OstEmul::Ip6Address x)
|
||||||
{
|
{
|
||||||
return UInt128(x.hi(), x.lo());
|
return UInt128(x.hi(), x.lo());
|
||||||
}
|
}
|
||||||
|
|
||||||
const quint64 kBcastMac = 0xffffffffffffULL;
|
inline bool isMacMcast(quint64 mac)
|
||||||
|
{
|
||||||
|
return (mac >> 40) & 0x01 == 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// XXX: Port owning DeviceManager already uses locks, so we don't use any
|
// XXX: Port owning DeviceManager already uses locks, so we don't use any
|
||||||
// locks within DeviceManager to protect deviceGroupList_ et.al.
|
// locks within DeviceManager to protect deviceGroupList_ et.al.
|
||||||
@ -195,14 +201,19 @@ void DeviceManager::receivePacket(PacketBuffer *pktBuf)
|
|||||||
dstMac = qFromBigEndian<quint32>(pktData + offset);
|
dstMac = qFromBigEndian<quint32>(pktData + offset);
|
||||||
offset += 4;
|
offset += 4;
|
||||||
dstMac = (dstMac << 16) | qFromBigEndian<quint16>(pktData + offset);
|
dstMac = (dstMac << 16) | qFromBigEndian<quint16>(pktData + offset);
|
||||||
|
|
||||||
|
qDebug("dstMac %012" PRIx64, dstMac);
|
||||||
|
|
||||||
|
// XXX: Treat multicast as bcast
|
||||||
|
if (isMacMcast(dstMac))
|
||||||
|
dstMac = kBcastMac;
|
||||||
|
|
||||||
dk.setMac(dstMac);
|
dk.setMac(dstMac);
|
||||||
offset += 2;
|
offset += 2;
|
||||||
|
|
||||||
// Skip srcMac - don't care
|
// Skip srcMac - don't care
|
||||||
offset += 6;
|
offset += 6;
|
||||||
|
|
||||||
qDebug("dstMac %012" PRIx64, dstMac);
|
|
||||||
|
|
||||||
_eth_type:
|
_eth_type:
|
||||||
// Extract EthType
|
// Extract EthType
|
||||||
ethType = qFromBigEndian<quint16>(pktData + offset);
|
ethType = qFromBigEndian<quint16>(pktData + offset);
|
||||||
|
@ -670,15 +670,23 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
|
|||||||
|
|
||||||
# ping the tx devices from the DUT
|
# ping the tx devices from the DUT
|
||||||
for i in range(num_devs):
|
for i in range(num_devs):
|
||||||
out = run('ping -c3 10.10.1.'+str(101+i*ip_step), warn_only=True)
|
if has_ip4:
|
||||||
assert '100% packet loss' not in out
|
out = run('ping -c3 10.10.1.'+str(101+i*ip_step), warn_only=True)
|
||||||
# TODO: ip6/ndp
|
assert '100% packet loss' not in out
|
||||||
|
if has_ip6:
|
||||||
|
out = run('ping -6 -c3 1234:1::'+format(101+i*ip_step, 'x'),
|
||||||
|
warn_only=True)
|
||||||
|
assert '100% packet loss' not in out
|
||||||
|
|
||||||
# ping the tx devices from the DUT
|
# ping the rx devices from the DUT
|
||||||
for i in range(num_devs):
|
for i in range(num_devs):
|
||||||
out = run('ping -c3 10.10.2.'+str(101+i*ip_step), warn_only=True)
|
if has_ip4:
|
||||||
assert '100% packet loss' not in out
|
out = run('ping -c3 10.10.2.'+str(101+i*ip_step), warn_only=True)
|
||||||
# TODO: ip6/ndp
|
assert '100% packet loss' not in out
|
||||||
|
if has_ip6:
|
||||||
|
out = run('ping -6 -c3 1234:2::'+format(101+i*ip_step, 'x'),
|
||||||
|
warn_only=True)
|
||||||
|
assert '100% packet loss' not in out
|
||||||
|
|
||||||
# We are all set now - so transmit the stream now
|
# We are all set now - so transmit the stream now
|
||||||
drone.startCapture(ports.rx)
|
drone.startCapture(ports.rx)
|
||||||
|
Loading…
Reference in New Issue
Block a user