/* 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 "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); #if 0 // FIXME: not working qDebug("vlan def size: %d", vlans->verticalHeader()->defaultSectionSize()); qDebug("vlan min size: %d", vlans->verticalHeader()->minimumSectionSize()); vlans->verticalHeader()->setDefaultSectionSize( vlans->verticalHeader()->minimumSectionSize()); qDebug("vlan def size: %d", vlans->verticalHeader()->defaultSectionSize()); qDebug("vlan min size: %d", vlans->verticalHeader()->minimumSectionSize()); #endif // 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 // TODO: use spinbox delegate with rangecheck for validation vlans->setRowCount(kMaxVlanTags); vlans->setColumnCount(kVlanColumns); vlans->setHorizontalHeaderLabels(vlanTableColumnHeaders); for (int i = 0; i < kMaxVlanTags; i++) { vlans->setItem(i, kVlanId, new QTableWidgetItem(QString::number(100*(i+1)))); vlans->setItem(i, kVlanCount, new QTableWidgetItem(QString::number(10))); 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::number(0x8100, 16))); } // Set vlan tag count *after* adding items so connected slots // can access the items vlanTagCount->setValue(kMaxVlanTags); ipStack->insertItems(0, ipStackItems); 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->setText(QString::number(count)); updateTotalDeviceCount(); } void DeviceGroupDialog::updateTotalDeviceCount() { totalDeviceCount->setText(QString::number( qMax(vlanCount->text().toInt(), 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() { OstProto::DeviceGroup *devGrp = port_->deviceGroupByIndex(index_); int tagCount = 0; 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::number(v.tpid(), 16)); } } vlanTagCount->setValue(tagCount); updateTotalVlanCount(); devicePerVlanCount->setValue(devGrp->device_count()); OstEmul::MacEmulation mac = devGrp->GetExtension(OstEmul::mac); if (!mac.has_address()) { // Mac address as per RFC 4814 Sec 4.2 // (RR & 0xFC):PP:PP:RR:RR:RR // where RR is a random number, PP:PP is 1-indexed port index // NOTE: although qrand() return type is a int, the max value // is RAND_MAX (stdlib.h) which is often 16-bit only, so we // use two random numbers quint32 r1 = qrand(), r2 = qrand(); quint64 mac; mac = quint64(r1 & 0xfc00) << 32 | quint64(port_->id() + 1) << 24 | quint64((r1 & 0xff) << 16 | (r2 & 0xffff)); macAddress->setValue(mac); } else macAddress->setValue(mac.address()); macStep->setValue(mac.step()); OstEmul::Ip4Emulation ip4 = devGrp->GetExtension(OstEmul::ip4); ip4Address->setValue(ip4.address()); ip4PrefixLength->setValue(ip4.prefix_length()); ip4Step->setValue(ip4.step()); ip4Gateway->setValue(ip4.default_gateway()); OstEmul::Ip6Emulation ip6 = devGrp->GetExtension(OstEmul::ip6); // ip6 fields don't have default values defined in the .proto // because protobuf doesn't allow different default values for // embedded message fields, so assign them here // Use address 2001:0200::/64 from the RFC 5180 range ip6Address->setValue(ip6.has_address() ? UINT128(ip6.address()) : UInt128(0x20010200ULL << 32, 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(0x20010200ULL << 32, 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_->deviceGroupByIndex(index_); int tagCount = 0; Q_ASSERT(devGrp); devGrp->mutable_core()->set_name(name->text().toStdString()); OstEmul::VlanEmulation *vlan = devGrp->mutable_encap() ->MutableExtension(OstEmul::vlan); vlan->clear_stack(); tagCount = vlanTagCount->value(); 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)); } 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); } }