/* 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 */ #include "devicegroupdialog.h" #include "port.h" #include "spinboxdelegate.h" #include "emulproto.pb.h" #include "uint128.h" #include #include 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()->setSectionResizeMode(QHeaderView::Stretch); vlans->resizeRowsToContents(); // Set vlan tag count *after* adding all items, so connected slots // can access the items vlanTagCount->setValue(kMaxVlanTags); devicePerVlanCount->setRange(1, 0x7fffffff); ipStack->insertItems(0, ipStackItems); // TODO: DeviceGroup Traversal; hide buttons for now // NOTE for implementation: Use a QHash // 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); } }