ostinato/common/ip6.cpp

821 lines
24 KiB
C++
Raw Normal View History

/*
Copyright (C) 2010 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 "ip6.h"
#include "uint128.h"
#include <QHostAddress>
Ip6Protocol::Ip6Protocol(StreamBase *stream, AbstractProtocol *parent)
: AbstractProtocol(stream, parent)
{
}
Ip6Protocol::~Ip6Protocol()
{
}
AbstractProtocol* Ip6Protocol::createInstance(StreamBase *stream,
AbstractProtocol *parent)
{
return new Ip6Protocol(stream, parent);
}
quint32 Ip6Protocol::protocolNumber() const
{
return OstProto::Protocol::kIp6FieldNumber;
}
void Ip6Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const
{
protocol.MutableExtension(OstProto::ip6)->CopyFrom(data);
protocol.mutable_protocol_id()->set_id(protocolNumber());
}
void Ip6Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol)
{
if (protocol.protocol_id().id() == protocolNumber() &&
protocol.HasExtension(OstProto::ip6))
data.MergeFrom(protocol.GetExtension(OstProto::ip6));
}
QString Ip6Protocol::name() const
{
return QString("Internet Protocol ver 6");
}
QString Ip6Protocol::shortName() const
{
return QString("IPv6");
}
AbstractProtocol::ProtocolIdType Ip6Protocol::protocolIdType() const
{
return ProtocolIdIp;
}
quint32 Ip6Protocol::protocolId(ProtocolIdType type) const
{
switch(type)
{
case ProtocolIdEth: return 0x86dd;
case ProtocolIdIp: return 0x29;
default:break;
}
return AbstractProtocol::protocolId(type);
}
int Ip6Protocol::fieldCount() const
{
return ip6_fieldCount;
}
AbstractProtocol::FieldFlags Ip6Protocol::fieldFlags(int index) const
{
AbstractProtocol::FieldFlags flags;
flags = AbstractProtocol::fieldFlags(index);
switch (index)
{
case ip6_version:
case ip6_trafficClass:
case ip6_flowLabel:
case ip6_payloadLength:
case ip6_nextHeader:
case ip6_hopLimit:
case ip6_srcAddress:
case ip6_dstAddress:
break;
case ip6_isOverrideVersion:
case ip6_isOverridePayloadLength:
case ip6_isOverrideNextHeader:
case ip6_srcAddrMode:
case ip6_srcAddrCount:
case ip6_srcAddrPrefix:
case ip6_dstAddrMode:
case ip6_dstAddrCount:
case ip6_dstAddrPrefix:
flags &= ~FrameField;
flags |= MetaField;
break;
default:
qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__,
index);
break;
}
return flags;
}
QVariant Ip6Protocol::fieldData(int index, FieldAttrib attrib,
int streamIndex) const
{
switch (index)
{
case ip6_version:
{
quint8 ver;
switch(attrib)
{
case FieldValue:
case FieldFrameValue:
case FieldTextValue:
if (data.is_override_version())
ver = data.version() & 0xF;
else
ver = 0x6;
break;
default:
ver = 0; // avoid the 'maybe used unitialized' warning
break;
}
switch(attrib)
{
case FieldName:
return QString("Version");
case FieldValue:
return ver;
case FieldTextValue:
return QString("%1").arg(ver);
case FieldFrameValue:
return QByteArray(1, char(ver));
case FieldBitSize:
return 4;
default:
break;
}
break;
}
case ip6_trafficClass:
{
switch(attrib)
{
case FieldName:
return QString("Traffic Class");
case FieldValue:
return data.traffic_class() & 0xFF;
case FieldTextValue:
return QString("%1").arg(data.traffic_class() & 0xFF,
2, BASE_HEX, QChar('0'));
case FieldFrameValue:
return QByteArray(1, char(data.traffic_class() & 0xFF));
default:
break;
}
break;
}
case ip6_flowLabel:
{
switch(attrib)
{
case FieldName:
return QString("Flow Label");
case FieldValue:
return data.flow_label() & 0xFFFFF;
case FieldTextValue:
return QString("%1").arg(data.flow_label() & 0xFFFFF,
5, BASE_HEX, QChar('0'));
case FieldFrameValue:
{
QByteArray fv;
fv.resize(4);
qToBigEndian((quint32) data.flow_label() & 0xFFFFF,
(uchar*) fv.data());
fv = fv.right(3);
return fv;
}
case FieldBitSize:
return 20;
default:
break;
}
break;
}
case ip6_payloadLength:
{
quint16 len;
switch(attrib)
{
case FieldValue:
case FieldFrameValue:
case FieldTextValue:
if (data.is_override_payload_length())
len = data.payload_length();
else
len = protocolFramePayloadSize(streamIndex);
break;
default:
len = 0; // avoid the 'maybe used unitialized' warning
break;
}
switch(attrib)
{
case FieldName:
return QString("Payload Length");
case FieldValue:
return len;
case FieldFrameValue:
{
QByteArray fv;
fv.resize(2);
qToBigEndian(len, (uchar*) fv.data());
return fv;
}
case FieldTextValue:
return QString("%1").arg(len);
case FieldBitSize:
return 16;
default:
break;
}
break;
}
case ip6_nextHeader:
{
quint8 nextHdr;
switch(attrib)
{
case FieldValue:
case FieldFrameValue:
case FieldTextValue:
if (data.is_override_next_header()) {
nextHdr = data.next_header();
}
else {
nextHdr = payloadProtocolId(ProtocolIdIp);
if ((nextHdr == 0)
&& next
&& (next->protocolIdType() == ProtocolIdNone)) {
nextHdr = 0x3b; // IPv6 No-Next-Header
}
}
break;
default:
nextHdr = 0; // avoid the 'maybe used unitialized' warning
break;
}
switch(attrib)
{
case FieldName:
return QString("Next Header");
case FieldValue:
return nextHdr;
case FieldTextValue:
return QString("%1").arg(nextHdr, 2, BASE_HEX, QChar('0'));
case FieldFrameValue:
return QByteArray(1, char(nextHdr));
default:
break;
}
break;
}
case ip6_hopLimit:
{
switch(attrib)
{
case FieldName:
return QString("Hop Limit");
case FieldValue:
return data.hop_limit() & 0xFF;
case FieldTextValue:
return QString("%1").arg(data.hop_limit() & 0xFF);
case FieldFrameValue:
return QByteArray(1, char(data.hop_limit() & 0xFF));
default:
break;
}
break;
}
case ip6_srcAddress:
{
int u;
UInt128 mask = 0;
UInt128 prefix = 0;
UInt128 host = 0;
UInt128 src(data.src_addr_hi(), data.src_addr_lo());
switch(data.src_addr_mode())
{
case OstProto::Ip6::kFixed:
break;
case OstProto::Ip6::kIncHost:
case OstProto::Ip6::kDecHost:
case OstProto::Ip6::kRandomHost:
u = streamIndex % data.src_addr_count();
mask = ~UInt128(0, 0) << (128 - data.src_addr_prefix());
prefix = src & mask;
if (data.src_addr_mode() == OstProto::Ip6::kIncHost) {
host = ((src & ~mask) + u) & ~mask;
}
else if (data.src_addr_mode() == OstProto::Ip6::kDecHost) {
host = ((src & ~mask) - u) & ~mask;
}
else if (data.src_addr_mode()==OstProto::Ip6::kRandomHost) {
// XXX: qrand is int (32bit) not 64bit, some stdlib
// implementations have RAND_MAX as low as 0x7FFF
host = UInt128(qrand(), qrand()) & ~mask;
}
src = prefix | host;
break;
default:
qWarning("Unhandled src_addr_mode = %d",
data.src_addr_mode());
}
switch(attrib)
{
case FieldName:
return QString("Source");
case FieldValue:
{
QVariant v;
v.setValue(src);
return v;
}
case FieldFrameValue:
case FieldTextValue:
{
QByteArray fv;
fv.resize(16);
qToBigEndian(src, (uchar*) fv.data());
if (attrib == FieldTextValue)
return QHostAddress(src.toArray()).toString();
else
return fv;
}
default:
break;
}
break;
}
case ip6_dstAddress:
{
int u;
UInt128 mask = 0;
UInt128 prefix = 0;
UInt128 host = 0;
UInt128 dst(data.dst_addr_hi(), data.dst_addr_lo());
switch(data.dst_addr_mode())
{
case OstProto::Ip6::kFixed:
break;
case OstProto::Ip6::kIncHost:
case OstProto::Ip6::kDecHost:
case OstProto::Ip6::kRandomHost:
u = streamIndex % data.dst_addr_count();
mask = ~UInt128(0, 0) << (128 - data.dst_addr_prefix());
prefix = dst & mask;
if (data.dst_addr_mode() == OstProto::Ip6::kIncHost) {
host = ((dst & ~mask) + u) & ~mask;
}
else if (data.dst_addr_mode() == OstProto::Ip6::kDecHost) {
host = ((dst & ~mask) - u) & ~mask;
}
else if (data.dst_addr_mode()==OstProto::Ip6::kRandomHost) {
// XXX: qrand is int (32bit) not 64bit, some stdlib
// implementations have RAND_MAX as low as 0x7FFF
host = UInt128(qrand(), qrand()) & ~mask;
}
dst = prefix | host;
break;
default:
qWarning("Unhandled dst_addr_mode = %d",
data.dst_addr_mode());
}
switch(attrib)
{
case FieldName:
return QString("Destination");
case FieldValue:
{
QVariant v;
v.setValue(dst);
return v;
}
case FieldFrameValue:
case FieldTextValue:
{
QByteArray fv;
fv.resize(16);
qToBigEndian(dst, (uchar*) fv.data());
if (attrib == FieldTextValue)
return QHostAddress(dst.toArray()).toString();
else
return fv;
}
default:
break;
}
break;
}
// Meta-Fields
case ip6_isOverrideVersion:
{
switch(attrib)
{
case FieldValue:
return data.is_override_version();
default:
break;
}
break;
}
case ip6_isOverridePayloadLength:
{
switch(attrib)
{
case FieldValue:
return data.is_override_payload_length();
default:
break;
}
break;
}
case ip6_isOverrideNextHeader:
{
switch(attrib)
{
case FieldValue:
return data.is_override_next_header();
default:
break;
}
break;
}
case ip6_srcAddrMode:
{
switch(attrib)
{
case FieldValue:
return data.src_addr_mode();
default:
break;
}
break;
}
case ip6_srcAddrCount:
{
switch(attrib)
{
case FieldValue:
return data.src_addr_count();
default:
break;
}
break;
}
case ip6_srcAddrPrefix:
{
switch(attrib)
{
case FieldValue:
return data.src_addr_prefix();
default:
break;
}
break;
}
case ip6_dstAddrMode:
{
switch(attrib)
{
case FieldValue:
return data.dst_addr_mode();
default:
break;
}
break;
}
case ip6_dstAddrCount:
{
switch(attrib)
{
case FieldValue:
return data.dst_addr_count();
default:
break;
}
break;
}
case ip6_dstAddrPrefix:
{
switch(attrib)
{
case FieldValue:
return data.dst_addr_prefix();
default:
break;
}
break;
}
default:
qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__,
index);
break;
}
return AbstractProtocol::fieldData(index, attrib, streamIndex);
}
bool Ip6Protocol::setFieldData(int index, const QVariant &value,
FieldAttrib attrib)
{
bool isOk = false;
if (attrib != FieldValue)
goto _exit;
switch (index)
{
case ip6_version:
{
uint ver = value.toUInt(&isOk);
if (isOk)
data.set_version(ver & 0xF);
break;
}
case ip6_trafficClass:
{
uint trfClass = value.toUInt(&isOk);
if (isOk)
data.set_traffic_class(trfClass & 0xFF);
break;
}
case ip6_flowLabel:
{
uint fl = value.toUInt(&isOk);
if (isOk)
data.set_flow_label(fl & 0xFFFFF);
break;
}
case ip6_payloadLength:
{
uint len = value.toUInt(&isOk);
if (isOk)
data.set_payload_length(len & 0xFFFF);
break;
}
case ip6_nextHeader:
{
uint ver = value.toUInt(&isOk);
if (isOk)
data.set_next_header(ver & 0xFF);
break;
}
case ip6_hopLimit:
{
uint hl = value.toUInt(&isOk);
if (isOk)
data.set_hop_limit(hl & 0xFF);
break;
}
case ip6_srcAddress:
{
if (value.typeName() == QString("UInt128")) {
UInt128 addr = value.value<UInt128>();
data.set_src_addr_hi(addr.hi64());
data.set_src_addr_lo(addr.lo64());
isOk = true;
break;
}
Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address();
quint64 x;
x = (quint64(addr[0]) << 56)
| (quint64(addr[1]) << 48)
| (quint64(addr[2]) << 40)
| (quint64(addr[3]) << 32)
| (quint64(addr[4]) << 24)
| (quint64(addr[5]) << 16)
| (quint64(addr[6]) << 8)
| (quint64(addr[7]) << 0);
data.set_src_addr_hi(x);
x = (quint64(addr[ 8]) << 56)
| (quint64(addr[ 9]) << 48)
| (quint64(addr[10]) << 40)
| (quint64(addr[11]) << 32)
| (quint64(addr[12]) << 24)
| (quint64(addr[13]) << 16)
| (quint64(addr[14]) << 8)
| (quint64(addr[15]) << 0);
data.set_src_addr_lo(x);
isOk = true;
break;
}
case ip6_dstAddress:
{
if (value.typeName() == QString("UInt128")) {
UInt128 addr = value.value<UInt128>();
data.set_dst_addr_hi(addr.hi64());
data.set_dst_addr_lo(addr.lo64());
isOk = true;
break;
}
Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address();
quint64 x;
x = (quint64(addr[0]) << 56)
| (quint64(addr[1]) << 48)
| (quint64(addr[2]) << 40)
| (quint64(addr[3]) << 32)
| (quint64(addr[4]) << 24)
| (quint64(addr[5]) << 16)
| (quint64(addr[6]) << 8)
| (quint64(addr[7]) << 0);
data.set_dst_addr_hi(x);
x = (quint64(addr[ 8]) << 56)
| (quint64(addr[ 9]) << 48)
| (quint64(addr[10]) << 40)
| (quint64(addr[11]) << 32)
| (quint64(addr[12]) << 24)
| (quint64(addr[13]) << 16)
| (quint64(addr[14]) << 8)
| (quint64(addr[15]) << 0);
data.set_dst_addr_lo(x);
isOk = true;
break;
}
// Meta-Fields
case ip6_isOverrideVersion:
{
bool ovr = value.toBool();
data.set_is_override_version(ovr);
isOk = true;
break;
}
case ip6_isOverridePayloadLength:
{
bool ovr = value.toBool();
data.set_is_override_payload_length(ovr);
isOk = true;
break;
}
case ip6_isOverrideNextHeader:
{
bool ovr = value.toBool();
data.set_is_override_next_header(ovr);
isOk = true;
break;
}
case ip6_srcAddrMode:
{
uint mode = value.toUInt(&isOk);
if (isOk && data.AddrMode_IsValid(mode))
data.set_src_addr_mode((OstProto::Ip6::AddrMode) mode);
else
isOk = false;
break;
}
case ip6_srcAddrCount:
{
uint count = value.toUInt(&isOk);
if (isOk)
data.set_src_addr_count(count);
break;
}
case ip6_srcAddrPrefix:
{
uint prefix = value.toUInt(&isOk);
if (isOk)
data.set_src_addr_prefix(prefix);
break;
}
case ip6_dstAddrMode:
{
uint mode = value.toUInt(&isOk);
if (isOk && data.AddrMode_IsValid(mode))
data.set_dst_addr_mode((OstProto::Ip6::AddrMode) mode);
else
isOk = false;
break;
}
case ip6_dstAddrCount:
{
uint count = value.toUInt(&isOk);
if (isOk)
data.set_dst_addr_count(count);
break;
}
case ip6_dstAddrPrefix:
{
uint prefix = value.toUInt(&isOk);
if (isOk)
data.set_dst_addr_prefix(prefix);
break;
}
default:
qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__,
index);
break;
}
_exit:
return isOk;
}
int Ip6Protocol::protocolFrameVariableCount() const
{
int count = AbstractProtocol::protocolFrameVariableCount();
if (data.src_addr_mode() != OstProto::Ip6::kFixed)
count = AbstractProtocol::lcm(count, data.src_addr_count());
if (data.dst_addr_mode() != OstProto::Ip6::kFixed)
count = AbstractProtocol::lcm(count, data.dst_addr_count());
return count;
}
quint32 Ip6Protocol::protocolFrameCksum(int streamIndex,
CksumType cksumType, CksumFlags cksumFlags) const
{
if (cksumType == CksumIpPseudo)
{
quint32 sum = 0;
QByteArray fv = protocolFrameValue(streamIndex);
const quint8 *p = (quint8*) fv.constData();
// src-ip, dst-ip
for (int i = 8; i < fv.size(); i+=2)
sum += *((quint16*)(p + i));
// XXX: payload length and protocol are also part of the
// pseudo cksum but we need to skip extension headers to
// get to them as per RFC 8200 Section 8.1
// Since we can't traverse beyond our immediate neighboring
// protocol from here, these two fields are counted in the
// pseudo cksum in AbstractProtocol::protocolFrameHeaderCksum()
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
return qFromBigEndian((quint16) ~sum);
}
return AbstractProtocol::protocolFrameCksum(
streamIndex, cksumType, cksumFlags);
}
bool Ip6Protocol::hasErrors(QStringList *errors) const
{
bool result = false;
if ((data.dst_addr_hi() == 0ULL) && (data.dst_addr_lo() == 0ULL)
&& (data.dst_addr_mode() == OstProto::Ip6::kFixed)) {
if (errors)
*errors << QObject::tr("Frames with Destination IP :: (all zeroes) "
"are likely to be dropped");
result = true;
}
if ((data.src_addr_hi() == 0ULL) && (data.src_addr_lo() == 0ULL)
&& (data.src_addr_mode() == OstProto::Ip6::kFixed)) {
if (errors)
*errors << QObject::tr("Frames with Source IP :: (all zeroes) "
"are likely to be dropped");
result = true;
}
return result;
}