diff --git a/client/dumpview.cpp b/client/dumpview.cpp new file mode 100644 index 0000000..a238e95 --- /dev/null +++ b/client/dumpview.cpp @@ -0,0 +1,175 @@ +#include "dumpview.h" + +DumpView::DumpView(QWidget *parent) +{ + int w, h; + + data.resize(73); + + // NOTE: Monospaced fonts only !!!!!!!!!!! + setFont(QFont("Courier")); + w = fontMetrics().width('X'); + h = fontMetrics().height(); + + mLineHeight = h; + mCharWidth = w; + + mSelectedRow = mSelectedCol = -1; + + // calculate width for offset column and the whitespace that follows it + mOffsetPaneTopRect = QRect(0, 0, w*4, h); + mDumpPaneTopRect = QRect(mOffsetPaneTopRect.right()+w*3, 0, + w*((8*3-1)+2+(8*3-1)), h); + mAsciiPaneTopRect = QRect(mDumpPaneTopRect.right()+w*3, 0, + w*(8+1+8), h); + qDebug("DumpView::DumpView"); +} + +#if 0 +QSize DumpView::sizeHint() const +{ +} +#endif + +void DumpView::mousePressEvent(QMouseEvent *event) +{ + int x = event->x(); + int row, col; + + if (x > mAsciiPaneTopRect.left()) + { + col = (x - mAsciiPaneTopRect.left()) / mCharWidth; + if (col == 8) // don't select whitespace + goto _exit; + else if (col > 8) // adjust for whitespace + col--; + } + else if (x > mDumpPaneTopRect.left()) + { + col = (x - mDumpPaneTopRect.left()) / (mCharWidth*3); + } + row = event->y()/mLineHeight; + + if ((col < 16) && (row < ((data.size()+16)/16))) + { + mSelectedRow = row; + mSelectedCol = col; + } + else + goto _exit; + + // last row check col + if ((row == (((data.size()+16)/16) - 1)) && (col >= (data.size() % 16))) + goto _exit; + + qDebug("dumpview::selection(%d, %d)", mSelectedRow, mSelectedCol); + update(); + return; + +_exit: + // Clear existing selection + mSelectedRow = -1; + update(); +} + +void DumpView::paintEvent(QPaintEvent* event) +{ + QStylePainter painter(this); + QRect offsetRect = mOffsetPaneTopRect; + QRect dumpRect = mDumpPaneTopRect; + QRect asciiRect = mAsciiPaneTopRect; + QPalette pal = palette(); + QByteArray ba; + + //qDebug("dumpview::paintEvent"); + + // FIXME(LOW): unable to set the self widget's font in constructor + painter.setFont(QFont("Courier")); + + // set a white background + painter.fillRect(rect(), QBrush(QColor(Qt::white))); + + // display the offset, dump and ascii panes 8 + 8 bytes on a line + for (int i = 0; i < data.size(); i+=16) + { + QString dumpStr, asciiStr; + + ba = data.mid(i, 16); + + // display offset + painter.drawItemText(offsetRect, Qt::AlignLeft | Qt::AlignTop, pal, + true, QString("%1").arg(i, 4, 16, QChar('0')), QPalette::WindowText); + // construct the dumpStr and asciiStr + for (int j = i; (j < (i+16)) && (j < data.size()); j++) + { + unsigned char c = data.at(j); + + // extra space after 8 bytes + if (((j+8) % 16) == 0) + { + dumpStr.append(" "); + asciiStr.append(" "); + } + + dumpStr.append(QString("%1").arg((uint)c, 2, 16, QChar('0')). + toUpper()).append(" "); + + if (isPrintable(c)) + asciiStr.append(QChar(c)); + else + asciiStr.append(QChar('.')); + } + + // display dump + painter.drawItemText(dumpRect, Qt::AlignLeft | Qt::AlignTop, pal, + true, dumpStr, QPalette::WindowText); + + // display ascii + painter.drawItemText(asciiRect, Qt::AlignLeft | Qt::AlignTop, pal, + true, asciiStr, QPalette::WindowText); + + // overpaint selection (if any) + if ((i/16) == mSelectedRow) + { + QRect r; + unsigned char c = data.at(mSelectedRow*16+mSelectedCol); + QString selectedAsciiStr, selectedDumpStr; + + qDebug("dumpview::paintEvent - Highlighted"); + + selectedDumpStr.append(QString("%1").arg((uint) c, 2, 16, QChar('0')).toUpper()); + + if (isPrintable(c)) + selectedAsciiStr.append(QChar(c)); + else + selectedAsciiStr.append(QChar('.')); + + // display dump + r = dumpRect; + if (mSelectedCol < 8) + r.translate(mCharWidth*(mSelectedCol*3), 0); + else + r.translate(mCharWidth*(mSelectedCol*3+1), 0); + r.setWidth(mCharWidth*2); + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedDumpStr, QPalette::HighlightedText); + + // display ascii + r = asciiRect; + if (mSelectedCol < 8) + r.translate(mCharWidth*(mSelectedCol), 0); + else + r.translate(mCharWidth*(mSelectedCol+1), 0); + r.setWidth(mCharWidth); + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedAsciiStr, QPalette::HighlightedText); + } + + // move the rects down + offsetRect.translate(0, mLineHeight); + dumpRect.translate(0, mLineHeight); + asciiRect.translate(0, mLineHeight); + } +} diff --git a/client/dumpview.h b/client/dumpview.h new file mode 100644 index 0000000..4578a91 --- /dev/null +++ b/client/dumpview.h @@ -0,0 +1,34 @@ +#include // FIXME: High + +class DumpView: public QWidget // QAbstractItemView // FIXME +{ +public: + DumpView(QWidget *parent=0); + // bool setBase(uint base); // valid values: 16, 8, 10, 2 etc. + // void hideAsciiPane(void); + // void showAsciiPane(void); + // void hideOffsetPane(void); + // void showOffsetPane(void); + + + //QSize sizeHint() const; + +protected: + void mousePressEvent(QMouseEvent *event); + //void mouseMoveEvent(QMouseEvent *event); + void paintEvent(QPaintEvent *event); + +private: + QString toAscii(QByteArray ba); + bool inline isPrintable(char c) + {if ((c > 48) && (c < 126)) return true; else return false; } + +private: + QRect mOffsetPaneTopRect; + QRect mDumpPaneTopRect; + QRect mAsciiPaneTopRect; + QByteArray data; + int mSelectedRow, mSelectedCol; + int mLineHeight; + int mCharWidth; +}; diff --git a/client/icons/portstats_filter.png b/client/icons/portstats_filter.png new file mode 100644 index 0000000..4606087 Binary files /dev/null and b/client/icons/portstats_filter.png differ diff --git a/client/ostinato.pro b/client/ostinato.pro index 3d27500..aceb998 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -3,14 +3,17 @@ CONFIG += qt debug QT += network RESOURCES += ostinato.qrc HEADERS += \ + dumpview.h \ hexlineedit.h \ mainwindow.h \ mythread.h \ + packetmodel.h \ port.h \ portgroup.h \ portgrouplist.h \ portmodel.h \ portstatsmodel.h \ + portstatsfilterdialog.h \ portstatswindow.h \ portswindow.h \ streamconfigdialog.h \ @@ -18,21 +21,25 @@ HEADERS += \ FORMS += \ mainwindow.ui \ + portstatsfilter.ui \ portstatswindow.ui \ portswindow.ui \ streamconfigdialog.ui SOURCES += \ + dumpview.cpp \ stream.cpp \ hexlineedit.cpp \ main.cpp \ mainwindow.cpp \ mythread.cpp \ + packetmodel.cpp \ port.cpp \ portgroup.cpp \ portgrouplist.cpp \ portmodel.cpp \ portstatsmodel.cpp \ + portstatsfilterdialog.cpp \ portstatswindow.cpp \ portswindow.cpp \ streamconfigdialog.cpp \ diff --git a/client/ostinato.qrc b/client/ostinato.qrc index 59f4930..09fdd4a 100644 --- a/client/ostinato.qrc +++ b/client/ostinato.qrc @@ -12,6 +12,7 @@ icons/portgroup_connect.png icons/portgroup_delete.png icons/portgroup_disconnect.png + icons/portstats_filter.png icons/sound_mute.png icons/sound_none.png icons/stream_add.png diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp new file mode 100644 index 0000000..4cbc92c --- /dev/null +++ b/client/packetmodel.cpp @@ -0,0 +1,440 @@ +#include "packetmodel.h" + + +PacketModel::PacketModel(Stream *pStream, QObject *parent) +{ + mpStream = pStream; +} + +int PacketModel::rowCount(const QModelIndex &parent) const +{ + // Parent - Invisible Root. + // Children - Top Level Items + if (!parent.isValid()) + { + int v = 0; + + if (mpStream->l2.eth.vlanMask & VM_SVLAN_TAGGED) + v++; + if (mpStream->l2.eth.vlanMask & VM_CVLAN_TAGGED) + v++; + + if (mpStream->proto.protoMask & PM_L3_PROTO_NONE) + return v+2; // L2, Data + if (mpStream->proto.protoMask & PM_L4_PROTO_NONE) + return v+3; // L2, L3, Data + else + return v+4; // L2, L3, L4, Data + } + + // Parent - Top Level Item (L2) + // Children(count) - Second Level Items (L2 fields) + if (isIndexL2Container(parent)) + { + switch(mpStream->proto.ft) + { + case Stream::e_ft_none: + return 2; // DstMac, SrcMac + break; + + case Stream::e_ft_eth_2: + case Stream::e_ft_802_3_raw: + return 3; // DstMac, SrcMac, Type/Len + break; + + case Stream::e_ft_802_3_llc: + case Stream::e_ft_snap: + return 5; // DstMac, SrcMac, Type, DSAP, SSAP, CTL, OUI, Type + break; + + default: + qDebug("%s: Unsupported frametype", __FUNCTION__); + return -1; + } + } + + // Parent - Top Level Item (SVLAN) + // Children(count) - Second Level Items (SVLAN fields) + if (isIndexSvlanContainer(parent)) + { + return 4; // TPID, PCP, DE, VlanId + } + + // Parent - Top Level Item (CVLAN) + // Children(count) - Second Level Items (CVLAN fields) + if (isIndexCvlanContainer(parent)) + { + return 4; // TPID, Prio, CFI, VlanId + } + + // Parent - Top Level Item (L3) + // Children(count) - Second Level Items (L3 fields) + if (isIndexL3Container(parent)) + { + // L3 cannot be "None" + Q_ASSERT(mpStream->proto.protoMask & PM_L3_PROTO_NONE); + + switch(mpStream->proto.etherType) + { + case ETH_TYP_IP: + return 12; // Ver, HdrLen, TOS, TotLen, Id, Flags, + // FragOfs, TTL, Proto, Cksum, SrcIp, DstIp + break; + case ETH_TYP_ARP: + return 0; // TODO(LOW) + break; + default: + qDebug("%s: Unsupported ethtype", __FUNCTION__); + return -1; + } + } + + // Parent - Top Level Item (L4) + // Children(count) - Second Level Items (L4 fields) + if (isIndexL4Container(parent)) + { + // L4 cannot be "None" + Q_ASSERT(mpStream->proto.protoMask & PM_L4_PROTO_NONE); + + switch(mpStream->proto.ipProto) + { + case IP_PROTO_TCP: + return 10; // SrcPort, DstPort, SeqNum, AckNum, HdrLen, + // Rsvd, Flags, Window, Cksum, UrgPtr, + break; + case IP_PROTO_UDP: + return 4; // SrcPort, DstPort, TotLen, Cksum + break; + case IP_PROTO_ICMP: + case IP_PROTO_IGMP: + return 0; // TODO(LOW) + break; + default: + qDebug("%s: Unsupported ethtype", __FUNCTION__); + return -1; + } + } + + // Parent - Second Level Item (L2 field) + // Children(count) - Third Level Items (L2 subfield) + if (isIndexL2Field(parent)) + { + return 0; // No subfields for any L2 field + } + + // Parent - Second Level Item (L3 field) + // Children(count) - Third Level Items (L3 subfield) + if (isIndexL3Field(parent)) + { + if (isIndexIpField(parent)) + return 0; // TODO (MED) + if (isIndexArpField(parent)) + return 0; // TODO (LOW) + + qDebug("%s: Unknown L3 Field", __FUNCTION__); + return 0; // catch all + } + + // Parent - Second Level Item (L4 field) + // Children(count) - Third Level Items (L4 subfield) + if (isIndexL4Field(parent)) + { + if (isIndexTcpField(parent)) + return 0; // TODO (MED) + if (isIndexUdpField(parent)) + return 0; // No subfields for any UDP fields + if (isIndexIcmpField(parent)) + return 0; // TODO (LOW) + if (isIndexIgmpField(parent)) + return 0; // TODO (LOW) + + qDebug("%s: Unknown L4 Field", __FUNCTION__); + return 0; // catch all + } + + //qDebug("%s: Catch all - need to investigate", __FUNCTION__); + return 0; // catch all +} + +int PacketModel::columnCount(const QModelIndex &parent) const +{ + return 1; +} + +QModelIndex PacketModel::index(int row, int col, const QModelIndex &parent) const +{ + QModelIndex index; + IndexId id, parentId; + uint vlanMask = mpStream->l2.eth.vlanMask; + + if (!hasIndex(row, col, parent)) + goto _exit; + + parentId.w = parent.internalId(); + + // Parent - Invisible Root + // Requested child - First/Top Level Item + if (parentId.ws.b2 == 0xFF) + { + Q_ASSERT(!parent.isValid()); + + if (mpStream->l2.eth.vlanMask & VM_UNTAGGED) + id.ws.b1 = row+2; // Only L2, L3, L4 + else if (VM_SINGLE_TAGGED(vlanMask)) + { + switch (row) + { + case 0: id.ws.b1 = 2; break; // L2 + case 1: id.ws.b1 = (vlanMask & VM_SVLAN_TAGGED)?0x88:0x81; break; + case 2: id.ws.b1 = 3; break; // L3 + case 3: id.ws.b1 = 4; break; // L4 + case 4: id.ws.b1 = 0; break; // Data + default: qWarning("%s: Unexpected row (%d)", __FUNCTION__, row); + } + } + else if (VM_DOUBLE_TAGGED(vlanMask)) + { + switch (row) + { + case 0: id.ws.b1 = 2; break; // L2 + case 1: id.ws.b1 = 0x88; break; // SVLAN + case 2: id.ws.b1 = 0x81; break; // CVLAN + case 3: id.ws.b1 = 3; break; // L3 + case 4: id.ws.b1 = 4; break; // L4 + case 5: id.ws.b1 = 0; break; // Data + default: qWarning("%s: Unexpected row (%d)", __FUNCTION__, row); + } + } + id.ws.b2 = 0xFF; + index = createIndex(row, col, id.w); + goto _exit; + } + + // Parent - First Level Item + // Requested child - Second Level Item + if (parentId.ws.b3 == 0xFF) + { + Q_ASSERT(parentId.ws.b1 != 0xFF); + Q_ASSERT(parentId.ws.b2 != 0xFF); + + id.ws.b1 = parentId.ws.b1; + id.ws.b2 = 0; // TODO(MED): Set Field Id for subfields + index = createIndex(row, col, id.w); + goto _exit; + } + + // Parent - Second Level Item (Field) + // Requested child - Third Level Item (Subfield) + // TODO(MED): Support subfields + // Till then we return an invalid index + +_exit: + return index; +} + +QModelIndex PacketModel::parent(const QModelIndex &index) const +{ + IndexId id, parentId; + + id.w = index.internalId(); + parentId = id; + + // 1st/Top Level Item - Protocol + // Requested Parent => Invisible Root + if (id.ws.b2 == 0xFF) + return QModelIndex(); + + // Second Level Item - Field + // Requested Parent => 1st Level Item (Protocol) + if (id.ws.b3 == 0xFF) + { + uint vlanMask = mpStream->l2.eth.vlanMask; + int row = -1; + + parentId.ws.b2 = 0xFF; + + if (vlanMask & VM_UNTAGGED) + { + row = parentId.ws.b1 - 2; + } + else if (VM_SINGLE_TAGGED(vlanMask)) + { + switch (parentId.ws.b1) + { + case 2: row = 0; break; // L2 + case 0x88: + case 0x81: row = 1; break; // SVlan/CVlan + case 3: row = 2; break; // L3 + case 4: row = 3; break; // L4 + case 0: row = 4; break; // Data + default: qWarning("%s: Unexpected b1 (%d)", __FUNCTION__, parentId.ws.b1); + } + } + else if (VM_DOUBLE_TAGGED(vlanMask)) + { + switch (parentId.ws.b1) + { + case 2: row = 0; break; // L2 + case 0x88: row = 1; break; // Svlan + case 0x81: row = 2; break; // CVlan + case 3: row = 3; break; // L3 + case 4: row = 4; break; // L4 + case 0: row = 5; break; // Data + default: qWarning("%s: Unexpected b1 (%d)", __FUNCTION__, parentId.ws.b1); + } + } + else + qWarning("%s: Unhandled leg", __FUNCTION__); + + return createIndex(row, 0, parentId.w); + } + + // Third Level Item - Subfield + // Requested Parent => 2nd Level Item (Field) + // TODO(Med) + qWarning("%s: Unexpected leg", __FUNCTION__); + return QModelIndex(); +} + +QVariant PacketModel::data(const QModelIndex &index, int role) const +{ + IndexId id; + + id.w = index.internalId(); + + if (id.ws.b2 == 0xFF) + return QString("Protocol Header"); + else + return QString("Field: Value"); +} + + +/* +** --------------- Private Stuff ----------------- +*/ +typedef union +{ + quint32 w; + struct + { + quint8 b1; + quint8 b2; + quint8 b3; + quint8 b4; + } ws; +} IndexId; + +bool PacketModel::isIndexContainer(const QModelIndex& index, int level) const +{ + IndexId id; + + id.w = index.internalId(); + if ((id.ws.b1 == level) && (id.ws.b2 == 0xFF)) + return true; + else + return false; +} + +bool PacketModel::isIndexL2Container(const QModelIndex& index) const +{ + return isIndexContainer(index, 2); +} + +bool PacketModel::isIndexSvlanContainer(const QModelIndex& index) const +{ + return isIndexContainer(index, 0x88); +} + +bool PacketModel::isIndexCvlanContainer(const QModelIndex& index) const +{ + return isIndexContainer(index, 0x81); +} + +bool PacketModel::isIndexL3Container(const QModelIndex& index) const +{ + return isIndexContainer(index, 3); +} + +bool PacketModel::isIndexL4Container(const QModelIndex& index) const +{ + return isIndexContainer(index, 4); +} + +bool PacketModel::isIndexField(const QModelIndex& index, int level) const +{ + IndexId id; + + id.w = index.internalId(); + if ((id.ws.b1 == level) && (id.ws.b2 != 0xFF) && (id.ws.b3 == 0xFF)) + return true; + else + return false; +} + +bool PacketModel::isIndexL2Field(const QModelIndex& index) const +{ + return isIndexField(index, 2); +} + +bool PacketModel::isIndexL3Field(const QModelIndex& index) const +{ + return isIndexField(index, 3); +} + +bool PacketModel::isIndexL4Field(const QModelIndex& index) const +{ + return isIndexField(index, 4); +} + +bool PacketModel::isIndexIpField(const QModelIndex& index) const +{ + IndexId id; + + id.w = index.internalId(); + if ((id.ws.b1 == 3) && (id.ws.b2 == 1) && (id.ws.b3 == 0xFF)) + return true; + else + return false; +} + +bool PacketModel::isIndexArpField(const QModelIndex& index) const +{ + IndexId id; + + id.w = index.internalId(); + if ((id.ws.b1 == 3) && (id.ws.b2 == 2) && (id.ws.b3 == 0xFF)) + return true; + else + return false; +} + +bool PacketModel::isIndexL4ProtoField(const QModelIndex& index, int proto) const +{ + IndexId id; + + id.w = index.internalId(); + if ((id.ws.b1 == 4) && (id.ws.b2 == proto) && (id.ws.b3 == 0xFF)) + return true; + else + return false; +} + +bool PacketModel::isIndexTcpField(const QModelIndex& index) const +{ + return isIndexL4ProtoField(index, IP_PROTO_TCP); +} + +bool PacketModel::isIndexUdpField(const QModelIndex& index) const +{ + return isIndexL4ProtoField(index, IP_PROTO_UDP); +} + +bool PacketModel::isIndexIcmpField(const QModelIndex& index) const +{ + return isIndexL4ProtoField(index, IP_PROTO_ICMP); +} + +bool PacketModel::isIndexIgmpField(const QModelIndex& index) const +{ + return isIndexL4ProtoField(index, IP_PROTO_IGMP); +} diff --git a/client/packetmodel.h b/client/packetmodel.h new file mode 100644 index 0000000..1ecd0d2 --- /dev/null +++ b/client/packetmodel.h @@ -0,0 +1,52 @@ +#ifndef _PACKET_MODEL_H +#define _PACKET_MODEL_H + +#include +#include "stream.h" + +class PacketModel: public QAbstractItemModel +{ + +public: + PacketModel(Stream *pStream, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; + +private: + Stream *mpStream; + typedef union _IndexId + { + quint32 w; + struct + { + quint8 b1; // 1st Level + quint8 b2; // 2nd Level + quint8 b3; // 3rd Level + quint8 b4; // Reserved + } ws; + } IndexId; + + bool PacketModel::isIndexContainer(const QModelIndex& index, int level) const; + bool PacketModel::isIndexL2Container(const QModelIndex& index) const; + bool PacketModel::isIndexSvlanContainer(const QModelIndex& index) const; + bool PacketModel::isIndexCvlanContainer(const QModelIndex& index) const; + bool PacketModel::isIndexL3Container(const QModelIndex& index) const; + bool PacketModel::isIndexL4Container(const QModelIndex& index) const; + bool PacketModel::isIndexField(const QModelIndex& index, int level) const; + bool PacketModel::isIndexL2Field(const QModelIndex& index) const; + bool PacketModel::isIndexL3Field(const QModelIndex& index) const; + bool PacketModel::isIndexL4Field(const QModelIndex& index) const; + bool PacketModel::isIndexIpField(const QModelIndex& index) const; + bool PacketModel::isIndexArpField(const QModelIndex& index) const; + bool PacketModel::isIndexL4ProtoField(const QModelIndex& index, int proto) const; + bool PacketModel::isIndexTcpField(const QModelIndex& index) const; + bool PacketModel::isIndexUdpField(const QModelIndex& index) const; + bool PacketModel::isIndexIcmpField(const QModelIndex& index) const; + bool PacketModel::isIndexIgmpField(const QModelIndex& index) const; +}; +#endif + diff --git a/client/port.cpp b/client/port.cpp index 755e52e..f7122d3 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -18,7 +18,9 @@ void Port::insertDummyStreams() { mStreams.append(*(new Stream)); mStreams[0].setName(QString("%1:%2:0").arg(portGroupId()).arg(id())); +#if 1 mStreams.append(*(new Stream)); mStreams[1].setName(QString("%1:%2:1").arg(portGroupId()).arg(id())); +#endif } diff --git a/client/portgroup.cpp b/client/portgroup.cpp index b7f8eb3..e8a7dfe 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -100,9 +100,9 @@ void PortGroup::ProcessCapabilityInfo(const char *msg, qint32 size) goto _next; } - p = new Port(NTOHL(cap->port), mPortGroupId); - p->setName(cap->name); - p->setDescription(cap->desc); + p = new Port(NTOHL(cap->portId), mPortGroupId); + p->setName(cap->portName); + p->setDescription(cap->portDesc); p->insertDummyStreams(); // FIXME: only for testing qDebug("before port append\n"); mPorts.append(*p); @@ -137,7 +137,7 @@ void PortGroup::when_connected() pkt.ver = 1; pkt.resv1 = 0; pkt.resv2 = 0; - pkt.msgType = HTONS(e_MT_CapabilityReq); + pkt.msgType = HTONS(e_MT_GetCapability); pkt.msgLen = HTONS(8); mpSocket->write((char*) &pkt, sizeof(pkt)); diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui index 3dae66d..8220436 100644 --- a/client/portstatsfilter.ui +++ b/client/portstatsfilter.ui @@ -1,6 +1,6 @@ - Dialog - + PortStatsFilterDialog + 0 @@ -16,7 +16,11 @@ - + + + QAbstractItemView::ExtendedSelection + + @@ -34,14 +38,14 @@ - + > - + < @@ -63,7 +67,11 @@ - + + + QAbstractItemView::ExtendedSelection + + @@ -80,10 +88,10 @@ - lvAllPorts - tbFilterIn - tbFilterOut - lvFilteredPorts + lvUnselected + tbSelectIn + tbSelectOut + lvSelected buttonBox @@ -91,7 +99,7 @@ buttonBox accepted() - Dialog + PortStatsFilterDialog accept() @@ -107,7 +115,7 @@ buttonBox rejected() - Dialog + PortStatsFilterDialog reject() diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp index 8845dd2..af95303 100644 --- a/client/portstatsfilterdialog.cpp +++ b/client/portstatsfilterdialog.cpp @@ -1,9 +1,84 @@ #include "portstatsfilterdialog.h" -PortStatsFilterDialog::PortStatsFilterDialog(AbstractItemModel *allPortsModel, - QWidget *parent) +PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) { setupUi(this); - lvAllPorts->setModel(allPortsModel); + // TODO(MED): Use ExtendedSelection and use "selected" instead of + // "current" for selecting in/out + // TODO(MED): Ensure items are READ-ONLY not editable + // TODO(MED): Enable "double-click" on items + lvUnselected->setSelectionMode(QAbstractItemView::SingleSelection); + lvSelected->setSelectionMode(QAbstractItemView::SingleSelection); + + mUnselected.setSortRole(PositionRole); + + lvUnselected->setModel(&mUnselected); + lvSelected->setModel(&mSelected); } + +QList PortStatsFilterDialog::getItemList(bool* ok, + QAbstractItemModel *model, Qt::Orientation orientation, + QList initial) +{ + QList ret; + + uint count = (orientation == Qt::Vertical) ? + model->rowCount() : model->columnCount(); + + *ok = false; + + mUnselected.clear(); + mSelected.clear(); + + for (uint i = 0; i < count; i++) + { + QStandardItem *item; + + item = new QStandardItem(model->headerData(i, orientation).toString()); + item->setData(i, PositionRole); + + if (initial.contains(i)) + mSelected.appendRow(item); + else + mUnselected.appendRow(item); + } + + // No need to sort right now 'coz we have inserted items in order + + if (exec() == QDialog::Accepted) + { + uint count = mSelected.rowCount(); + for (uint i = 0; i < count; i++) + { + QModelIndex index = mSelected.index(i, 0, QModelIndex()); + QStandardItem *item = mSelected.itemFromIndex(index); + ret.append(item->data(PositionRole).toInt()); + } + *ok = true; + } + + return ret; +} + +void PortStatsFilterDialog::on_tbSelectIn_clicked() +{ + QStandardItem *item; + + item = mUnselected.takeItem(lvUnselected->currentIndex().row()); + if (mUnselected.removeRow(lvUnselected->currentIndex().row())) + mSelected.appendRow(item); +} + +void PortStatsFilterDialog::on_tbSelectOut_clicked() +{ + QStandardItem *item; + + item = mSelected.takeItem(lvSelected->currentIndex().row()); + if (mSelected.removeRow(lvSelected->currentIndex().row())) + { + mUnselected.appendRow(item); + mUnselected.sort(0); + } +} + diff --git a/client/portstatsfilterdialog.h b/client/portstatsfilterdialog.h index c180f34..e845e0d 100644 --- a/client/portstatsfilterdialog.h +++ b/client/portstatsfilterdialog.h @@ -3,7 +3,8 @@ #include #include -#include "ui_portstatsfilterdialog.h" +#include +#include "ui_portstatsfilter.h" #include "portgrouplist.h" class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog @@ -11,8 +12,21 @@ class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog Q_OBJECT public: - PortStatsFilterDialog(AbstractItemModel *allPortsModel, - QWidget *parent = 0); + PortStatsFilterDialog(QWidget *parent = 0); + QList getItemList(bool* ok, QAbstractItemModel *model, + Qt::Orientation orientation = Qt::Vertical, + QList initial = QList()); + +private: + enum ItemRole { + PositionRole = Qt::UserRole + 1 + }; + QStandardItemModel mUnselected; + QStandardItemModel mSelected; + +private slots: + void on_tbSelectIn_clicked(); + void on_tbSelectOut_clicked(); }; #endif diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp index 594209e..1339ebb 100644 --- a/client/portstatswindow.cpp +++ b/client/portstatswindow.cpp @@ -1,15 +1,37 @@ + #include "portstatswindow.h" #include "portstatsmodel.h" +#include "portstatsfilterdialog.h" + +#include "QHeaderView" //PortStatsWindow::PortStatsWindow(QWidget *parent) : QDialog (parent) PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) { setupUi(this); - tvPortStats->setModel(pgl->getPortStatsModel()); - + model = pgl->getPortStatsModel(); + tvPortStats->setModel(model); + tvPortStats->horizontalHeader()->setMovable(true); } PortStatsWindow::~PortStatsWindow() { } + +void PortStatsWindow::on_tbFilter_clicked() +{ + bool ok; + QList currentColumns, newColumns; + PortStatsFilterDialog dialog; + + for(int i = 0; i < model->columnCount(); i++) + if (!tvPortStats->isColumnHidden(i)) + currentColumns.append(i); + + newColumns = dialog.getItemList(&ok, model, Qt::Horizontal, currentColumns); + + if(ok) + for(int i = 0; i < model->columnCount(); i++) + tvPortStats->setColumnHidden(i, !newColumns.contains(i)); +} diff --git a/client/portstatswindow.h b/client/portstatswindow.h index 2cec09a..e5a1a97 100644 --- a/client/portstatswindow.h +++ b/client/portstatswindow.h @@ -15,6 +15,9 @@ public: ~PortStatsWindow(); private: QAbstractItemModel *model; + +private slots: + void on_tbFilter_clicked(); }; #endif diff --git a/client/portstatswindow.ui b/client/portstatswindow.ui index f3dff15..4396ff6 100644 --- a/client/portstatswindow.ui +++ b/client/portstatswindow.ui @@ -1,133 +1,192 @@ - - PortStatsWindow - - - - 0 - 0 - 502 - 415 - - - - Form - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - Start Transmit - - - Stop Transmit - - - Start Transmit - - - :/icons/control_play.png - - - - - - - Stop Transmit - - - Stop Transmit - - - Stop Trasmit - - - :/icons/control_stop.png - - - - - - - Clear - - - - - - - Clear All - - - - - - - Start Capture - - - :/icons/sound_none.png - - - - - - - Stop Capture - - - :/icons/sound_mute.png - - - - - - - View Capture - - - :/icons/magnifier.png - - - - - - - Qt::Vertical - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - + + PortStatsWindow + + + + 0 + 0 + 502 + 415 + + + + Form + + + + + + Clear All + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Clear All + + + Starts transmit on selected port(s) + + + Start Transmit + + + :/icons/control_play.png + + + + + + + Clear All + + + Stops transmit on selected port(s) + + + Stop Trasmit + + + :/icons/control_stop.png + + + + + + + Clear All + + + Clears statistics of the selected port(s) + + + Clear + + + + + + + Clear All + + + Clears statistics of all ports + + + Clear All + + + + + + + Clear All + + + Captures packets on the selected port(s) + + + Start Capture + + + :/icons/sound_none.png + + + + + + + Clear All + + + End capture on selecteed port(s) + + + Stop Capture + + + :/icons/sound_mute.png + + + + + + + Clear All + + + View captured packets on selected port(s) + + + View Capture + + + :/icons/magnifier.png + + + + + + + Clear All + + + Qt::Vertical + + + + + + + Clear All + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Clear All + + + Select which ports to view + + + Filter + + + :/icons/portstats_filter.png + + + + + + + + + + Clear All + + + + + + + + + + diff --git a/client/stream.h b/client/stream.h index c1f9d5f..4469065 100644 --- a/client/stream.h +++ b/client/stream.h @@ -6,11 +6,13 @@ class StreamConfigDialog; class StreamModel; +class PacketModel; class Stream { friend class StreamConfigDialog; friend class StreamModel; + friend class PacketModel; enum FrameType { e_ft_none, @@ -114,6 +116,13 @@ class Stream { #define VM_SVLAN_TAGGED 0x0100 #define VM_SVLAN_TPID_OVERRIDE 0x0200 +#define VM_SINGLE_TAGGED(mask) \ + ((mask & VM_CVLAN_TAGGED ) | (mask & VM_SVLAN_TAGGED)) +#define VM_DOUBLE_TAGGED(mask) \ + (mask & (VM_CVLAN_TAGGED | VM_SVLAN_TAGGED)) + + + quint16 ctpid; quint16 cvlanPrio : 3; quint16 cvlanCfi : 1; diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index f29592f..f51c49e 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -2,6 +2,9 @@ #include "streamconfigdialog.h" #include "stream.h" +// TODO(LOW): Remove +#include "modeltest.h" + StreamConfigDialog::StreamConfigDialog(QList *streamList, uint streamIndex, QWidget *parent) : QDialog (parent) { @@ -13,6 +16,12 @@ StreamConfigDialog::StreamConfigDialog(QList *streamList, mCurrentStreamIndex = streamIndex; LoadCurrentStream(); + mpPacketModel = new PacketModel(&((*mpStreamList)[mCurrentStreamIndex]), + this); + tvPacketTree->setModel(mpPacketModel); + mpPacketModelTester = new ModelTest(mpPacketModel); + tvPacketTree->header()->hide(); + qDebug("stream %p %d/%d loaded", mpStreamList, mCurrentStreamIndex, mpStreamList->size()); @@ -66,6 +75,8 @@ void StreamConfigDialog::setupUiExtra() StreamConfigDialog::~StreamConfigDialog() { + delete mpPacketModelTester; + delete mpPacketModel; } void StreamConfigDialog::on_cmbDstMacMode_currentIndexChanged(QString mode) diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index 95e7380..6ccf074 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -3,7 +3,9 @@ #include #include "ui_streamconfigdialog.h" -#include +#include "stream.h" +#include "packetmodel.h" +#include "modeltest.h" #define MAX_MAC_ITER_COUNT 256 #define MIN_PKT_LEN 64 @@ -26,6 +28,8 @@ public: private: QList *mpStreamList; uint mCurrentStreamIndex; + PacketModel *mpPacketModel; + ModelTest *mpPacketModelTester; void setupUiExtra(); void LoadCurrentStream(); diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 85f29e3..7e7efe1 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -33,9 +33,9 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - 0 + 2 - + Packet Config @@ -2023,66 +2023,18 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff Packet View - + Qt::Vertical - - - true - - - - New Column - - - - - Ethernet - - - - DstMac: 00:00:00:00:00:00 - - - - - SrcMac: 00:00:00:00:00:00 - - - - - EtherType: IP (0800) - - - - - - IP - - - - Version: 4 - - - - - Header Length: 20 - - - - - - - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;">0004 00 00 00 00 00 00</p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></p></body></html> + + + QAbstractItemView::SelectItems + @@ -2145,6 +2097,12 @@ p, li { white-space: pre-wrap; } QLineEdit
hexlineedit.h
+ + DumpView + QWidget +
dumpview.h
+ 1 +
twTopLevel @@ -2347,12 +2305,12 @@ p, li { white-space: pre-wrap; } setEnabled(bool) - 79 - 247 + 101 + 276 - 99 - 247 + 101 + 276
@@ -2363,12 +2321,12 @@ p, li { white-space: pre-wrap; } setEnabled(bool) - 79 - 247 + 101 + 276 - 99 - 247 + 101 + 276
@@ -2443,12 +2401,12 @@ p, li { white-space: pre-wrap; } setEnabled(bool) - 79 - 247 + 101 + 276 - 99 - 247 + 101 + 276 @@ -2459,44 +2417,44 @@ p, li { white-space: pre-wrap; } setEnabled(bool) - 79 - 247 + 101 + 276 + + + 101 + 276 + + + + + rbFtNone + toggled(bool) + lblDsap + setHidden(bool) + + + 57 + 218 + + + 58 + 218 + + + + + rbFtNone + toggled(bool) + leDsap + setHidden(bool) + + + 57 + 218 99 - 247 - - - - - rbFtNone - toggled(bool) - lblDsap - setHidden(bool) - - - 43 - 186 - - - 137 - 185 - - - - - rbFtNone - toggled(bool) - leDsap - setHidden(bool) - - - 43 - 186 - - - 178 - 185 + 218 @@ -2507,12 +2465,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 186 + 57 + 218 - 137 - 211 + 58 + 218 @@ -2523,12 +2481,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 186 + 57 + 218 - 178 - 211 + 99 + 218 @@ -2539,8 +2497,8 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 186 + 57 + 218 137 @@ -2555,12 +2513,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 186 + 57 + 218 - 178 - 237 + 99 + 218 @@ -2571,12 +2529,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 186 + 57 + 218 - 137 - 263 + 58 + 218 @@ -2587,12 +2545,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 186 + 57 + 218 - 178 - 263 + 99 + 218 @@ -2603,12 +2561,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 186 + 57 + 218 - 137 - 289 + 58 + 218 @@ -2619,12 +2577,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 186 + 57 + 218 - 178 - 289 + 99 + 218 @@ -2635,12 +2593,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 211 + 57 + 218 - 137 - 185 + 58 + 218 @@ -2651,12 +2609,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 211 + 57 + 218 - 178 - 185 + 99 + 218 @@ -2667,12 +2625,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 211 + 57 + 218 - 137 - 211 + 58 + 218 @@ -2683,12 +2641,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 211 + 57 + 218 - 178 - 211 + 99 + 218 @@ -2699,8 +2657,8 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 236 + 57 + 218 137 @@ -2715,12 +2673,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 236 + 57 + 218 - 178 - 237 + 99 + 218 @@ -2731,8 +2689,8 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 211 + 57 + 218 137 @@ -2747,12 +2705,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 211 + 57 + 218 - 178 - 237 + 99 + 218 @@ -2763,12 +2721,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 211 + 57 + 218 - 137 - 263 + 58 + 218 @@ -2779,12 +2737,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 211 + 57 + 218 - 178 - 263 + 99 + 218 @@ -2795,12 +2753,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 236 + 57 + 218 - 137 - 185 + 58 + 218 @@ -2811,12 +2769,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 236 + 57 + 218 - 178 - 185 + 99 + 218 @@ -2827,12 +2785,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 236 + 57 + 218 - 137 - 211 + 58 + 218 @@ -2843,12 +2801,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 236 + 57 + 218 - 178 - 211 + 99 + 218 @@ -2859,8 +2817,8 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 236 + 57 + 218 137 @@ -2875,12 +2833,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 236 + 57 + 218 - 178 - 237 + 99 + 218 @@ -2891,12 +2849,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 236 + 57 + 218 - 137 - 263 + 58 + 218 @@ -2907,12 +2865,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 236 + 57 + 218 - 178 - 263 + 99 + 218 @@ -2923,12 +2881,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 236 + 57 + 218 - 137 - 289 + 58 + 218 @@ -2939,12 +2897,12 @@ p, li { white-space: pre-wrap; } setHidden(bool) - 43 - 236 + 57 + 218 - 178 - 289 + 99 + 218 @@ -2987,12 +2945,12 @@ p, li { white-space: pre-wrap; } setDisabled(bool) - 43 - 286 + 57 + 218 - 178 - 185 + 99 + 218 @@ -3003,12 +2961,12 @@ p, li { white-space: pre-wrap; } setDisabled(bool) - 43 - 286 + 57 + 218 - 178 - 211 + 99 + 218 @@ -3019,12 +2977,12 @@ p, li { white-space: pre-wrap; } setDisabled(bool) - 43 - 286 + 57 + 218 - 178 - 237 + 99 + 218 @@ -3035,12 +2993,12 @@ p, li { white-space: pre-wrap; } setEnabled(bool) - 355 - 196 + 570 + 218 - 300 - 291 + 570 + 302 @@ -3051,12 +3009,12 @@ p, li { white-space: pre-wrap; } setEnabled(bool) - 355 - 196 + 570 + 218 - 372 - 291 + 570 + 302 @@ -3067,12 +3025,12 @@ p, li { white-space: pre-wrap; } setEnabled(bool) - 355 - 196 + 570 + 218 - 443 - 291 + 570 + 302 @@ -3083,12 +3041,12 @@ p, li { white-space: pre-wrap; } setEnabled(bool) - 355 - 196 + 570 + 218 - 372 - 267 + 570 + 302 @@ -3099,12 +3057,12 @@ p, li { white-space: pre-wrap; } setEnabled(bool) - 355 - 196 + 570 + 218 - 443 - 267 + 570 + 302 @@ -3115,12 +3073,12 @@ p, li { white-space: pre-wrap; } click() - 300 - 196 + 570 + 218 - 300 - 267 + 570 + 302 @@ -3131,12 +3089,12 @@ p, li { white-space: pre-wrap; } click() - 407 - 196 + 570 + 218 - 300 - 267 + 570 + 302 @@ -3147,12 +3105,12 @@ p, li { white-space: pre-wrap; } click() - 457 - 196 + 570 + 218 - 300 - 267 + 570 + 302 @@ -3163,12 +3121,12 @@ p, li { white-space: pre-wrap; } setEnabled(bool) - 334 - 147 + 224 + 207 - 411 - 177 + 224 + 209 @@ -3179,12 +3137,12 @@ p, li { white-space: pre-wrap; } setEnabled(bool) - 38 - 73 + 125 + 207 - 180 - 284 + 191 + 236 @@ -3195,12 +3153,12 @@ p, li { white-space: pre-wrap; } setEnabled(bool) - 46 - 98 + 125 + 207 - 162 - 313 + 173 + 236 @@ -3211,12 +3169,12 @@ p, li { white-space: pre-wrap; } setEnabled(bool) - 73 - 108 + 125 + 207 - 225 - 184 + 224 + 216 diff --git a/common/protocol.h b/common/protocol.h index f7c61dc..01f35f6 100644 --- a/common/protocol.h +++ b/common/protocol.h @@ -31,23 +31,139 @@ typedef struct { } tCommHdr; typedef enum { - e_MT_CapabilityReq=1, - e_MT_CapabilityInfo + e_MT_GetCapability=1, // C-->S + e_MT_CapabilityInfo, // C<--S + + e_MT_ChangePortConfig, // C-->S + e_MT_GetPortConfig, // C-->S + e_MT_PortInfo, // C<--S + + e_MT_StartTx, // C-->S + e_MT_StopTx, // C-->S + e_MT_StartCapture, // C-->S + e_MT_StopCapture, // C-->S + e_MT_GetCaptureBuffer, // C-->S + e_MT_CaptureBufferInfo, // C-->S + + e_MT_GetStats, // C-->S + e_MT_StatsInfo, // C<--S + e_MT_ClearStats, // C-->S + } eMsgType; typedef enum { - e_TT_PortCapability + e_TT_PortCapability=0x0000, + + e_TT_StreamOper = 0x0100, + e_TT_StreamName, + e_TT_StreamStatus, + e_TT_StreamFrameLength, + e_TT_StreamDataPattern, + e_TT_StreamHeaderData, } eTlvType; typedef struct { UINT16 tlvType; UINT16 tlvLen; - UINT32 port; - UINT32 speed; +} tTlv; + +typedef struct { + UINT16 tlvType; + UINT16 tlvLen; + UINT32 portId; + UINT32 portSpeed; #define TLV_MAX_PORT_NAME 64 #define TLV_MAX_PORT_DESC 64 - char name[TLV_MAX_PORT_NAME]; - char desc[TLV_MAX_PORT_DESC]; + char portName[TLV_MAX_PORT_NAME]; + char portDesc[TLV_MAX_PORT_DESC]; } tTlvPortCapability; +typedef struct { + UINT16 tlvType; + UINT16 tlvLen; + UINT32 portId; + UINT32 streamId; +} tTlvStream; + +typedef struct { + UINT16 tlvType; + UINT16 tlvLen; + UINT32 portId; + UINT32 streamId; + UINT16 rsvd; + UINT16 streamOper; +#define TLV_STREAM_OPER_INSERT_HEAD 0x0001 +#define TLV_STREAM_OPER_INSERT_TAIL 0x0002 +#define TLV_STREAM_OPER_INSERT_BEFORE 0x0003 +#define TLV_STREAM_OPER_DELETE 0x0010 + UINT32 StreamId; +} tTlvStreamOper; + +typedef struct { + UINT16 tlvType; + UINT16 tlvLen; + UINT32 portId; + UINT32 streamId; + char streamName[0]; +} tTlvStreamName; + +typedef struct { + UINT16 tlvType; + UINT16 tlvLen; + UINT32 portId; + UINT32 streamId; + UINT32 streamStatus; +#define TLV_STREAM_STATUS_DISABLED 0 +#define TLV_STREAM_STATUS_ENABLED 1 +} tTlvStreamStatus; + +typedef struct { + UINT16 tlvType; + UINT16 tlvLen; + UINT32 portId; + UINT32 streamId; + UINT16 frameLenMode; +#define TLV_STREAM_FRAME_LEN_MODE_FIXED 0x0000 +#define TLV_STREAM_FRAME_LEN_MODE_RANDOM 0x0001 +#define TLV_STREAM_FRAME_LEN_MODE_INCREMENT 0x0002 +#define TLV_STREAM_FRAME_LEN_MODE_DECREMENT 0x0003 + UINT16 frameLen; + UINT16 frameLenMin; + UINT16 frameLenMax; +} tTlvStreamFrameLength; + +typedef struct { + UINT16 tlvType; + UINT16 tlvLen; + UINT32 portId; + UINT32 streamId; + UINT16 dataPatternMode; +#define TLV_STREAM_DATA_PATTERN_MODE_FIXED 0x0000 +#define TLV_STREAM_DATA_PATTERN_MODE_RANDOM 0x0001 +#define TLV_STREAM_DATA_PATTERN_MODE_INCREMENT 0x0002 +#define TLV_STREAM_DATA_PATTERN_MODE_DECREMENT 0x0003 + UINT16 rsvd; + UINT32 dataPattern; +} tTlvStreamDataPattern; + +typedef struct { + UINT16 tlvType; + UINT16 tlvLen; + UINT32 portId; + UINT32 streamId; + UINT16 rsvd; + UINT16 headerLen; + UINT8 header[0]; +} tTlvStreamHeaderData; + +typedef union { + tTlvStream tlv; + tTlvStreamOper oper; + tTlvStreamName name; + tTlvStreamStatus status; + tTlvStreamFrameLength frameLen; + tTlvStreamDataPattern dataPattern; + tTlvStreamHeaderData headerData; +} uTlvStream; + #endif diff --git a/server/rxtx.cpp b/server/rxtx.cpp index 7c0be69..0a1bcdf 100644 --- a/server/rxtx.cpp +++ b/server/rxtx.cpp @@ -1,6 +1,8 @@ +#include "qtglobal" // FIXME: needed only for qdebug #include "rxtx.h" #include "../common/protocol.h" -#include "qtglobal" // FIXME: needed only for qdebug + + //#define LOG(...) drone->ui.teLOG->append(QString().sprintf( __VA_ARGS__)) //#define LOG(...) drone->LOG(QString().sprintf( __VA_ARGS__)) @@ -36,6 +38,8 @@ RxTx::RxTx(AbstractHost *host) { portInfo[i].portId = i; portInfo[i].dev = d; + portInfo[i].streamHead = NULL; + portInfo[i].streamTail = NULL; #if 1 LOG("%d. %s", i, d->name); if (d->description) @@ -67,13 +71,17 @@ RxTx::LOG(char* fmt, ...) RxTx::~RxTx() { + unsigned int i; + + for (i = 0; i < numPorts; i++) + DeleteAllStreams(i); pcap_freealldevs(alldevs); } void RxTx::ProcessMsg(const char* msg, int len) { tCommHdr *hdr; - // TODO: For now, assuming we'll get a complete msg + // TODO: For now assuming we'll get a complete msg // but need to fix this as this is a TCP stream hdr = (tCommHdr*) msg; @@ -87,9 +95,15 @@ void RxTx::ProcessMsg(const char* msg, int len) qDebug("msgType - %x: %x\n", hdr->msgType, NTOHS(hdr->msgType)); switch (NTOHS(hdr->msgType)) { - case e_MT_CapabilityReq: + case e_MT_GetCapability: SendCapabilityInfo(); break; + case e_MT_ChangePortConfig: + ProcessPortConfig(msg+sizeof(tCommHdr), len - sizeof(tCommHdr)); + break; + case e_MT_GetPortConfig: + SendPortInfo(); + break; default: LOG("Rcvd msg with unrecognized msgType %d\n", NTOHS(hdr->msgType)); @@ -114,20 +128,20 @@ void RxTx::SendCapabilityInfo(void) // TLV: Port Capability ((tTlvPortCapability*)(p))->tlvType = HTONS(e_TT_PortCapability); ((tTlvPortCapability*)(p))->tlvLen = HTONS(sizeof(tTlvPortCapability)); - ((tTlvPortCapability*)(p))->port = HTONL(portInfo[i].portId); - ((tTlvPortCapability*)(p))->speed = 0; // TODO + ((tTlvPortCapability*)(p))->portId = HTONL(portInfo[i].portId); + ((tTlvPortCapability*)(p))->portSpeed = 0; // TODO #if 0 strncpy(((tTlvPortCapability*)(p))->name, portInfo[i].dev->name, TLV_MAX_PORT_NAME); ((tTlvPortCapability*)(p))->name[TLV_MAX_PORT_NAME-1] = 0; #else - strcpy(((tTlvPortCapability*)(p))->name, "eth"); + strcpy(((tTlvPortCapability*)(p))->portName, "eth"); //strcat(((tTlvPortCapability*)(p))->name, itoa(portInfo[i].portId, NULL, 10)); - itoa(portInfo[i].portId, &(((tTlvPortCapability*)(p))->name[3]), 10); + itoa(portInfo[i].portId, &(((tTlvPortCapability*)(p))->portName[3]), 10); #endif - strncpy(((tTlvPortCapability*)(p))->desc, + strncpy(((tTlvPortCapability*)(p))->portDesc, portInfo[i].dev->description, TLV_MAX_PORT_DESC); - ((tTlvPortCapability*)(p))->desc[TLV_MAX_PORT_DESC -1] = 0; + ((tTlvPortCapability*)(p))->portDesc[TLV_MAX_PORT_DESC -1] = 0; p += sizeof(tTlvPortCapability); } msgLen = (p - msg); @@ -145,3 +159,350 @@ void RxTx::SendCapabilityInfo(void) host->Log(logStr); host->SendMsg(pktBuff, msgLen); } + +void RxTx::ProcessPortConfig(const char* msg, int len) +{ + // ASSUMPTION: msg points to start of first TLV + UINT8 *p = (UINT8*) msg; + uTlvStream u; + Stream *s; + + // Extract and process each TLV + while (len) + { + if (len < 12) + { + LOG("Length (%d) Error - not enough to fit a TLV", len); + goto _exit; + } + + u.tlv.tlvType = NTOHS(GET16(p)); + u.tlv.tlvLen = NTOHS(GET16(p+2)); + u.tlv.portId = NTOHL(GET32(p+4)); + u.tlv.streamId = NTOHL(GET32(p+8)); + + p += 12; + len -= 12; + + // Locate the correct node for processing + if (u.tlv.portId >= numPorts) + goto _next_tlv; + + s = GetStream(u.tlv.portId, u.tlv.streamId); + if ((s == NULL) && (u.tlv.tlvType!= e_TT_StreamOper)) + { + LOG("Unrecognized stream Id %d\n", u.tlv.streamId); + goto _next_tlv; + } + + switch(u.tlv.tlvType) + { + case e_TT_StreamOper: + u.oper.streamOper = NTOHS(GET16(p+2)); + switch (u.oper.streamOper) + { + case TLV_STREAM_OPER_DELETE: + if (!DeleteStream(u.tlv.portId, u.tlv.streamId)) + { + LOG("No Stream with id %d currently in list\n", + u.tlv.streamId); + goto _next_tlv; + } + break; + case TLV_STREAM_OPER_INSERT_HEAD: + s = new Stream; + s->id = u.tlv.streamId; + + InsertStreamAtHead(u.tlv.portId, s); + break; + case TLV_STREAM_OPER_INSERT_TAIL: + s = new Stream; + s->id = u.tlv.streamId; + + InsertStreamAtTail(u.tlv.portId, s); + break; + case TLV_STREAM_OPER_INSERT_BEFORE: + { + UINT32 nextStreamId; + + s = new Stream; + s->id = u.tlv.streamId; + + nextStreamId = NTOHS(GET32(p+4)); + + if (!InsertStreamBefore(u.tlv.portId, s, nextStreamId)) + { + LOG("List Empty or No stream with id %d " + "currently in list\n", nextStreamId); + goto _next_tlv; + } + break; + } + default: + LOG("Unrecognized Stream Oper %d\n", + u.oper.streamOper); + goto _next_tlv; + } + break; + case e_TT_StreamName: + strncpy(s->name, (char*) p, MAX_STREAM_NAME_SIZE); + break; + + case e_TT_StreamStatus: + u.status.streamStatus = NTOHL(GET32(p)); + if (u.status.streamStatus == TLV_STREAM_STATUS_DISABLED) + s->flags |= STREAM_FLAG_VALUE_STATUS_DISABLED; // FIXME + else if (u.status.streamStatus == TLV_STREAM_STATUS_ENABLED) + s->flags |= STREAM_FLAG_VALUE_STATUS_ENABLED; // FIXME + else + goto _next_tlv; + break; + + case e_TT_StreamFrameLength: + u.frameLen.frameLenMode = NTOHS(GET16(p)); + u.frameLen.frameLen = NTOHS(GET16(p+2)); + u.frameLen.frameLenMin = NTOHS(GET16(p+4)); + u.frameLen.frameLenMax = NTOHS(GET16(p+6)); + + s->pktLen = u.frameLen.frameLen; + + // FIXME: other frameLen params + break; + + case e_TT_StreamDataPattern: + u.dataPattern.dataPatternMode = NTOHS(GET16(p)); + u.dataPattern.dataPattern = NTOHS(GET32(p+4)); + + s->dataPattern = u.dataPattern.dataPattern; + + // FIXME: other dataPattern params + break; + + case e_TT_StreamHeaderData: + u.headerData.headerLen = NTOHS(GET16(p+2)); + + s->hdrLen = u.headerData.headerLen; + memcpy(s->pktHdr, p+4, u.headerData.headerLen); + break; + + default: + LOG("Unrecognizeed/Unexpected TLV %d\n", u.tlv.tlvType); + } + +_next_tlv: + p += u.tlv.tlvLen; + len -= u.tlv.tlvLen; + } + +_exit: + return; +} + +void RxTx::SendPortInfo(unsigned int port) +{ + // Assumption: port is valid + assert(port < numPorts); + +} + +/* +** --------------------- STREAM LIST OPERATIONS ------------------------- +*/ + +void RxTx::InsertStreamAtHead(unsigned int port, Stream *s) +{ + if (portInfo[port].streamHead == NULL) + { + // list empty - first entry being added + s->next = NULL; + portInfo[port].streamHead = portInfo[port].streamTail = s; + } + else + { + // at least one entry in list, so tail does not change + s->next = portInfo[port].streamHead; + portInfo[port].streamHead = s; + } +} + +void RxTx::InsertStreamAtTail(unsigned int port, Stream *s) +{ + s->next = NULL; + if (portInfo[port].streamHead == NULL) + { + // list empty - first entry being added + portInfo[port].streamHead = portInfo[port].streamTail = s; + } + else + { + // at least one entry in list, so head does not change + portInfo[port].streamTail->next = s; + portInfo[port].streamTail = s; + } +} + +bool RxTx::InsertStreamBefore(unsigned int port, Stream *s, + unsigned int nextStreamId) +{ + Stream *q, *r; + + // For an "Insert Before", list cannot be empty + if (portInfo[port].streamHead == NULL) + { + LOG("Cannot 'insert before' in an empty list"); + return false; + } + + // Traverse with 'r' and keep track of previous with 'q' + q = NULL; + r = portInfo[port].streamHead; + while (r != NULL) + { + if (r->id == nextStreamId) + { + if (r == portInfo[port].streamHead) + { + // Insert at Head + s->next = portInfo[port].streamHead; + portInfo[port].streamHead = s; + } + else if (r == portInfo[port].streamTail) + { + // Insert one before Tail + s->next = portInfo[port].streamTail; + q->next = s; + } + else + { + s->next = r; + q->next = s; + } + + break; + } + q = r; + r = r->next; + } + + if (r == NULL) + return false; + else + return true; +} + +bool RxTx::DeleteStream(unsigned int port, Stream *s) +{ + Stream *q, *r; + + // Traverse with 'r' and keep track of prev with 'q' + q = NULL; + r = portInfo[port].streamHead; + while (r != NULL) + { + if (r == s) + { + if (r == portInfo[port].streamHead) + { + if (portInfo[port].streamHead == portInfo[port].streamTail) + { + portInfo[port].streamHead = NULL; + portInfo[port].streamTail = NULL; + } + else + portInfo[port].streamHead = portInfo[port].streamHead->next; + } + else if (r == portInfo[port].streamTail) + { + q->next = NULL; + portInfo[port].streamTail = q; + } + else + { + q->next = r->next; + } + + delete r; + break; + } + q = r; + r = r->next; + } + + if (r == NULL) + return false; + else + return true; +} + +bool RxTx::DeleteStream(unsigned int port, unsigned int streamId) +{ + Stream *q, *r; + + // Traverse with 'r' and keep track of prev with 'q' + q = NULL; + r = portInfo[port].streamHead; + while (r != NULL) + { + if (r->id == streamId) + { + if (r == portInfo[port].streamHead) + { + if (portInfo[port].streamHead == portInfo[port].streamTail) + { + portInfo[port].streamHead = NULL; + portInfo[port].streamTail = NULL; + } + else + portInfo[port].streamHead = portInfo[port].streamHead->next; + } + else if (r == portInfo[port].streamTail) + { + q->next = NULL; + portInfo[port].streamTail = q; + } + else + { + q->next = r->next; + } + + delete r; + break; + } + q = r; + r = r->next; + } + + if (r == NULL) + return false; + else + return true; +} + +void RxTx::DeleteAllStreams(unsigned int port) +{ + Stream *r, *q; + + r = portInfo[port].streamHead; + while (r != NULL) + { + q = r; + r = r->next; + delete q; + } +} + +Stream* RxTx::GetStream(unsigned int port, unsigned int streamId) +{ + Stream *r; + + r = portInfo[port].streamHead; + while (r != NULL) + { + if (r->id == streamId) + return r; + r = r->next; + } + + return NULL; +} + diff --git a/server/rxtx.h b/server/rxtx.h index 97eb2c3..986b978 100644 --- a/server/rxtx.h +++ b/server/rxtx.h @@ -4,11 +4,41 @@ #include "pcap.h" #include "abstracthost.h" +#define GET16(x) (UINT16)( \ + (*((UINT8*)x+0) << 16 ) \ + | (*((UINT8*)x+1))) + +#define GET32(x) (UINT32)( \ + (*((UINT8*)x+0) << 24) \ + | (*((UINT8*)x+1) << 16) \ + | (*((UINT8*)x+2) << 8 ) \ + | (*((UINT8*)x+3))) + +#define MAX_PKT_HDR_SIZE 1536 +#define MAX_STREAM_NAME_SIZE 64 + +typedef struct _Stream +{ + unsigned int id; + char name[MAX_STREAM_NAME_SIZE]; + unsigned char pktHdr[MAX_PKT_HDR_SIZE]; + unsigned short hdrLen; + unsigned short pktLen; + unsigned int dataPattern; + unsigned int flags; +#define STREAM_FLAG_MASK_STATUS 0x00000001 +#define STREAM_FLAG_VALUE_STATUS_DISABLED 0x00000000 +#define STREAM_FLAG_VALUE_STATUS_ENABLED 0x00000001 + + struct _Stream *next; +} Stream; typedef struct { unsigned int portId; pcap_if_t *dev; + Stream *streamHead; + Stream *streamTail; } PortInfo; class RxTx @@ -28,8 +58,19 @@ class RxTx PortInfo *portInfo; pcap_if_t *alldevs; + void InsertStreamAtHead(unsigned int port, Stream *s); + void InsertStreamAtTail(unsigned int port, Stream *s); + bool InsertStreamBefore(unsigned int port, Stream *s, + unsigned int nextStreamId); + bool DeleteStream(unsigned int port, Stream *s); + bool DeleteStream(unsigned int port, unsigned int streamId); + void DeleteAllStreams(unsigned int port); + Stream* GetStream(unsigned int port, unsigned int streamId); + //void Log(char *fmt, ...); void SendCapabilityInfo(void); + void SendPortInfo(unsigned int port); + void ProcessPortConfig(const char* msg, int len); }; #endif