356 lines
12 KiB
C++
356 lines
12 KiB
C++
/*
|
|
Copyright (C) 2016 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 "devicegroupdialog.h"
|
|
|
|
#include "port.h"
|
|
#include "spinboxdelegate.h"
|
|
|
|
#include "emulproto.pb.h"
|
|
#include "uint128.h"
|
|
|
|
#include <QHeaderView>
|
|
#include <QHostAddress>
|
|
|
|
enum { kVlanId, kVlanCount, kVlanStep, kVlanCfi, kVlanPrio, kVlanTpid,
|
|
kVlanColumns };
|
|
static QStringList vlanTableColumnHeaders = QStringList()
|
|
<< "Vlan Id" << "Count" << "Step" << "CFI/DE" << "Prio" << "TPID";
|
|
|
|
enum { kIpNone, kIp4, kIp6, kIpDual };
|
|
static QStringList ipStackItems = QStringList()
|
|
<< "None" << "IPv4" << "IPv6" << "Dual";
|
|
|
|
inline UInt128 UINT128(OstEmul::Ip6Address x)
|
|
{
|
|
return UInt128(x.hi(), x.lo());
|
|
}
|
|
|
|
inline OstEmul::Ip6Address IP6ADDR(UInt128 x)
|
|
{
|
|
OstEmul::Ip6Address ip;
|
|
|
|
ip.set_hi(x.hi64());
|
|
ip.set_lo(x.lo64());
|
|
return ip;
|
|
}
|
|
|
|
DeviceGroupDialog::DeviceGroupDialog(
|
|
Port *port,
|
|
int deviceGroupIndex,
|
|
QWidget *parent,
|
|
Qt::WindowFlags flags)
|
|
: QDialog(parent, flags), port_(port), index_(deviceGroupIndex)
|
|
{
|
|
// Setup the Dialog
|
|
setupUi(this);
|
|
|
|
vlanTagCount->setRange(0, kMaxVlanTags);
|
|
|
|
// Populate the Vlan Table with placeholders - we do this so that
|
|
// user entered values are retained during the lifetime of the dialog
|
|
// even if user is playing around with number of vlan tags
|
|
vlans->setRowCount(kMaxVlanTags);
|
|
vlans->setColumnCount(kVlanColumns);
|
|
vlans->setHorizontalHeaderLabels(vlanTableColumnHeaders);
|
|
for (int i = 0; i < kMaxVlanTags; i++) {
|
|
// Use same default values as defined in .proto
|
|
vlans->setItem(i, kVlanId,
|
|
new QTableWidgetItem(QString::number(100*(i+1))));
|
|
vlans->setItem(i, kVlanCount,
|
|
new QTableWidgetItem(QString::number(1)));
|
|
vlans->setItem(i, kVlanStep,
|
|
new QTableWidgetItem(QString::number(1)));
|
|
vlans->setItem(i, kVlanCfi,
|
|
new QTableWidgetItem(QString::number(0)));
|
|
vlans->setItem(i, kVlanPrio,
|
|
new QTableWidgetItem(QString::number(0)));
|
|
vlans->setItem(i, kVlanTpid,
|
|
new QTableWidgetItem(QString("0x8100")));
|
|
}
|
|
|
|
// Set SpinBoxDelegate for all columns except TPID
|
|
SpinBoxDelegate *spd = new SpinBoxDelegate(this);
|
|
spd->setColumnRange(kVlanId, 0, 4095);
|
|
spd->setColumnRange(kVlanStep, 0, 4095);
|
|
spd->setColumnRange(kVlanCfi, 0, 1);
|
|
spd->setColumnRange(kVlanPrio, 0, 7);
|
|
for (int i = 0; i < kVlanColumns; i++) {
|
|
if (i != kVlanTpid)
|
|
vlans->setItemDelegateForColumn(i, spd);
|
|
}
|
|
|
|
vlans->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
|
|
vlans->resizeRowsToContents();
|
|
|
|
// Set vlan tag count *after* adding all items, so connected slots
|
|
// can access the items
|
|
vlanTagCount->setValue(kMaxVlanTags);
|
|
|
|
ipStack->insertItems(0, ipStackItems);
|
|
|
|
// TODO: DeviceGroup Traversal; hide buttons for now
|
|
// NOTE for implementation: Use a QHash<int, deviceGroup*>
|
|
// to store modified values while traversing; in accept()
|
|
// update port->deviceGroups[] from the QHash
|
|
prev->setHidden(true);
|
|
next->setHidden(true);
|
|
|
|
// TODO: Preview devices expanded from deviceGroup configuration
|
|
// for user convenience
|
|
|
|
// setup dialog to auto-resize as widgets are hidden or shown
|
|
layout()->setSizeConstraint(QLayout::SetFixedSize);
|
|
|
|
connect(devicePerVlanCount, SIGNAL(valueChanged(const QString&)),
|
|
this, SLOT(updateTotalDeviceCount()));
|
|
|
|
connect(ip4Address, SIGNAL(textEdited(const QString&)),
|
|
this, SLOT(updateIp4Gateway()));
|
|
connect(ip4PrefixLength, SIGNAL(valueChanged(const QString&)),
|
|
this, SLOT(updateIp4Gateway()));
|
|
connect(ip6Address, SIGNAL(textEdited(const QString&)),
|
|
this, SLOT(updateIp6Gateway()));
|
|
connect(ip6PrefixLength, SIGNAL(valueChanged(const QString&)),
|
|
this, SLOT(updateIp6Gateway()));
|
|
|
|
loadDeviceGroup();
|
|
}
|
|
|
|
void DeviceGroupDialog::accept()
|
|
{
|
|
storeDeviceGroup();
|
|
QDialog::accept();
|
|
}
|
|
|
|
//
|
|
// Private Slots
|
|
//
|
|
void DeviceGroupDialog::on_vlanTagCount_valueChanged(int value)
|
|
{
|
|
Q_ASSERT((value >= 0) && (value <= kMaxVlanTags));
|
|
|
|
for (int row = 0; row < kMaxVlanTags; row++)
|
|
vlans->setRowHidden(row, row >= value);
|
|
|
|
vlans->setVisible(value > 0);
|
|
updateTotalVlanCount();
|
|
}
|
|
|
|
void DeviceGroupDialog::on_vlans_cellChanged(int row, int col)
|
|
{
|
|
if (col != kVlanCount)
|
|
return;
|
|
|
|
if (vlans->isRowHidden(row))
|
|
return;
|
|
|
|
updateTotalVlanCount();
|
|
}
|
|
|
|
void DeviceGroupDialog::on_ipStack_currentIndexChanged(int index)
|
|
{
|
|
switch (index) {
|
|
case kIpNone:
|
|
ip4->hide();
|
|
ip6->hide();
|
|
break;
|
|
case kIp4:
|
|
ip4->show();
|
|
ip6->hide();
|
|
break;
|
|
case kIp6:
|
|
ip4->hide();
|
|
ip6->show();
|
|
break;
|
|
case kIpDual:
|
|
ip4->show();
|
|
ip6->show();
|
|
break;
|
|
default:
|
|
Q_ASSERT(false); // Unreachable!
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DeviceGroupDialog::updateTotalVlanCount()
|
|
{
|
|
int count = vlanTagCount->value() ? 1 : 0;
|
|
for (int i = 0; i < vlanTagCount->value(); i++)
|
|
count *= vlans->item(i, kVlanCount)->text().toUInt();
|
|
vlanCount->setValue(count);
|
|
|
|
updateTotalDeviceCount();
|
|
}
|
|
|
|
void DeviceGroupDialog::updateTotalDeviceCount()
|
|
{
|
|
totalDeviceCount->setValue(qMax(vlanCount->value(), 1)
|
|
* devicePerVlanCount->value());
|
|
}
|
|
|
|
void DeviceGroupDialog::updateIp4Gateway()
|
|
{
|
|
quint32 net = ip4Address->value() & (~0 << (32 - ip4PrefixLength->value()));
|
|
ip4Gateway->setValue(net | 0x01);
|
|
}
|
|
|
|
void DeviceGroupDialog::updateIp6Gateway()
|
|
{
|
|
UInt128 net = ip6Address->value()
|
|
& (~UInt128(0, 0) << (128 - ip6PrefixLength->value()));
|
|
ip6Gateway->setValue(net | UInt128(0, 1));
|
|
}
|
|
|
|
void DeviceGroupDialog::loadDeviceGroup()
|
|
{
|
|
const OstProto::DeviceGroup *devGrp = port_->deviceGroupByIndex(index_);
|
|
int tagCount = 0;
|
|
// use 1-indexed id so that it matches the port id used in the
|
|
// RFC 4814 compliant mac addresses assigned by default to deviceGroups
|
|
// XXX: use deviceGroupId also as part of the id?
|
|
quint32 id = (port_->id()+1) & 0xff;
|
|
|
|
Q_ASSERT(devGrp);
|
|
|
|
name->setText(QString::fromStdString(devGrp->core().name()));
|
|
|
|
if (devGrp->has_encap() && devGrp->encap().HasExtension(OstEmul::vlan)) {
|
|
OstEmul::VlanEmulation vlan = devGrp->encap()
|
|
.GetExtension(OstEmul::vlan);
|
|
tagCount = vlan.stack_size();
|
|
for (int i = 0; i < tagCount; i++) {
|
|
OstEmul::VlanEmulation::Vlan v = vlan.stack(i);
|
|
vlans->item(i, kVlanPrio)->setText(
|
|
QString::number((v.vlan_tag() >> 13) & 0x7));
|
|
vlans->item(i, kVlanCfi)->setText(
|
|
QString::number((v.vlan_tag() >> 12) & 0x1));
|
|
vlans->item(i, kVlanId)->setText(
|
|
QString::number(v.vlan_tag() & 0x0fff));
|
|
vlans->item(i, kVlanCount)->setText(QString::number(v.count()));
|
|
vlans->item(i, kVlanStep)->setText(QString::number(v.step()));
|
|
vlans->item(i, kVlanTpid)->setText(QString("0x%1")
|
|
.arg(v.tpid(), 0, 16));
|
|
}
|
|
}
|
|
vlanTagCount->setValue(tagCount);
|
|
|
|
updateTotalVlanCount();
|
|
devicePerVlanCount->setValue(devGrp->device_count());
|
|
|
|
OstEmul::MacEmulation mac = devGrp->GetExtension(OstEmul::mac);
|
|
Q_ASSERT(mac.has_address());
|
|
macAddress->setValue(mac.address());
|
|
macStep->setValue(mac.step());
|
|
|
|
OstEmul::Ip4Emulation ip4 = devGrp->GetExtension(OstEmul::ip4);
|
|
// If address is not set, assign one from RFC 2544 space - 192.18.0.0/15
|
|
// Use port Id as the 3rd octet of the address
|
|
ip4Address->setValue(ip4.has_address() ?
|
|
ip4.address() : 0xc6120002 | (id << 8));
|
|
ip4PrefixLength->setValue(ip4.prefix_length());
|
|
ip4Step->setValue(ip4.has_step()? ip4.step() : 1);
|
|
ip4Gateway->setValue(ip4.has_default_gateway() ?
|
|
ip4.default_gateway() : 0xc6120001 | (id << 8));
|
|
|
|
OstEmul::Ip6Emulation ip6 = devGrp->GetExtension(OstEmul::ip6);
|
|
// If address is not set, assign one from RFC 5180 space 2001:0200::/64
|
|
// Use port Id as the 3rd hextet of the address
|
|
ip6Address->setValue(ip6.has_address() ?
|
|
UINT128(ip6.address()) :
|
|
UInt128((0x200102000000ULL | id) << 16, 2));
|
|
ip6PrefixLength->setValue(ip6.prefix_length());
|
|
ip6Step->setValue(ip6.has_step() ? UINT128(ip6.step()) : UInt128(0, 1));
|
|
ip6Gateway->setValue(ip6.has_default_gateway() ?
|
|
UINT128(ip6.default_gateway()) :
|
|
UInt128((0x200102000000ULL | id) << 16, 1));
|
|
|
|
int stk = kIpNone;
|
|
if (devGrp->HasExtension(OstEmul::ip4))
|
|
if (devGrp->HasExtension(OstEmul::ip6))
|
|
stk = kIpDual;
|
|
else
|
|
stk = kIp4;
|
|
else if (devGrp->HasExtension(OstEmul::ip6))
|
|
stk = kIp6;
|
|
ipStack->setCurrentIndex(stk);
|
|
}
|
|
|
|
void DeviceGroupDialog::storeDeviceGroup()
|
|
{
|
|
OstProto::DeviceGroup *devGrp = port_->mutableDeviceGroupByIndex(index_);
|
|
int tagCount = vlanTagCount->value();
|
|
|
|
Q_ASSERT(devGrp);
|
|
|
|
devGrp->mutable_core()->set_name(name->text().toStdString());
|
|
|
|
OstEmul::VlanEmulation *vlan = devGrp->mutable_encap()
|
|
->MutableExtension(OstEmul::vlan);
|
|
vlan->clear_stack();
|
|
for (int i = 0; i < tagCount; i++) {
|
|
OstEmul::VlanEmulation::Vlan *v = vlan->add_stack();
|
|
v->set_vlan_tag(
|
|
vlans->item(i, kVlanPrio)->text().toUInt() << 13
|
|
| vlans->item(i, kVlanCfi)->text().toUInt() << 12
|
|
| vlans->item(i, kVlanId)->text().toUInt());
|
|
v->set_count(vlans->item(i, kVlanCount)->text().toUInt());
|
|
v->set_step(vlans->item(i, kVlanStep)->text().toUInt());
|
|
v->set_tpid(vlans->item(i, kVlanTpid)->text().toUInt(NULL, 16));
|
|
}
|
|
|
|
if (!tagCount)
|
|
devGrp->clear_encap();
|
|
|
|
devGrp->set_device_count(devicePerVlanCount->value());
|
|
|
|
OstEmul::MacEmulation *mac = devGrp->MutableExtension(OstEmul::mac);
|
|
mac->set_address(macAddress->value());
|
|
mac->set_step(macStep->value());
|
|
|
|
if (ipStack->currentIndex() == kIp4
|
|
|| ipStack->currentIndex() == kIpDual) {
|
|
OstEmul::Ip4Emulation *ip4 = devGrp->MutableExtension(OstEmul::ip4);
|
|
ip4->set_address(ip4Address->value());
|
|
ip4->set_prefix_length(ip4PrefixLength->value());
|
|
ip4->set_default_gateway(ip4Gateway->value());
|
|
ip4->set_step(ip4Step->value());
|
|
|
|
if (ipStack->currentIndex() == kIp4)
|
|
devGrp->ClearExtension(OstEmul::ip6);
|
|
}
|
|
|
|
if (ipStack->currentIndex() == kIp6
|
|
|| ipStack->currentIndex() == kIpDual) {
|
|
OstEmul::Ip6Emulation *ip6 = devGrp->MutableExtension(OstEmul::ip6);
|
|
ip6->mutable_address()->CopyFrom(IP6ADDR(ip6Address->value()));
|
|
ip6->set_prefix_length(ip6PrefixLength->value());
|
|
ip6->mutable_step()->CopyFrom(IP6ADDR(ip6Step->value()));
|
|
ip6->mutable_default_gateway()->CopyFrom(IP6ADDR(ip6Gateway->value()));
|
|
|
|
if (ipStack->currentIndex() == kIp6)
|
|
devGrp->ClearExtension(OstEmul::ip4);
|
|
}
|
|
|
|
if (ipStack->currentIndex() == kIpNone) {
|
|
devGrp->ClearExtension(OstEmul::ip4);
|
|
devGrp->ClearExtension(OstEmul::ip6);
|
|
}
|
|
}
|