2015-09-14 07:49:52 -05:00
|
|
|
/*
|
|
|
|
Copyright (C) 2015 Srivats P.
|
|
|
|
|
|
|
|
This file is part of "Ostinato"
|
|
|
|
|
|
|
|
This is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "device.h"
|
|
|
|
|
|
|
|
#include "../common/emulproto.pb.h"
|
|
|
|
#include "devicemanager.h"
|
|
|
|
#include "packetbuffer.h"
|
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
#include <QHostAddress>
|
2015-09-14 07:49:52 -05:00
|
|
|
#include <qendian.h>
|
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
const int kBaseHex = 16;
|
2015-11-04 07:20:08 -06:00
|
|
|
const quint64 kBcastMac = 0xffffffffffffULL;
|
2015-09-14 07:49:52 -05:00
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
/*
|
2015-11-14 05:36:43 -06:00
|
|
|
* NOTE:
|
2015-09-20 07:19:15 -05:00
|
|
|
* 1. Device Key is (VLANS + MAC) - is assumed to be unique for a device
|
2015-11-14 05:36:43 -06:00
|
|
|
* 2. Device clients/users (viz. DeviceManager) should take care when
|
2015-09-20 07:19:15 -05:00
|
|
|
* setting params that change the key, if the key is used elsewhere
|
|
|
|
* (e.g. in a hash)
|
|
|
|
*/
|
|
|
|
|
|
|
|
Device::Device(DeviceManager *deviceManager)
|
2015-09-14 07:49:52 -05:00
|
|
|
{
|
|
|
|
deviceManager_ = deviceManager;
|
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
for (int i = 0; i < kMaxVlan; i++)
|
|
|
|
vlan_[i] = 0;
|
|
|
|
numVlanTags_ = 0;
|
|
|
|
mac_ = 0;
|
2015-12-31 08:47:56 -06:00
|
|
|
|
|
|
|
ip4_ = ip4Gateway_ = 0;
|
2015-09-20 07:19:15 -05:00
|
|
|
ip4PrefixLength_ = 0;
|
2015-09-14 07:49:52 -05:00
|
|
|
|
2015-12-31 08:47:56 -06:00
|
|
|
ip6_ = ip6Gateway_ = UInt128(0, 0);
|
|
|
|
ip6PrefixLength_ = 0;
|
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
clearKey();
|
2015-09-14 07:49:52 -05:00
|
|
|
}
|
|
|
|
|
2015-12-20 08:03:02 -06:00
|
|
|
void Device::setVlan(int index, quint16 vlan, quint16 tpid)
|
2015-09-14 07:49:52 -05:00
|
|
|
{
|
2015-09-20 07:19:15 -05:00
|
|
|
int ofs;
|
2015-09-14 07:49:52 -05:00
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
if ((index < 0) || (index >= kMaxVlan)) {
|
|
|
|
qWarning("%s: vlan index %d out of range (0 - %d)", __FUNCTION__,
|
|
|
|
index, kMaxVlan - 1);
|
|
|
|
return;
|
|
|
|
}
|
2015-09-14 07:49:52 -05:00
|
|
|
|
2015-12-20 08:03:02 -06:00
|
|
|
vlan_[index] = (tpid << 16) | vlan;
|
2015-09-14 07:49:52 -05:00
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
ofs = index * sizeof(quint16);
|
|
|
|
key_[ofs] = vlan >> 8;
|
|
|
|
key_[ofs+1] = vlan & 0xff;
|
2015-09-14 07:49:52 -05:00
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
if (index >= numVlanTags_)
|
|
|
|
numVlanTags_ = index + 1;
|
|
|
|
}
|
2015-09-14 07:49:52 -05:00
|
|
|
|
2015-11-10 08:10:32 -06:00
|
|
|
quint64 Device::mac()
|
|
|
|
{
|
|
|
|
return mac_;
|
|
|
|
}
|
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
void Device::setMac(quint64 mac)
|
|
|
|
{
|
|
|
|
int ofs = kMaxVlan * sizeof(quint16);
|
2015-09-14 07:49:52 -05:00
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
mac_ = mac;
|
|
|
|
memcpy(key_.data() + ofs, (char*)&mac, sizeof(mac));
|
2015-09-14 07:49:52 -05:00
|
|
|
}
|
|
|
|
|
2015-11-04 07:20:08 -06:00
|
|
|
void Device::setIp4(quint32 address, int prefixLength, quint32 gateway)
|
2015-09-14 07:49:52 -05:00
|
|
|
{
|
2015-09-20 07:19:15 -05:00
|
|
|
ip4_ = address;
|
|
|
|
ip4PrefixLength_ = prefixLength;
|
2015-11-04 07:20:08 -06:00
|
|
|
ip4Gateway_ = gateway;
|
2015-09-14 07:49:52 -05:00
|
|
|
}
|
|
|
|
|
2015-12-31 08:47:56 -06:00
|
|
|
void Device::setIp6(UInt128 address, int prefixLength, UInt128 gateway)
|
|
|
|
{
|
|
|
|
ip6_ = address;
|
|
|
|
ip6PrefixLength_ = prefixLength;
|
|
|
|
ip6Gateway_ = gateway;
|
|
|
|
}
|
|
|
|
|
2015-11-11 01:35:15 -06:00
|
|
|
void Device::getConfig(OstEmul::Device *deviceConfig)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < numVlanTags_; i++)
|
|
|
|
deviceConfig->add_vlan(vlan_[i]);
|
|
|
|
deviceConfig->set_mac(mac_);
|
2015-12-31 08:47:56 -06:00
|
|
|
|
2015-11-11 01:35:15 -06:00
|
|
|
deviceConfig->set_ip4(ip4_);
|
|
|
|
deviceConfig->set_ip4_prefix_length(ip4PrefixLength_);
|
|
|
|
deviceConfig->set_ip4_default_gateway(ip4Gateway_);
|
2015-12-31 08:47:56 -06:00
|
|
|
|
|
|
|
#if 0 // FIXME
|
|
|
|
deviceConfig->set_ip6(ip6_);
|
|
|
|
deviceConfig->set_ip6_prefix_length(ip6PrefixLength_);
|
|
|
|
deviceConfig->set_ip6_default_gateway(ip6Gateway_);
|
|
|
|
#endif
|
2015-11-11 01:35:15 -06:00
|
|
|
}
|
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
QString Device::config()
|
2015-09-14 07:49:52 -05:00
|
|
|
{
|
2015-12-31 08:47:56 -06:00
|
|
|
return QString("<vlans=%1/%2/%3/%4 mac=%5 ip4=%6/%7 ip6=%8/%9>")
|
2015-12-20 08:03:02 -06:00
|
|
|
.arg((vlan_[0] >> 16) != kVlanTpid ?
|
|
|
|
QString("0x%1-%2")
|
|
|
|
.arg(vlan_[0] >> 16, 4, kBaseHex, QChar('0'))
|
|
|
|
.arg(vlan_[0] & 0xFFFF) :
|
|
|
|
QString("%1")
|
|
|
|
.arg(vlan_[0] & 0xFFFF))
|
|
|
|
.arg((vlan_[1] >> 16) != kVlanTpid ?
|
|
|
|
QString("0x%1-%2")
|
|
|
|
.arg(vlan_[1] >> 16, 4, kBaseHex, QChar('0'))
|
|
|
|
.arg(vlan_[1] & 0xFFFF) :
|
|
|
|
QString("%1")
|
|
|
|
.arg(vlan_[1] & 0xFFFF))
|
|
|
|
.arg((vlan_[2] >> 16) != kVlanTpid ?
|
|
|
|
QString("0x%1-%2")
|
|
|
|
.arg(vlan_[2] >> 16, 4, kBaseHex, QChar('0'))
|
|
|
|
.arg(vlan_[2] & 0xFFFF) :
|
|
|
|
QString("%1")
|
|
|
|
.arg(vlan_[2] & 0xFFFF))
|
|
|
|
.arg((vlan_[3] >> 16) != kVlanTpid ?
|
|
|
|
QString("0x%1-%2")
|
|
|
|
.arg(vlan_[3] >> 16, 4, kBaseHex, QChar('0'))
|
|
|
|
.arg(vlan_[3] & 0xFFFF) :
|
|
|
|
QString("%1")
|
|
|
|
.arg(vlan_[3] & 0xFFFF))
|
2015-09-20 07:19:15 -05:00
|
|
|
.arg(mac_, 12, kBaseHex, QChar('0'))
|
|
|
|
.arg(QHostAddress(ip4_).toString())
|
2015-12-31 08:47:56 -06:00
|
|
|
.arg(ip4PrefixLength_)
|
|
|
|
.arg(QHostAddress(ip6_.toArray()).toString())
|
|
|
|
.arg(ip6PrefixLength_);
|
2015-09-20 07:19:15 -05:00
|
|
|
}
|
2015-09-14 07:49:52 -05:00
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
DeviceKey Device::key()
|
|
|
|
{
|
|
|
|
return key_;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Device::clearKey()
|
2015-11-14 05:36:43 -06:00
|
|
|
{
|
2015-09-20 07:19:15 -05:00
|
|
|
key_.fill(0, kMaxVlan * sizeof(quint16) + sizeof(quint64));
|
|
|
|
}
|
|
|
|
|
|
|
|
int Device::encapSize()
|
|
|
|
{
|
|
|
|
// ethernet header + vlans
|
2015-11-14 05:36:43 -06:00
|
|
|
int size = 14 + 4*numVlanTags_;
|
2015-09-14 07:49:52 -05:00
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Device::encap(PacketBuffer *pktBuf, quint64 dstMac, quint16 type)
|
|
|
|
{
|
2015-09-20 07:19:15 -05:00
|
|
|
int ofs;
|
2015-11-14 05:36:43 -06:00
|
|
|
quint64 srcMac = mac_;
|
2015-09-14 07:49:52 -05:00
|
|
|
uchar *p = pktBuf->push(encapSize());
|
|
|
|
|
|
|
|
if (!p) {
|
|
|
|
qWarning("%s: failed to push %d bytes [0x%p, 0x%p]", __FUNCTION__,
|
|
|
|
encapSize(), pktBuf->head(), pktBuf->data());
|
|
|
|
goto _exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
*(quint32*)(p ) = qToBigEndian(quint32(dstMac >> 16));
|
|
|
|
*(quint16*)(p + 4) = qToBigEndian(quint16(dstMac & 0xffff));
|
|
|
|
*(quint32*)(p + 6) = qToBigEndian(quint32(srcMac >> 16));
|
|
|
|
*(quint16*)(p + 10) = qToBigEndian(quint16(srcMac & 0xffff));
|
2015-09-20 07:19:15 -05:00
|
|
|
ofs = 12;
|
|
|
|
for (int i = 0; i < numVlanTags_; i++) {
|
2015-12-20 08:03:02 -06:00
|
|
|
*(quint32*)(p + ofs) = qToBigEndian(vlan_[i]);
|
2015-09-20 07:19:15 -05:00
|
|
|
ofs += 4;
|
|
|
|
}
|
|
|
|
*(quint16*)(p + ofs) = qToBigEndian(type);
|
|
|
|
ofs += 2;
|
|
|
|
|
|
|
|
Q_ASSERT(ofs == encapSize());
|
2015-09-14 07:49:52 -05:00
|
|
|
|
|
|
|
_exit:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-04 07:20:08 -06:00
|
|
|
// We expect pktBuf to point to EthType on entry
|
|
|
|
void Device::receivePacket(PacketBuffer *pktBuf)
|
|
|
|
{
|
|
|
|
quint16 ethType = qFromBigEndian<quint16>(pktBuf->data());
|
|
|
|
pktBuf->pull(2);
|
|
|
|
|
|
|
|
qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType);
|
|
|
|
|
|
|
|
switch(ethType)
|
|
|
|
{
|
|
|
|
case 0x0806: // ARP
|
|
|
|
receiveArp(pktBuf);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0800: // IPv4
|
2015-12-23 09:48:25 -06:00
|
|
|
receiveIp4(pktBuf);
|
|
|
|
break;
|
|
|
|
|
2015-11-04 07:20:08 -06:00
|
|
|
case 0x86dd: // IPv6
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// FIXME: temporary hack till DeviceManager clones pbufs
|
|
|
|
pktBuf->push(2);
|
|
|
|
}
|
|
|
|
|
2015-09-14 07:49:52 -05:00
|
|
|
void Device::transmitPacket(PacketBuffer *pktBuf)
|
|
|
|
{
|
|
|
|
deviceManager_->transmitPacket(pktBuf);
|
|
|
|
}
|
|
|
|
|
2015-11-04 07:20:08 -06:00
|
|
|
void Device::clearNeighbors()
|
|
|
|
{
|
|
|
|
arpTable.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resolve the Neighbor IP address for this to-be-transmitted pktBuf
|
2015-09-20 07:19:15 -05:00
|
|
|
// We expect pktBuf to point to EthType on entry
|
2015-11-04 07:20:08 -06:00
|
|
|
void Device::resolveNeighbor(PacketBuffer *pktBuf)
|
2015-09-14 07:49:52 -05:00
|
|
|
{
|
2015-09-20 07:19:15 -05:00
|
|
|
quint16 ethType = qFromBigEndian<quint16>(pktBuf->data());
|
|
|
|
pktBuf->pull(2);
|
2015-09-14 07:49:52 -05:00
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType);
|
2015-09-14 07:49:52 -05:00
|
|
|
|
|
|
|
switch(ethType)
|
|
|
|
{
|
2015-11-04 07:20:08 -06:00
|
|
|
case 0x0800: // IPv4
|
|
|
|
sendArpRequest(pktBuf);
|
2015-09-14 07:49:52 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x86dd: // IPv6
|
2015-12-31 08:47:56 -06:00
|
|
|
sendNeighborSolicit(pktBuf);
|
|
|
|
break;
|
|
|
|
|
2015-09-14 07:49:52 -05:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2015-10-03 02:48:44 -05:00
|
|
|
// FIXME: temporary hack till DeviceManager clones pbufs
|
|
|
|
pktBuf->push(2);
|
2015-09-14 07:49:52 -05:00
|
|
|
}
|
|
|
|
|
2015-11-04 07:20:08 -06:00
|
|
|
// Append this device's neighbors to the list
|
2015-11-10 23:22:29 -06:00
|
|
|
void Device::getNeighbors(OstEmul::DeviceNeighborList *neighbors)
|
2015-11-04 07:20:08 -06:00
|
|
|
{
|
|
|
|
QList<quint32> ipList = arpTable.keys();
|
|
|
|
QList<quint64> macList = arpTable.values();
|
|
|
|
|
|
|
|
Q_ASSERT(ipList.size() == macList.size());
|
|
|
|
|
|
|
|
for (int i = 0; i < ipList.size(); i++) {
|
|
|
|
OstEmul::ArpEntry *arp = neighbors->add_arp();
|
|
|
|
arp->set_ip4(ipList.at(i));
|
|
|
|
arp->set_mac(macList.at(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-10 08:10:32 -06:00
|
|
|
// We expect pktBuf to point to EthType on entry
|
|
|
|
bool Device::isOrigin(const PacketBuffer *pktBuf)
|
|
|
|
{
|
|
|
|
const uchar *pktData = pktBuf->data();
|
|
|
|
quint16 ethType = qFromBigEndian<quint16>(pktData);
|
|
|
|
|
|
|
|
qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType);
|
|
|
|
pktData += 2;
|
|
|
|
|
|
|
|
// We know only about IP packets
|
|
|
|
if (ethType == 0x0800) { // IPv4
|
|
|
|
int ipHdrLen = (pktData[0] & 0x0F) << 2;
|
|
|
|
quint32 srcIp;
|
|
|
|
|
|
|
|
if (pktBuf->length() < ipHdrLen) {
|
|
|
|
qDebug("incomplete IPv4 header: expected %d, actual %d",
|
|
|
|
ipHdrLen, pktBuf->length());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
srcIp = qFromBigEndian<quint32>(pktData + ipHdrLen - 8);
|
|
|
|
qDebug("%s: pktSrcIp/selfIp = 0x%x/0x%x", __FUNCTION__, srcIp, ip4_);
|
|
|
|
return (srcIp == ip4_);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We expect pktBuf to point to EthType on entry
|
|
|
|
quint64 Device::neighborMac(const PacketBuffer *pktBuf)
|
|
|
|
{
|
|
|
|
const uchar *pktData = pktBuf->data();
|
|
|
|
quint16 ethType = qFromBigEndian<quint16>(pktData);
|
|
|
|
|
|
|
|
qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType);
|
|
|
|
pktData += 2;
|
|
|
|
|
|
|
|
// We know only about IP packets
|
|
|
|
if (ethType == 0x0800) { // IPv4
|
|
|
|
int ipHdrLen = (pktData[0] & 0x0F) << 2;
|
|
|
|
quint32 dstIp, tgtIp, mask;
|
|
|
|
|
|
|
|
if (pktBuf->length() < ipHdrLen) {
|
|
|
|
qDebug("incomplete IPv4 header: expected %d, actual %d",
|
|
|
|
ipHdrLen, pktBuf->length());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
dstIp = qFromBigEndian<quint32>(pktData + ipHdrLen - 4);
|
|
|
|
mask = ~0 << (32 - ip4PrefixLength_);
|
|
|
|
qDebug("dst %x self %x mask %x", dstIp, ip4_, mask);
|
|
|
|
tgtIp = ((dstIp & mask) == (ip4_ & mask)) ? dstIp : ip4Gateway_;
|
|
|
|
|
|
|
|
return arpTable.value(tgtIp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
//
|
|
|
|
// Private Methods
|
|
|
|
//
|
|
|
|
void Device::receiveArp(PacketBuffer *pktBuf)
|
2015-09-14 07:49:52 -05:00
|
|
|
{
|
2015-09-20 07:19:15 -05:00
|
|
|
PacketBuffer *rspPkt;
|
2015-09-14 07:49:52 -05:00
|
|
|
uchar *pktData = pktBuf->data();
|
|
|
|
int offset = 0;
|
|
|
|
quint16 hwType, protoType;
|
|
|
|
quint8 hwAddrLen, protoAddrLen;
|
|
|
|
quint16 opCode;
|
|
|
|
quint64 srcMac, tgtMac;
|
|
|
|
quint32 srcIp, tgtIp;
|
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
// Extract tgtIp first to check quickly if this packet is for us or not
|
|
|
|
tgtIp = qFromBigEndian<quint32>(pktData + 24);
|
|
|
|
if (tgtIp != ip4_) {
|
2015-11-14 05:36:43 -06:00
|
|
|
qDebug("tgtIp %s is not me %s",
|
2015-09-20 07:19:15 -05:00
|
|
|
qPrintable(QHostAddress(tgtIp).toString()),
|
|
|
|
qPrintable(QHostAddress(ip4_).toString()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-09-14 07:49:52 -05:00
|
|
|
// Extract annd verify ARP packet contents
|
|
|
|
hwType = qFromBigEndian<quint16>(pktData + offset);
|
|
|
|
offset += 2;
|
|
|
|
if (hwType != 1) // Mac
|
|
|
|
goto _invalid_exit;
|
|
|
|
|
|
|
|
protoType = qFromBigEndian<quint16>(pktData + offset);
|
|
|
|
offset += 2;
|
|
|
|
if (protoType != 0x0800) // IPv4
|
|
|
|
goto _invalid_exit;
|
|
|
|
|
|
|
|
hwAddrLen = pktData[offset];
|
|
|
|
offset += 1;
|
|
|
|
if (hwAddrLen != 6)
|
|
|
|
goto _invalid_exit;
|
|
|
|
|
|
|
|
protoAddrLen = pktData[offset];
|
|
|
|
offset += 1;
|
|
|
|
if (protoAddrLen != 4)
|
|
|
|
goto _invalid_exit;
|
|
|
|
|
|
|
|
opCode = qFromBigEndian<quint16>(pktData + offset);
|
|
|
|
offset += 2;
|
|
|
|
|
|
|
|
srcMac = qFromBigEndian<quint32>(pktData + offset);
|
|
|
|
offset += 4;
|
|
|
|
srcMac = (srcMac << 16) | qFromBigEndian<quint16>(pktData + offset);
|
|
|
|
offset += 2;
|
|
|
|
|
|
|
|
srcIp = qFromBigEndian<quint32>(pktData + offset);
|
|
|
|
offset += 4;
|
|
|
|
|
|
|
|
tgtMac = qFromBigEndian<quint32>(pktData + offset);
|
|
|
|
offset += 4;
|
|
|
|
tgtMac = (tgtMac << 16) | qFromBigEndian<quint16>(pktData + offset);
|
|
|
|
offset += 2;
|
|
|
|
|
|
|
|
switch (opCode)
|
|
|
|
{
|
2015-09-20 07:19:15 -05:00
|
|
|
case 1: // ARP Request
|
2015-11-04 07:20:08 -06:00
|
|
|
arpTable.insert(srcIp, srcMac);
|
|
|
|
|
2015-09-20 07:19:15 -05:00
|
|
|
rspPkt = new PacketBuffer;
|
2015-11-14 05:36:43 -06:00
|
|
|
rspPkt->reserve(encapSize());
|
2015-09-20 07:19:15 -05:00
|
|
|
pktData = rspPkt->put(28);
|
|
|
|
if (pktData) {
|
|
|
|
// HTYP, PTYP
|
|
|
|
*(quint32*)(pktData ) = qToBigEndian(quint32(0x00010800));
|
|
|
|
// HLEN, PLEN, OPER
|
|
|
|
*(quint32*)(pktData+ 4) = qToBigEndian(quint32(0x06040002));
|
|
|
|
// Source H/W Addr, Proto Addr
|
|
|
|
*(quint32*)(pktData+ 8) = qToBigEndian(quint32(mac_ >> 16));
|
|
|
|
*(quint16*)(pktData+12) = qToBigEndian(quint16(mac_ & 0xffff));
|
|
|
|
*(quint32*)(pktData+14) = qToBigEndian(ip4_);
|
|
|
|
// Target H/W Addr, Proto Addr
|
|
|
|
*(quint32*)(pktData+18) = qToBigEndian(quint32(srcMac >> 16));
|
|
|
|
*(quint16*)(pktData+22) = qToBigEndian(quint16(srcMac & 0xffff));
|
|
|
|
*(quint32*)(pktData+24) = qToBigEndian(srcIp);
|
2015-09-14 07:49:52 -05:00
|
|
|
}
|
2015-09-20 07:19:15 -05:00
|
|
|
|
|
|
|
encap(rspPkt, srcMac, 0x0806);
|
|
|
|
transmitPacket(rspPkt);
|
|
|
|
|
|
|
|
qDebug("Sent ARP Reply for srcIp/tgtIp=%s/%s",
|
2015-11-14 05:36:43 -06:00
|
|
|
qPrintable(QHostAddress(srcIp).toString()),
|
2015-09-20 07:19:15 -05:00
|
|
|
qPrintable(QHostAddress(tgtIp).toString()));
|
2015-09-14 07:49:52 -05:00
|
|
|
break;
|
|
|
|
case 2: // ARP Response
|
2015-11-04 07:20:08 -06:00
|
|
|
arpTable.insert(srcIp, srcMac);
|
|
|
|
break;
|
|
|
|
|
2015-09-14 07:49:52 -05:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
_invalid_exit:
|
|
|
|
qWarning("Invalid ARP content");
|
|
|
|
return;
|
|
|
|
}
|
2015-11-04 07:20:08 -06:00
|
|
|
|
|
|
|
// Send ARP request for the IPv4 packet in pktBuf
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
if (pktBuf->length() < ipHdrLen) {
|
|
|
|
qDebug("incomplete IPv4 header: expected %d, actual %d",
|
|
|
|
ipHdrLen, pktBuf->length());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extract srcIp first to check quickly that this packet originates
|
|
|
|
// from this device
|
|
|
|
srcIp = qFromBigEndian<quint32>(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<quint32>(pktData + ipHdrLen - 4);
|
|
|
|
|
|
|
|
mask = ~0 << (32 - ip4PrefixLength_);
|
|
|
|
qDebug("dst %x src %x mask %x", dstIp, srcIp, mask);
|
|
|
|
tgtIp = ((dstIp & mask) == (srcIp & mask)) ? dstIp : ip4Gateway_;
|
|
|
|
|
2015-11-10 08:10:32 -06:00
|
|
|
// Do we already have a ARP entry (resolved or unresolved)?
|
|
|
|
// FIXME: do we need a timer to resend ARP for unresolved entries?
|
|
|
|
if (arpTable.contains(tgtIp))
|
|
|
|
return;
|
|
|
|
|
2015-11-04 07:20:08 -06:00
|
|
|
reqPkt = new PacketBuffer;
|
|
|
|
reqPkt->reserve(encapSize());
|
|
|
|
pktData = reqPkt->put(28);
|
|
|
|
if (pktData) {
|
|
|
|
// HTYP, PTYP
|
|
|
|
*(quint32*)(pktData ) = qToBigEndian(quint32(0x00010800));
|
|
|
|
// HLEN, PLEN, OPER
|
|
|
|
*(quint32*)(pktData+ 4) = qToBigEndian(quint32(0x06040001));
|
|
|
|
// Source H/W Addr, Proto Addr
|
|
|
|
*(quint32*)(pktData+ 8) = qToBigEndian(quint32(mac_ >> 16));
|
|
|
|
*(quint16*)(pktData+12) = qToBigEndian(quint16(mac_ & 0xffff));
|
|
|
|
*(quint32*)(pktData+14) = qToBigEndian(srcIp);
|
|
|
|
// Target H/W Addr, Proto Addr
|
|
|
|
*(quint32*)(pktData+18) = qToBigEndian(quint32(0));
|
|
|
|
*(quint16*)(pktData+22) = qToBigEndian(quint16(0));
|
|
|
|
*(quint32*)(pktData+24) = qToBigEndian(tgtIp);
|
|
|
|
}
|
|
|
|
|
|
|
|
encap(reqPkt, kBcastMac, 0x0806);
|
|
|
|
transmitPacket(reqPkt);
|
2015-11-10 08:10:32 -06:00
|
|
|
arpTable.insert(tgtIp, 0);
|
2015-11-04 07:20:08 -06:00
|
|
|
|
|
|
|
qDebug("Sent ARP Request for srcIp/tgtIp=%s/%s",
|
|
|
|
qPrintable(QHostAddress(srcIp).toString()),
|
|
|
|
qPrintable(QHostAddress(tgtIp).toString()));
|
|
|
|
}
|
2015-11-29 09:48:31 -06:00
|
|
|
|
2015-12-23 09:48:25 -06:00
|
|
|
void Device::receiveIp4(PacketBuffer *pktBuf)
|
|
|
|
{
|
|
|
|
uchar *pktData = pktBuf->data();
|
|
|
|
uchar ipProto;
|
|
|
|
quint32 dstIp;
|
|
|
|
|
|
|
|
if (pktData[0] != 0x45) {
|
|
|
|
qDebug("%s: Unsupported IP version or options (%02x) ", __FUNCTION__,
|
|
|
|
pktData[0]);
|
|
|
|
goto _invalid_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pktBuf->length() < 20) {
|
|
|
|
qDebug("incomplete IPv4 header: expected 20, actual %d",
|
|
|
|
pktBuf->length());
|
|
|
|
goto _invalid_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX: We don't verify IP Header checksum
|
|
|
|
|
|
|
|
dstIp = qFromBigEndian<quint32>(pktData + 16);
|
|
|
|
if (dstIp != ip4_) {
|
|
|
|
qDebug("%s: dstIp %x is not me (%x)", __FUNCTION__, dstIp, ip4_);
|
|
|
|
goto _invalid_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
ipProto = pktData[9];
|
|
|
|
switch (ipProto) {
|
|
|
|
case 1: // ICMP
|
|
|
|
pktBuf->pull(20);
|
|
|
|
receiveIcmp4(pktBuf);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
_invalid_exit:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function assumes we are replying back to the same IP
|
|
|
|
// that originally sent us the packet and therefore we can reuse the
|
|
|
|
// ingress packet for egress; in other words, it assumes the
|
|
|
|
// original IP header is intact and will just reuse it after
|
|
|
|
// minimal modifications
|
|
|
|
void Device::sendIp4Reply(PacketBuffer *pktBuf)
|
|
|
|
{
|
|
|
|
uchar *pktData = pktBuf->push(20);
|
|
|
|
uchar origTtl = pktData[8];
|
|
|
|
uchar ipProto = pktData[9];
|
|
|
|
quint32 srcIp, dstIp;
|
|
|
|
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))
|
|
|
|
return;
|
|
|
|
|
|
|
|
*(quint32*)(pktData + 12) = qToBigEndian(srcIp);
|
|
|
|
*(quint32*)(pktData + 16) = qToBigEndian(dstIp);
|
|
|
|
|
|
|
|
// Reset TTL
|
|
|
|
pktData[8] = 64;
|
|
|
|
|
|
|
|
// Incremental checksum update (RFC 1624 [Eqn.3])
|
|
|
|
// HC' = ~(~HC + ~m + m')
|
|
|
|
sum = quint16(~qFromBigEndian<quint16>(pktData + 10)); // old cksum
|
|
|
|
sum += quint16(~quint16(origTtl << 8 | ipProto)); // old value
|
|
|
|
sum += quint16(pktData[8] << 8 | ipProto); // new value
|
|
|
|
while(sum >> 16)
|
|
|
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
|
|
|
*(quint16*)(pktData + 10) = qToBigEndian(quint16(~sum));
|
|
|
|
|
|
|
|
encap(pktBuf, arpTable.value(dstIp), 0x0800);
|
|
|
|
transmitPacket(pktBuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Device::receiveIcmp4(PacketBuffer *pktBuf)
|
|
|
|
{
|
|
|
|
uchar *pktData = pktBuf->data();
|
|
|
|
quint32 sum;
|
|
|
|
|
|
|
|
// XXX: We don't verify icmp checksum
|
|
|
|
|
|
|
|
// We handle only ping request
|
|
|
|
if (pktData[0] != 8) { // Echo Request
|
|
|
|
qDebug("%s: Ignoring non echo request (%d)", __FUNCTION__, pktData[0]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2015-12-31 08:47:56 -06:00
|
|
|
// Send NS for the IPv6 packet in pktBuf
|
|
|
|
// pktBuf points to start of IP header
|
|
|
|
void Device::sendNeighborSolicit(PacketBuffer *pktBuf)
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
2015-11-29 09:48:31 -06:00
|
|
|
bool operator<(const DeviceKey &a1, const DeviceKey &a2)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
while (i < a1.size()) {
|
|
|
|
if (uchar(a1.at(i)) < uchar(a2.at(i)))
|
|
|
|
return true;
|
|
|
|
if (uchar(a1.at(i)) > uchar(a2.at(i)))
|
|
|
|
return false;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|