Device Emulation (contd.): Implemented responding IPv6 NS with NA

This commit is contained in:
Srivats P 2016-01-08 20:01:42 +05:30
parent eff603304e
commit 04147076c4
4 changed files with 116 additions and 30 deletions

View File

@ -52,6 +52,11 @@ inline quint32 sumUInt128(UInt128 value)
return sum;
}
inline bool isIp6Mcast(UInt128 ip)
{
return (ip.hi64() >> 56) == 0xff;
}
Device::Device(DeviceManager *deviceManager)
{
deviceManager_ = deviceManager;
@ -588,12 +593,14 @@ void Device::receiveIp4(PacketBuffer *pktBuf)
}
ipProto = pktData[9];
qDebug("%s: ipProto = %d", __FUNCTION__, ipProto);
switch (ipProto) {
case 1: // ICMP
pktBuf->pull(20);
receiveIcmp4(pktBuf);
break;
default:
qWarning("%s: Unsupported ipProto %d", __FUNCTION__, ipProto);
break;
}
@ -692,8 +699,9 @@ void Device::receiveIp6(PacketBuffer *pktBuf)
goto _invalid_exit;
}
// FIXME: check for specific mcast address(es) instead of any mcast?
dstIp = qFromBigEndian<UInt128>(pktData + 24);
if (dstIp != ip6_) {
if (!isIp6Mcast(dstIp) && (dstIp != ip6_)) {
qDebug("%s: dstIp %s is not me (%s)", __FUNCTION__,
qPrintable(QHostAddress(dstIp.toArray()).toString()),
qPrintable(QHostAddress(ip6_.toArray()).toString()));
@ -770,32 +778,20 @@ void Device::receiveNdp(PacketBuffer *pktBuf)
{
uchar *pktData = pktBuf->data();
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) {
qDebug("%s: incomplete NA header: expected 32, actual %d",
__FUNCTION__, pktBuf->length());
if (len < minLen) {
qDebug("%s: incomplete NS/NA header: expected %d, actual %d",
__FUNCTION__, minLen, 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
// TODO: Validation as per RFC 4861
sendNeighborAdvertisement(pktBuf);
break;
}
case 136: { // Neigh Advt
@ -902,6 +898,76 @@ void Device::sendNeighborSolicit(PacketBuffer *pktBuf)
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)
{
int i = 0;

View File

@ -82,6 +82,7 @@ private: // methods
void receiveNdp(PacketBuffer *pktBuf);
void sendNeighborSolicit(PacketBuffer *pktBuf);
void sendNeighborAdvertisement(PacketBuffer *pktBuf);
private: // data
static const int kMaxVlan = 4;

View File

@ -31,12 +31,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
const quint64 kBcastMac = 0xffffffffffffULL;
inline UInt128 UINT128(OstEmul::Ip6Address x)
{
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
// locks within DeviceManager to protect deviceGroupList_ et.al.
@ -195,14 +201,19 @@ void DeviceManager::receivePacket(PacketBuffer *pktBuf)
dstMac = qFromBigEndian<quint32>(pktData + offset);
offset += 4;
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);
offset += 2;
// Skip srcMac - don't care
offset += 6;
qDebug("dstMac %012" PRIx64, dstMac);
_eth_type:
// Extract EthType
ethType = qFromBigEndian<quint16>(pktData + offset);

View File

@ -670,15 +670,23 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
# ping the tx devices from the DUT
for i in range(num_devs):
if has_ip4:
out = run('ping -c3 10.10.1.'+str(101+i*ip_step), warn_only=True)
assert '100% packet loss' not in out
# TODO: ip6/ndp
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):
if has_ip4:
out = run('ping -c3 10.10.2.'+str(101+i*ip_step), warn_only=True)
assert '100% packet loss' not in out
# TODO: ip6/ndp
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
drone.startCapture(ports.rx)